Bytestream.java
- /**
- *
- * Copyright the original author or authors
- *
- * 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.bytestreams.socks5.packet;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import org.jivesoftware.smack.packet.IQ;
- import org.jivesoftware.smack.packet.NamedElement;
- import org.jivesoftware.smack.util.Objects;
- import org.jivesoftware.smack.util.StringUtils;
- import org.jivesoftware.smack.util.XmlStringBuilder;
- import org.jxmpp.jid.Jid;
- /**
- * A stanza representing part of a SOCKS5 Bytestream negotiation.
- *
- * @author Alexander Wenckus
- */
- public class Bytestream extends IQ {
- public static final String ELEMENT = QUERY_ELEMENT;
- /**
- * The XMPP namespace of the SOCKS5 Bytestream.
- */
- public static final String NAMESPACE = "http://jabber.org/protocol/bytestreams";
- private String sessionID;
- private Mode mode = Mode.tcp;
- private final List<StreamHost> streamHosts = new ArrayList<>();
- private StreamHostUsed usedHost;
- private Activate toActivate;
- /**
- * The default constructor.
- */
- public Bytestream() {
- super(ELEMENT, NAMESPACE);
- }
- /**
- * 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) {
- this();
- setSessionID(SID);
- }
- /**
- * Set the session ID related to the bytestream. The session ID is a unique identifier used to
- * differentiate between stream negotiations.
- *
- * @param sessionID the unique session ID that identifies the transfer.
- */
- public void setSessionID(final String sessionID) {
- this.sessionID = sessionID;
- }
- /**
- * Returns the session ID related to the bytestream negotiation.
- *
- * @return Returns the session ID related to the bytestream 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 the transport mode, either UDP or TCP
- * @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 JID of the stream host.
- * @param address The internet address of the stream host.
- * @return The added stream host.
- */
- public StreamHost addStreamHost(final Jid 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 JID 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 Jid JID, final String address, final int port) {
- StreamHost host = new StreamHost(JID, address, 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 List<StreamHost> getStreamHosts() {
- return Collections.unmodifiableList(streamHosts);
- }
- /**
- * Returns the stream host related to the given JID, or null if there is none.
- *
- * @param JID The JID of the desired stream host.
- * @return Returns the stream host related to the given JID, or null if there is none.
- */
- public StreamHost getStreamHost(final Jid JID) {
- if (JID == null) {
- return null;
- }
- for (StreamHost host : streamHosts) {
- 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 replies to the initiator with the
- * JID of the SOCKS5 host that they used.
- *
- * @param JID The JID of the used host.
- */
- public void setUsedHost(final Jid 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 stanza 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 stanza 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 stanza 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 JID of the target of the file transfer.
- */
- public void setToActivate(final Jid targetID) {
- this.toActivate = new Activate(targetID);
- }
- @Override
- protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
- switch (getType()) {
- case set:
- xml.optAttribute("sid", getSessionID());
- xml.optAttribute("mode", getMode());
- xml.rightAngleBracket();
- if (getToActivate() == null) {
- for (StreamHost streamHost : getStreamHosts()) {
- xml.append(streamHost.toXML(null));
- }
- }
- else {
- xml.append(getToActivate().toXML(null));
- }
- break;
- case result:
- xml.rightAngleBracket();
- xml.optAppend(getUsedHost());
- // TODO Bytestream can include either used host *or* streamHosts. Never both. This should be ensured by the
- // constructions mechanisms of Bytestream
- // A result from the server can also contain stream hosts
- for (StreamHost host : streamHosts) {
- xml.append(host.toXML(null));
- }
- break;
- case get:
- xml.setEmptyElement();
- break;
- default:
- throw new IllegalStateException();
- }
- return xml;
- }
- /**
- * Stanza extension that represents a potential SOCKS5 proxy for the file transfer. Stream hosts
- * are forwarded to the target of the file transfer who then chooses and connects to one.
- *
- * @author Alexander Wenckus
- */
- public static class StreamHost implements NamedElement {
- public static String ELEMENTNAME = "streamhost";
- private final Jid JID;
- private final String addy;
- private final int port;
- public StreamHost(Jid jid, String address) {
- this(jid, address, 0);
- }
- /**
- * Default constructor.
- *
- * @param JID The JID of the stream host.
- * @param address The internet address of the stream host.
- * @param port port of the stream host.
- */
- public StreamHost(final Jid JID, final String address, int port) {
- this.JID = Objects.requireNonNull(JID, "StreamHost JID must not be null");
- this.addy = StringUtils.requireNotNullOrEmpty(address, "StreamHost address must not be null");
- this.port = port;
- }
- /**
- * Returns the JID of the stream host.
- *
- * @return Returns the JID of the stream host.
- */
- public Jid 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;
- }
- /**
- * 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;
- }
- @Override
- public String getElementName() {
- return ELEMENTNAME;
- }
- @Override
- public XmlStringBuilder toXML(String enclosingNamespace) {
- XmlStringBuilder xml = new XmlStringBuilder(this);
- xml.attribute("jid", getJID());
- xml.attribute("host", getAddress());
- if (getPort() != 0) {
- xml.attribute("port", Integer.toString(getPort()));
- } else {
- xml.attribute("zeroconf", "_jabber.bytestreams");
- }
- xml.closeEmptyElement();
- return xml;
- }
- }
- /**
- * After selected a SOCKS5 stream host and successfully connecting, the target of the file
- * transfer returns a byte stream stanza with the stream host used extension.
- *
- * @author Alexander Wenckus
- */
- public static class StreamHostUsed implements NamedElement {
- public static String ELEMENTNAME = "streamhost-used";
- private final Jid JID;
- /**
- * Default constructor.
- *
- * @param JID The JID of the selected stream host.
- */
- public StreamHostUsed(final Jid JID) {
- this.JID = JID;
- }
- /**
- * Returns the JID of the selected stream host.
- *
- * @return Returns the JID of the selected stream host.
- */
- public Jid getJID() {
- return JID;
- }
- @Override
- public String getElementName() {
- return ELEMENTNAME;
- }
- @Override
- public XmlStringBuilder toXML(String enclosingNamespace) {
- XmlStringBuilder xml = new XmlStringBuilder(this);
- xml.attribute("jid", getJID());
- xml.closeEmptyElement();
- return xml;
- }
- }
- /**
- * The stanza sent by the stream initiator to the stream proxy to activate the connection.
- *
- * @author Alexander Wenckus
- */
- public static class Activate implements NamedElement {
- public static String ELEMENTNAME = "activate";
- private final Jid target;
- /**
- * Default constructor specifying the target of the stream.
- *
- * @param target The target of the stream.
- */
- public Activate(final Jid target) {
- this.target = target;
- }
- /**
- * Returns the target of the activation.
- *
- * @return Returns the target of the activation.
- */
- public Jid getTarget() {
- return target;
- }
- @Override
- public String getElementName() {
- return ELEMENTNAME;
- }
- @Override
- public XmlStringBuilder toXML(String enclosingNamespace) {
- XmlStringBuilder xml = new XmlStringBuilder(this);
- xml.rightAngleBracket();
- xml.escape(getTarget());
- xml.closeElement(this);
- return xml;
- }
- }
- /**
- * The stream can be either a TCP stream or a UDP stream.
- *
- * @author Alexander Wenckus
- */
- public enum Mode {
- /**
- * A TCP based stream.
- */
- tcp,
- /**
- * A UDP based stream.
- */
- udp;
- public static Mode fromName(String name) {
- Mode mode;
- try {
- mode = Mode.valueOf(name);
- }
- catch (Exception ex) {
- mode = tcp;
- }
- return mode;
- }
- }
- }