diff --git a/source/org/jivesoftware/smackx/packet/Bytestream.java b/source/org/jivesoftware/smackx/packet/Bytestream.java new file mode 100644 index 000000000..e073bc9d8 --- /dev/null +++ b/source/org/jivesoftware/smackx/packet/Bytestream.java @@ -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(""); + 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(""); + + 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(""); + 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); + } + } +}