mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-09-10 18:59:41 +02:00
File Transfer. (SMACK-72) (SMACK-122)
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@3395 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
e3c264c689
commit
8d0db1a339
23 changed files with 5781 additions and 1 deletions
465
source/org/jivesoftware/smackx/packet/ByteStream.java
Normal file
465
source/org/jivesoftware/smackx/packet/ByteStream.java
Normal file
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
* Created on Jun 21, 2005
|
||||
*/
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A packet representing part of a Socks5 Bytestream negotiation.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*/
|
||||
public class Bytestream extends IQ {
|
||||
|
||||
private String sessionID;
|
||||
|
||||
private Mode mode = Mode.TCP;
|
||||
|
||||
private final List streamHosts = new ArrayList();
|
||||
|
||||
private StreamHostUsed usedHost;
|
||||
|
||||
private Activate toActivate;
|
||||
|
||||
/**
|
||||
* The default constructor
|
||||
*/
|
||||
public Bytestream() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor where the session ID can be specified.
|
||||
*
|
||||
* @param SID The session ID related to the negotiation.
|
||||
* @see #setSessionID(String)
|
||||
*/
|
||||
public Bytestream(final String SID) {
|
||||
super();
|
||||
setSessionID(SID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the session ID related to the Byte Stream. The session ID is a unique
|
||||
* identifier used to differentiate between stream negotations.
|
||||
*
|
||||
* @param sessionID
|
||||
*/
|
||||
public void setSessionID(final String sessionID) {
|
||||
this.sessionID = sessionID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session ID related to the Byte Stream negotiation.
|
||||
*
|
||||
* @return Returns the session ID related to the Byte Stream negotiation.
|
||||
* @see #setSessionID(String)
|
||||
*/
|
||||
public String getSessionID() {
|
||||
return sessionID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transport mode. This should be put in the initiation of the
|
||||
* interaction.
|
||||
*
|
||||
* @param mode
|
||||
* @see Mode
|
||||
*/
|
||||
public void setMode(final Mode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transport mode.
|
||||
*
|
||||
* @return Returns the transport mode.
|
||||
* @see #setMode(Mode)
|
||||
*/
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a potential stream host that the remote user can connect to to
|
||||
* receive the file.
|
||||
*
|
||||
* @param JID The jabber ID of the stream host.
|
||||
* @param address The internet address of the stream host.
|
||||
* @return The added stream host.
|
||||
*/
|
||||
public StreamHost addStreamHost(final String JID, final String address) {
|
||||
return addStreamHost(JID, address, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a potential stream host that the remote user can connect to to
|
||||
* receive the file.
|
||||
*
|
||||
* @param JID The jabber ID of the stream host.
|
||||
* @param address The internet address of the stream host.
|
||||
* @param port The port on which the remote host is seeking connections.
|
||||
* @return The added stream host.
|
||||
*/
|
||||
public StreamHost addStreamHost(final String JID, final String address,
|
||||
final int port) {
|
||||
StreamHost host = new StreamHost(JID, address);
|
||||
host.setPort(port);
|
||||
addStreamHost(host);
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a potential stream host that the remote user can transfer the file
|
||||
* through.
|
||||
*
|
||||
* @param host The potential stream host.
|
||||
*/
|
||||
public void addStreamHost(final StreamHost host) {
|
||||
streamHosts.add(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of stream hosts contained in the packet.
|
||||
*
|
||||
* @return Returns the list of stream hosts contained in the packet.
|
||||
*/
|
||||
public Collection getStreamHosts() {
|
||||
return Collections.unmodifiableCollection(streamHosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stream host related to the given jabber ID, or null if there
|
||||
* is none.
|
||||
*
|
||||
* @param JID The jabber ID of the desired stream host.
|
||||
* @return Returns the stream host related to the given jabber ID, or null
|
||||
* if there is none.
|
||||
*/
|
||||
public StreamHost getStreamHost(final String JID) {
|
||||
StreamHost host;
|
||||
for (Iterator it = streamHosts.iterator(); it.hasNext();) {
|
||||
host = (StreamHost) it.next();
|
||||
if (host.getJID().equals(JID)) {
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of stream hosts contained in this packet.
|
||||
*
|
||||
* @return Returns the count of stream hosts contained in this packet.
|
||||
*/
|
||||
public int countStreamHosts() {
|
||||
return streamHosts.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon connecting to the stream host the target of the stream replys to the
|
||||
* initiator with the jabber id of the Socks5 host that they used.
|
||||
*
|
||||
* @param JID The jabber ID of the used host.
|
||||
*/
|
||||
public void setUsedHost(final String JID) {
|
||||
this.usedHost = new StreamHostUsed(JID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Socks5 host connected to by the remote user.
|
||||
*
|
||||
* @return Returns the Socks5 host connected to by the remote user.
|
||||
*/
|
||||
public StreamHostUsed getUsedHost() {
|
||||
return usedHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the activate element of the packet sent to the proxy host to
|
||||
* verify the identity of the initiator and match them to the appropriate
|
||||
* stream.
|
||||
*
|
||||
* @return Returns the activate element of the packet sent to the proxy host
|
||||
* to verify the identity of the initiator and match them to the
|
||||
* appropriate stream.
|
||||
*/
|
||||
public Activate getToActivate() {
|
||||
return toActivate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upon the response from the target of the used host the activate packet is
|
||||
* sent to the Socks5 proxy. The proxy will activate the stream or return an
|
||||
* error after verifying the identity of the initiator, using the activate
|
||||
* packet.
|
||||
*
|
||||
* @param targetID The jabber ID of the target of the file transfer.
|
||||
*/
|
||||
public void setToActivate(final String targetID) {
|
||||
this.toActivate = new Activate(targetID);
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
buf.append("<query xmlns=\"http://jabber.org/protocol/bytestreams\"");
|
||||
if (this.getType().equals(IQ.Type.SET)) {
|
||||
if (getSessionID() != null)
|
||||
buf.append(" sid=\"").append(getSessionID()).append("\"");
|
||||
if (getMode() != null)
|
||||
buf.append(" mode = \"").append(getMode()).append("\"");
|
||||
buf.append(">");
|
||||
if (getToActivate() == null) {
|
||||
for (Iterator it = getStreamHosts().iterator(); it.hasNext();)
|
||||
buf.append(((StreamHost) it.next()).toXML());
|
||||
}
|
||||
else {
|
||||
buf.append(getToActivate().toXML());
|
||||
}
|
||||
}
|
||||
else if (this.getType().equals(IQ.Type.RESULT)) {
|
||||
buf.append(">");
|
||||
if (getUsedHost() != null)
|
||||
buf.append(getUsedHost().toXML());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
buf.append("</query>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet extension that represents a potential Socks5 proxy for the file
|
||||
* transfer. Stream hosts are forwared to the target of the file transfer
|
||||
* who then chooses and connects to one.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*/
|
||||
public static class StreamHost implements PacketExtension {
|
||||
|
||||
public static String NAMESPACE = "";
|
||||
|
||||
public static String ELEMENTNAME = "streamhost";
|
||||
|
||||
private final String JID;
|
||||
|
||||
private final String addy;
|
||||
|
||||
private int port = 0;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*
|
||||
* @param JID The jabber ID of the stream host.
|
||||
* @param address The internet address of the stream host.
|
||||
*/
|
||||
public StreamHost(final String JID, final String address) {
|
||||
this.JID = JID;
|
||||
this.addy = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the jabber ID of the stream host.
|
||||
*
|
||||
* @return Returns the jabber ID of the stream host.
|
||||
*/
|
||||
public String getJID() {
|
||||
return JID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internet address of the stream host.
|
||||
*
|
||||
* @return Returns the internet address of the stream host.
|
||||
*/
|
||||
public String getAddress() {
|
||||
return addy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port of the stream host.
|
||||
*
|
||||
* @param port The port on which the potential stream host would accept
|
||||
* the connection.
|
||||
*/
|
||||
public void setPort(final int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port on which the potential stream host would accept the
|
||||
* connection.
|
||||
*
|
||||
* @return Returns the port on which the potential stream host would
|
||||
* accept the connection.
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return ELEMENTNAME;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
buf.append("<").append(getElementName()).append(" ");
|
||||
buf.append("jid=\"").append(getJID()).append("\" ");
|
||||
buf.append("host=\"").append(getAddress()).append("\" ");
|
||||
if (getPort() != 0)
|
||||
buf.append("port=\"").append(getPort()).append("\"");
|
||||
else
|
||||
buf.append("zeroconf=\"_jabber.bytestreams\"");
|
||||
buf.append("/>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After selected a Socks5 stream host and successfully connecting, the
|
||||
* target of the file transfer returns a byte stream packet with the stream
|
||||
* host used extension.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*/
|
||||
public static class StreamHostUsed implements PacketExtension {
|
||||
|
||||
public String NAMESPACE = "";
|
||||
|
||||
public static String ELEMENTNAME = "streamhost-used";
|
||||
|
||||
private final String JID;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*
|
||||
* @param JID The jabber ID of the selected stream host.
|
||||
*/
|
||||
public StreamHostUsed(final String JID) {
|
||||
this.JID = JID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the jabber ID of the selected stream host.
|
||||
*
|
||||
* @return Returns the jabber ID of the selected stream host.
|
||||
*/
|
||||
public String getJID() {
|
||||
return JID;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return ELEMENTNAME;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(getElementName()).append(" ");
|
||||
buf.append("jid=\"").append(getJID()).append("\" ");
|
||||
buf.append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The packet sent by the stream initiator to the stream proxy to activate
|
||||
* the connection.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*/
|
||||
public static class Activate implements PacketExtension {
|
||||
|
||||
public String NAMESPACE = "";
|
||||
|
||||
public static String ELEMENTNAME = "activate";
|
||||
|
||||
private final String target;
|
||||
|
||||
/**
|
||||
* Default constructor specifying the target of the stream.
|
||||
*
|
||||
* @param target The target of the stream.
|
||||
*/
|
||||
public Activate(final String target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target of the activation.
|
||||
*
|
||||
* @return Returns the target of the activation.
|
||||
*/
|
||||
public String getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return ELEMENTNAME;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(getElementName()).append(">");
|
||||
buf.append(getTarget());
|
||||
buf.append("</").append(getElementName()).append(">");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The stream can be either a TCP stream or a UDP stream.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*/
|
||||
public static class Mode {
|
||||
|
||||
/**
|
||||
* A TCP based stream.
|
||||
*/
|
||||
public static Mode TCP = new Mode("tcp");
|
||||
|
||||
/**
|
||||
* A UDP based stream.
|
||||
*/
|
||||
public static Mode UDP = new Mode("udp");
|
||||
|
||||
private final String modeString;
|
||||
|
||||
private Mode(final String mode) {
|
||||
this.modeString = mode;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return modeString;
|
||||
}
|
||||
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof Mode))
|
||||
return false;
|
||||
return modeString.equals(((Mode) obj).modeString);
|
||||
}
|
||||
}
|
||||
}
|
225
source/org/jivesoftware/smackx/packet/IBBExtensions.java
Normal file
225
source/org/jivesoftware/smackx/packet/IBBExtensions.java
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Created on Jul 5, 2005
|
||||
*/
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
|
||||
/**
|
||||
* The different extensions used throughtout the negotiation and transfer
|
||||
* process.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*
|
||||
*/
|
||||
public class IBBExtensions {
|
||||
|
||||
public static final String NAMESPACE = "http://jabber.org/protocol/ibb";
|
||||
|
||||
private abstract static class IBB extends IQ {
|
||||
final String sid;
|
||||
|
||||
private IBB(final String sid) {
|
||||
this.sid = sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique stream ID for this file transfer.
|
||||
*
|
||||
* @return Returns the unique stream ID for this file transfer.
|
||||
*/
|
||||
public String getSessionID() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a request to open the file transfer.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*
|
||||
*/
|
||||
public static class Open extends IBB {
|
||||
|
||||
public static final String ELEMENT_NAME = "open";
|
||||
|
||||
private final int blockSize;
|
||||
|
||||
/**
|
||||
* Constructs an open packet.
|
||||
*
|
||||
* @param sid
|
||||
* The streamID of the file transfer.
|
||||
* @param blockSize
|
||||
* The block size of the file transfer.
|
||||
*/
|
||||
public Open(final String sid, final int blockSize) {
|
||||
super(sid);
|
||||
this.blockSize = blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* The size blocks in which the data will be sent.
|
||||
*
|
||||
* @return The size blocks in which the data will be sent.
|
||||
*/
|
||||
public int getBlockSize() {
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return ELEMENT_NAME;
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\" ");
|
||||
buf.append("sid=\"").append(getSessionID()).append("\" ");
|
||||
buf.append("block-size=\"").append(getBlockSize()).append("\"");
|
||||
buf.append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data packet containing a portion of the file being sent encoded in
|
||||
* base64.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*
|
||||
*/
|
||||
public static class Data implements PacketExtension {
|
||||
|
||||
private long seq;
|
||||
|
||||
private String data;
|
||||
|
||||
public static final String ELEMENT_NAME = "data";
|
||||
|
||||
final String sid;
|
||||
|
||||
/**
|
||||
* Returns the unique stream ID identifying this file transfer.
|
||||
*
|
||||
* @return Returns the unique stream ID identifying this file transfer.
|
||||
*/
|
||||
public String getSessionID() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor.
|
||||
*
|
||||
* @param sid
|
||||
* The stream ID.
|
||||
*/
|
||||
public Data(final String sid) {
|
||||
this.sid = sid;
|
||||
}
|
||||
|
||||
public Data(final String sid, final long seq, final String data) {
|
||||
this(sid);
|
||||
this.seq = seq;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return ELEMENT_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data contained in this packet.
|
||||
*
|
||||
* @return Returns the data contained in this packet.
|
||||
*/
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data contained in this packet.
|
||||
*
|
||||
* @param data
|
||||
* The data encoded in base65
|
||||
*/
|
||||
public void setData(final String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sequence of this packet in regard to the other data
|
||||
* packets.
|
||||
*
|
||||
* @return Returns the sequence of this packet in regard to the other
|
||||
* data packets.
|
||||
*/
|
||||
public long getSeq() {
|
||||
return seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sequence of this packet.
|
||||
*
|
||||
* @param seq
|
||||
* A number between 0 and 65535
|
||||
*/
|
||||
public void setSeq(final long seq) {
|
||||
this.seq = seq;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace())
|
||||
.append("\" ");
|
||||
buf.append("sid=\"").append(getSessionID()).append("\" ");
|
||||
buf.append("seq=\"").append(getSeq()).append("\"");
|
||||
buf.append(">");
|
||||
buf.append(getData());
|
||||
buf.append("</").append(getElementName()).append(">");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the closing of the file transfer.
|
||||
*
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
*
|
||||
*/
|
||||
public static class Close extends IBB {
|
||||
public static final String ELEMENT_NAME = "close";
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @param sid
|
||||
* The unique stream ID identifying this file transfer.
|
||||
*/
|
||||
public Close(String sid) {
|
||||
super(sid);
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return ELEMENT_NAME;
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\" ");
|
||||
buf.append("sid=\"").append(getSessionID()).append("\"");
|
||||
buf.append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
403
source/org/jivesoftware/smackx/packet/StreamInitiation.java
Normal file
403
source/org/jivesoftware/smackx/packet/StreamInitiation.java
Normal file
|
@ -0,0 +1,403 @@
|
|||
/*
|
||||
* Created on Jun 16, 2005
|
||||
*/
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
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() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
buffer.append("<").append(getElementName()).append(" xmlns=\"")
|
||||
.append(getNamespace()).append("\" ");
|
||||
|
||||
if (getName() != null) {
|
||||
buffer.append("name=\"").append(getName()).append("\" ");
|
||||
}
|
||||
|
||||
if (getSize() > 0) {
|
||||
buffer.append("size=\"").append(getSize()).append("\" ");
|
||||
}
|
||||
|
||||
if (getDate() != null) {
|
||||
buffer.append("date=\"").append(DelayInformation.UTC_FORMAT.format(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() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf
|
||||
.append("<feature xmlns=\"http://jabber.org/protocol/feature-neg\">");
|
||||
buf.append(data.toXML());
|
||||
buf.append("</feature>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue