Socks5TransferNegotiator.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.filetransfer;

  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.OutputStream;
  21. import java.io.PushbackInputStream;

  22. import org.jivesoftware.smack.SmackException;
  23. import org.jivesoftware.smack.SmackException.NoResponseException;
  24. import org.jivesoftware.smack.XMPPConnection;
  25. import org.jivesoftware.smack.XMPPException;
  26. import org.jivesoftware.smack.XMPPException.XMPPErrorException;
  27. import org.jivesoftware.smack.packet.Stanza;
  28. import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
  29. import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;
  30. import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
  31. import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
  32. import org.jivesoftware.smackx.si.packet.StreamInitiation;
  33. import org.jxmpp.jid.Jid;

  34. /**
  35.  * Negotiates a SOCKS5 Bytestream to be used for file transfers. The implementation is based on the
  36.  * {@link Socks5BytestreamManager} and the {@link Socks5BytestreamRequest}.
  37.  *
  38.  * @author Henning Staib
  39.  * @see <a href="http://xmpp.org/extensions/xep-0065.html">XEP-0065: SOCKS5 Bytestreams</a>
  40.  */
  41. public class Socks5TransferNegotiator extends StreamNegotiator {

  42.     private XMPPConnection connection;

  43.     private Socks5BytestreamManager manager;

  44.     Socks5TransferNegotiator(XMPPConnection connection) {
  45.         this.connection = connection;
  46.         this.manager = Socks5BytestreamManager.getBytestreamManager(this.connection);
  47.     }

  48.     @Override
  49.     public OutputStream createOutgoingStream(String streamID, Jid initiator, Jid target) throws NoResponseException, SmackException, XMPPException
  50.                     {
  51.         try {
  52.             return this.manager.establishSession(target, streamID).getOutputStream();
  53.         }
  54.         catch (IOException e) {
  55.             throw new SmackException("error establishing SOCKS5 Bytestream", e);
  56.         }
  57.         catch (InterruptedException e) {
  58.             throw new SmackException("error establishing SOCKS5 Bytestream", e);
  59.         }
  60.     }

  61.     @Override
  62.     public InputStream createIncomingStream(StreamInitiation initiation) throws XMPPErrorException,
  63.                     InterruptedException, SmackException {
  64.         /*
  65.          * SOCKS5 initiation listener must ignore next SOCKS5 Bytestream request with given session
  66.          * ID
  67.          */
  68.         this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID());

  69.         Stanza streamInitiation = initiateIncomingStream(this.connection, initiation);
  70.         return negotiateIncomingStream(streamInitiation);
  71.     }

  72.     @Override
  73.     public void newStreamInitiation(final Jid from, String streamID) {
  74.         /*
  75.          * this method is always called prior to #negotiateIncomingStream() so the SOCKS5
  76.          * InitiationListener must ignore the next SOCKS5 Bytestream request with the given session
  77.          * ID
  78.          */
  79.         this.manager.ignoreBytestreamRequestOnce(streamID);
  80.     }

  81.     @Override
  82.     public String[] getNamespaces() {
  83.         return new String[] { Bytestream.NAMESPACE };
  84.     }

  85.     @Override
  86.     InputStream negotiateIncomingStream(Stanza streamInitiation) throws InterruptedException,
  87.                     SmackException, XMPPErrorException {
  88.         // build SOCKS5 Bytestream request
  89.         Socks5BytestreamRequest request = new ByteStreamRequest(this.manager,
  90.                         (Bytestream) streamInitiation);

  91.         // always accept the request
  92.         Socks5BytestreamSession session = request.accept();

  93.         // test input stream
  94.         try {
  95.             PushbackInputStream stream = new PushbackInputStream(session.getInputStream());
  96.             int firstByte = stream.read();
  97.             stream.unread(firstByte);
  98.             return stream;
  99.         }
  100.         catch (IOException e) {
  101.             throw new SmackException("Error establishing input stream", e);
  102.         }
  103.     }

  104.     /**
  105.      * Derive from Socks5BytestreamRequest to access protected constructor.
  106.      */
  107.     private static class ByteStreamRequest extends Socks5BytestreamRequest {

  108.         private ByteStreamRequest(Socks5BytestreamManager manager, Bytestream byteStreamRequest) {
  109.             super(manager, byteStreamRequest);
  110.         }

  111.     }

  112. }