Bytestream.java

  1. /**
  2.  *
  3.  * Copyright the original author or authors
  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.bytestreams.socks5.packet;

  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;

  21. import org.jivesoftware.smack.packet.IQ;
  22. import org.jivesoftware.smack.packet.NamedElement;
  23. import org.jivesoftware.smack.util.XmlStringBuilder;
  24. import org.jxmpp.jid.Jid;

  25. /**
  26.  * A packet representing part of a SOCKS5 Bytestream negotiation.
  27.  *
  28.  * @author Alexander Wenckus
  29.  */
  30. public class Bytestream extends IQ {

  31.     public static final String ELEMENT = QUERY_ELEMENT;

  32.     /**
  33.      * The XMPP namespace of the SOCKS5 Bytestream
  34.      */
  35.     public static final String NAMESPACE = "http://jabber.org/protocol/bytestreams";

  36.     private String sessionID;

  37.     private Mode mode = Mode.tcp;

  38.     private final List<StreamHost> streamHosts = new ArrayList<StreamHost>();

  39.     private StreamHostUsed usedHost;

  40.     private Activate toActivate;

  41.     /**
  42.      * The default constructor
  43.      */
  44.     public Bytestream() {
  45.         super(ELEMENT, NAMESPACE);
  46.     }

  47.     /**
  48.      * A constructor where the session ID can be specified.
  49.      *
  50.      * @param SID The session ID related to the negotiation.
  51.      * @see #setSessionID(String)
  52.      */
  53.     public Bytestream(final String SID) {
  54.         this();
  55.         setSessionID(SID);
  56.     }

  57.     /**
  58.      * Set the session ID related to the bytestream. The session ID is a unique identifier used to
  59.      * differentiate between stream negotiations.
  60.      *
  61.      * @param sessionID the unique session ID that identifies the transfer.
  62.      */
  63.     public void setSessionID(final String sessionID) {
  64.         this.sessionID = sessionID;
  65.     }

  66.     /**
  67.      * Returns the session ID related to the bytestream negotiation.
  68.      *
  69.      * @return Returns the session ID related to the bytestream negotiation.
  70.      * @see #setSessionID(String)
  71.      */
  72.     public String getSessionID() {
  73.         return sessionID;
  74.     }

  75.     /**
  76.      * Set the transport mode. This should be put in the initiation of the interaction.
  77.      *
  78.      * @param mode the transport mode, either UDP or TCP
  79.      * @see Mode
  80.      */
  81.     public void setMode(final Mode mode) {
  82.         this.mode = mode;
  83.     }

  84.     /**
  85.      * Returns the transport mode.
  86.      *
  87.      * @return Returns the transport mode.
  88.      * @see #setMode(Mode)
  89.      */
  90.     public Mode getMode() {
  91.         return mode;
  92.     }

  93.     /**
  94.      * Adds a potential stream host that the remote user can connect to to receive the file.
  95.      *
  96.      * @param JID The JID of the stream host.
  97.      * @param address The internet address of the stream host.
  98.      * @return The added stream host.
  99.      */
  100.     public StreamHost addStreamHost(final Jid JID, final String address) {
  101.         return addStreamHost(JID, address, 0);
  102.     }

  103.     /**
  104.      * Adds a potential stream host that the remote user can connect to to receive the file.
  105.      *
  106.      * @param JID The JID of the stream host.
  107.      * @param address The internet address of the stream host.
  108.      * @param port The port on which the remote host is seeking connections.
  109.      * @return The added stream host.
  110.      */
  111.     public StreamHost addStreamHost(final Jid JID, final String address, final int port) {
  112.         StreamHost host = new StreamHost(JID, address, port);
  113.         addStreamHost(host);

  114.         return host;
  115.     }

  116.     /**
  117.      * Adds a potential stream host that the remote user can transfer the file through.
  118.      *
  119.      * @param host The potential stream host.
  120.      */
  121.     public void addStreamHost(final StreamHost host) {
  122.         streamHosts.add(host);
  123.     }

  124.     /**
  125.      * Returns the list of stream hosts contained in the packet.
  126.      *
  127.      * @return Returns the list of stream hosts contained in the packet.
  128.      */
  129.     public List<StreamHost> getStreamHosts() {
  130.         return Collections.unmodifiableList(streamHosts);
  131.     }

  132.     /**
  133.      * Returns the stream host related to the given JID, or null if there is none.
  134.      *
  135.      * @param JID The JID of the desired stream host.
  136.      * @return Returns the stream host related to the given JID, or null if there is none.
  137.      */
  138.     public StreamHost getStreamHost(final Jid JID) {
  139.         if (JID == null) {
  140.             return null;
  141.         }
  142.         for (StreamHost host : streamHosts) {
  143.             if (host.getJID().equals(JID)) {
  144.                 return host;
  145.             }
  146.         }

  147.         return null;
  148.     }

  149.     /**
  150.      * Returns the count of stream hosts contained in this packet.
  151.      *
  152.      * @return Returns the count of stream hosts contained in this packet.
  153.      */
  154.     public int countStreamHosts() {
  155.         return streamHosts.size();
  156.     }

  157.     /**
  158.      * Upon connecting to the stream host the target of the stream replies to the initiator with the
  159.      * JID of the SOCKS5 host that they used.
  160.      *
  161.      * @param JID The JID of the used host.
  162.      */
  163.     public void setUsedHost(final Jid JID) {
  164.         this.usedHost = new StreamHostUsed(JID);
  165.     }

  166.     /**
  167.      * Returns the SOCKS5 host connected to by the remote user.
  168.      *
  169.      * @return Returns the SOCKS5 host connected to by the remote user.
  170.      */
  171.     public StreamHostUsed getUsedHost() {
  172.         return usedHost;
  173.     }

  174.     /**
  175.      * Returns the activate element of the packet sent to the proxy host to verify the identity of
  176.      * the initiator and match them to the appropriate stream.
  177.      *
  178.      * @return Returns the activate element of the packet sent to the proxy host to verify the
  179.      *         identity of the initiator and match them to the appropriate stream.
  180.      */
  181.     public Activate getToActivate() {
  182.         return toActivate;
  183.     }

  184.     /**
  185.      * Upon the response from the target of the used host the activate packet is sent to the SOCKS5
  186.      * proxy. The proxy will activate the stream or return an error after verifying the identity of
  187.      * the initiator, using the activate packet.
  188.      *
  189.      * @param targetID The JID of the target of the file transfer.
  190.      */
  191.     public void setToActivate(final Jid targetID) {
  192.         this.toActivate = new Activate(targetID);
  193.     }

  194.     @Override
  195.     protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
  196.         switch(getType()) {
  197.         case set:
  198.             xml.optAttribute("sid", getSessionID());
  199.             xml.optAttribute("mode", getMode());
  200.             xml.rightAngleBracket();
  201.             if (getToActivate() == null) {
  202.                 for (StreamHost streamHost : getStreamHosts()) {
  203.                     xml.append(streamHost.toXML());
  204.                 }
  205.             }
  206.             else {
  207.                 xml.append(getToActivate().toXML());
  208.             }
  209.             break;
  210.         case result:
  211.             xml.rightAngleBracket();
  212.             xml.optAppend(getUsedHost());
  213.             // TODO Bytestream can include either used host *or* streamHosts. Never both. This should be ensured by the
  214.             // constructions mechanisms of Bytestream
  215.             // A result from the server can also contain stream hosts
  216.             for (StreamHost host : streamHosts) {
  217.                 xml.append(host.toXML());
  218.             }
  219.             break;
  220.         case get:
  221.             xml.setEmptyElement();
  222.             break;
  223.         default:
  224.             throw new IllegalStateException();
  225.         }

  226.         return xml;
  227.     }

  228.     /**
  229.      * Packet extension that represents a potential SOCKS5 proxy for the file transfer. Stream hosts
  230.      * are forwarded to the target of the file transfer who then chooses and connects to one.
  231.      *
  232.      * @author Alexander Wenckus
  233.      */
  234.     public static class StreamHost implements NamedElement {

  235.         public static String ELEMENTNAME = "streamhost";

  236.         private final Jid JID;

  237.         private final String addy;

  238.         private final int port;

  239.         public StreamHost(Jid jid, String address) {
  240.             this(jid, address, 0);
  241.         }

  242.         /**
  243.          * Default constructor.
  244.          *
  245.          * @param JID The JID of the stream host.
  246.          * @param address The internet address of the stream host.
  247.          */
  248.         public StreamHost(final Jid JID, final String address, int port) {
  249.             this.JID = JID;
  250.             this.addy = address;
  251.             this.port = port;
  252.         }

  253.         /**
  254.          * Returns the JID of the stream host.
  255.          *
  256.          * @return Returns the JID of the stream host.
  257.          */
  258.         public Jid getJID() {
  259.             return JID;
  260.         }

  261.         /**
  262.          * Returns the internet address of the stream host.
  263.          *
  264.          * @return Returns the internet address of the stream host.
  265.          */
  266.         public String getAddress() {
  267.             return addy;
  268.         }

  269.         /**
  270.          * Returns the port on which the potential stream host would accept the connection.
  271.          *
  272.          * @return Returns the port on which the potential stream host would accept the connection.
  273.          */
  274.         public int getPort() {
  275.             return port;
  276.         }

  277.         public String getElementName() {
  278.             return ELEMENTNAME;
  279.         }

  280.         @Override
  281.         public XmlStringBuilder toXML() {
  282.             XmlStringBuilder xml = new XmlStringBuilder(this);
  283.             xml.attribute("jid", getJID());
  284.             xml.attribute("host", getAddress());
  285.             if (getPort() != 0) {
  286.                 xml.attribute("port", Integer.toString(getPort()));
  287.             } else {
  288.                 xml.attribute("zeroconf", "_jabber.bytestreams");
  289.             }
  290.             xml.closeEmptyElement();
  291.             return xml;
  292.         }
  293.     }

  294.     /**
  295.      * After selected a SOCKS5 stream host and successfully connecting, the target of the file
  296.      * transfer returns a byte stream packet with the stream host used extension.
  297.      *
  298.      * @author Alexander Wenckus
  299.      */
  300.     public static class StreamHostUsed implements NamedElement {

  301.         public static String ELEMENTNAME = "streamhost-used";

  302.         private final Jid JID;

  303.         /**
  304.          * Default constructor.
  305.          *
  306.          * @param JID The JID of the selected stream host.
  307.          */
  308.         public StreamHostUsed(final Jid JID) {
  309.             this.JID = JID;
  310.         }

  311.         /**
  312.          * Returns the JID of the selected stream host.
  313.          *
  314.          * @return Returns the JID of the selected stream host.
  315.          */
  316.         public Jid getJID() {
  317.             return JID;
  318.         }

  319.         public String getElementName() {
  320.             return ELEMENTNAME;
  321.         }

  322.         @Override
  323.         public XmlStringBuilder toXML() {
  324.             XmlStringBuilder xml = new XmlStringBuilder(this);
  325.             xml.attribute("jid", getJID());
  326.             xml.closeEmptyElement();
  327.             return xml;
  328.         }
  329.     }

  330.     /**
  331.      * The packet sent by the stream initiator to the stream proxy to activate the connection.
  332.      *
  333.      * @author Alexander Wenckus
  334.      */
  335.     public static class Activate implements NamedElement {

  336.         public static String ELEMENTNAME = "activate";

  337.         private final Jid target;

  338.         /**
  339.          * Default constructor specifying the target of the stream.
  340.          *
  341.          * @param target The target of the stream.
  342.          */
  343.         public Activate(final Jid target) {
  344.             this.target = target;
  345.         }

  346.         /**
  347.          * Returns the target of the activation.
  348.          *
  349.          * @return Returns the target of the activation.
  350.          */
  351.         public Jid getTarget() {
  352.             return target;
  353.         }

  354.         public String getElementName() {
  355.             return ELEMENTNAME;
  356.         }

  357.         @Override
  358.         public XmlStringBuilder toXML() {
  359.             XmlStringBuilder xml = new XmlStringBuilder(this);
  360.             xml.rightAngleBracket();
  361.             xml.escape(getTarget());
  362.             xml.closeElement(this);
  363.             return xml;
  364.         }
  365.     }

  366.     /**
  367.      * The stream can be either a TCP stream or a UDP stream.
  368.      *
  369.      * @author Alexander Wenckus
  370.      */
  371.     public enum Mode {

  372.         /**
  373.          * A TCP based stream.
  374.          */
  375.         tcp,

  376.         /**
  377.          * A UDP based stream.
  378.          */
  379.         udp;

  380.         public static Mode fromName(String name) {
  381.             Mode mode;
  382.             try {
  383.                 mode = Mode.valueOf(name);
  384.             }
  385.             catch (Exception ex) {
  386.                 mode = tcp;
  387.             }

  388.             return mode;
  389.         }
  390.     }
  391. }