summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarun Gupta <tarun1995gupta@gmail.com>2015-08-03 12:08:37 (GMT)
committerAlex Clayton <alex.clayton@isode.com>2016-01-21 10:47:51 (GMT)
commit97a085f7e2c9b7820000eaace97dc0ab6392cb0d (patch)
treed3df191a053a69bc52238b76b8e9e42af043302c /src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java
parentfa1633e3b4d75a8217459cdc5fe64e9ee5ace65a (diff)
downloadstroke-97a085f7e2c9b7820000eaace97dc0ab6392cb0d.zip
stroke-97a085f7e2c9b7820000eaace97dc0ab6392cb0d.tar.bz2
Completes FileTransfer according to Swiften.
S5BTransport Session still needs generic T. FileTransfer, OutgoingFileTransfer and IncomingFileTransfer are made an interface due to the need of multiple inheritance in IncomingJingleFileTransfer and OutgoingJingleFileTransfer. Corresponding documentation has been updated. License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details. Test-Information: None. Change-Id: If44cf387767865c37492d871c12d623f94ebaa3a
Diffstat (limited to 'src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java')
-rw-r--r--src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java
new file mode 100644
index 0000000..cb09ad9
--- /dev/null
+++ b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerSession.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.filetransfer;
+
+import com.isode.stroke.network.Connection;
+import com.isode.stroke.network.HostAddressPort;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.SafeByteArray;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Signal;
+import com.isode.stroke.signals.Slot;
+import com.isode.stroke.signals.Slot1;
+import java.util.logging.Logger;
+
+public class SOCKS5BytestreamServerSession {
+
+ private Connection connection;
+ private SOCKS5BytestreamRegistry bytestreams;
+ private ByteArray unprocessedData;
+ private State state;
+ private int chunkSize;
+ private String streamID = "";
+ private ReadBytestream readBytestream;
+ private WriteBytestream writeBytestream;
+ private boolean waitingForData;
+
+ private SignalConnection disconnectedConnection;
+ private SignalConnection dataReadConnection;
+ private SignalConnection dataWrittenConnection;
+ private SignalConnection dataAvailableConnection;
+ private Logger logger_ = Logger.getLogger(this.getClass().getName());
+
+ public enum State {
+ Initial,
+ WaitingForAuthentication,
+ WaitingForRequest,
+ ReadyForTransfer,
+ ReadingData,
+ WritingData,
+ Finished
+ };
+
+ public SOCKS5BytestreamServerSession(Connection connection, SOCKS5BytestreamRegistry bytestreams) {
+ this.connection = connection;
+ this.bytestreams = bytestreams;
+ this.state = State.Initial;
+ this.chunkSize = 131072;
+ this.waitingForData = false;
+ disconnectedConnection = connection.onDisconnected.connect(new Slot1<Connection.Error>() {
+ @Override
+ public void call(Connection.Error e) {
+ handleDisconnected(e);
+ }
+ });
+ }
+
+ public void setChunkSize(int chunkSize) {
+ this.chunkSize = chunkSize;
+ }
+
+ public void start() {
+ logger_.fine("\n");
+ dataReadConnection = connection.onDataRead.connect(new Slot1<SafeByteArray>() {
+ @Override
+ public void call(SafeByteArray s) {
+ handleDataRead(s);
+ }
+ });
+ state = State.WaitingForAuthentication;
+ }
+
+ public void stop() {
+ finish(false);
+ }
+
+ public void startSending(ReadBytestream stream) {
+ if (!State.ReadyForTransfer.equals(state)) { logger_.fine("Not ready for transfer!\n"); return; }
+ readBytestream = stream;
+ state = State.WritingData;
+ dataAvailableConnection = readBytestream.onDataAvailable.connect(new Slot() {
+ @Override
+ public void call() {
+ handleDataAvailable();
+ }
+ });
+ dataWrittenConnection = connection.onDataWritten.connect(new Slot() {
+ @Override
+ public void call() {
+ sendData();
+ }
+ });
+ sendData();
+ }
+
+ public void startReceiving(WriteBytestream stream) {
+ if (!State.ReadyForTransfer.equals(state)) { logger_.fine("Not ready for transfer!\n"); return; }
+
+ writeBytestream = stream;
+ state = State.ReadingData;
+ writeBytestream.write(unprocessedData);
+ // onBytesReceived(unprocessedData.getSize());
+ unprocessedData.clear();
+ }
+
+ public HostAddressPort getAddressPort() {
+ return connection.getLocalAddress();
+ }
+
+ public Signal1<FileTransferError> onFinished = new Signal1<FileTransferError>();
+ public Signal1<Long> onBytesSent = new Signal1<Long>();
+ // boost::signal<void (unsigned long long)> onBytesReceived;
+
+ public String getStreamID() {
+ return streamID;
+ }
+
+ private void finish(boolean error) {
+ logger_.fine(error + " " + state + "\n");
+ if (State.Finished.equals(state)) {
+ return;
+ }
+
+ disconnectedConnection.disconnect();
+ dataReadConnection.disconnect();
+ dataWrittenConnection.disconnect();
+ dataAvailableConnection.disconnect();
+ readBytestream = null;
+ state = State.Finished;
+ if (error) {
+ onFinished.emit(new FileTransferError(FileTransferError.Type.PeerError));
+ } else {
+ onFinished.emit(null);
+ }
+ }
+
+ private void process() {
+ if (State.WaitingForAuthentication.equals(state)) {
+ if (unprocessedData.getSize() >= 2) {
+ int authCount = unprocessedData.getData()[1];
+ int i = 2;
+ while (i < 2 + authCount && i < unprocessedData.getSize()) {
+ // Skip authentication mechanism
+ ++i;
+ }
+ if (i == 2 + authCount) {
+ // Authentication message is complete
+ if (i != unprocessedData.getSize()) {
+ logger_.fine("Junk after authentication mechanism\n");
+ }
+ unprocessedData.clear();
+ connection.write(new SafeByteArray(new byte[]{0x05, 0x00}));
+ state = State.WaitingForRequest;
+ }
+ }
+ }
+ else if (State.WaitingForRequest.equals(state)) {
+ if (unprocessedData.getSize() >= 5) {
+ ByteArray requestID = new ByteArray();
+ int i = 5;
+ int hostnameSize = unprocessedData.getData()[4];
+ while (i < 5 + hostnameSize && i < unprocessedData.getSize()) {
+ requestID.append(unprocessedData.getData()[i]);
+ ++i;
+ }
+ // Skip the port: 2 byte large, one already skipped. Add one for comparison with size
+ i += 2;
+ if (i <= unprocessedData.getSize()) {
+ if (i != unprocessedData.getSize()) {
+ logger_.fine("Junk after authentication mechanism\n");
+ }
+ unprocessedData.clear();
+ streamID = requestID.toString();
+ boolean hasBytestream = bytestreams.hasBytestream(streamID);
+ SafeByteArray result = new SafeByteArray("0x05");
+ result.append(hasBytestream ? (byte)0x0 : (byte)0x4);
+ result.append(new ByteArray(new byte[]{0x00, 0x03}));
+ result.append(Integer.toString(requestID.getSize()));
+ result.append(requestID.append(new ByteArray(new byte[]{0x00, 0x00})));
+ if (!hasBytestream) {
+ logger_.fine("Readstream or Wrtiestream with ID " + streamID + " not found!\n");
+ connection.write(result);
+ finish(true);
+ }
+ else {
+ logger_.fine("Found stream. Sent OK.\n");
+ connection.write(result);
+ state = State.ReadyForTransfer;
+ }
+ }
+ }
+ }
+ }
+
+ private void handleDataRead(SafeByteArray data) {
+ if (!State.ReadingData.equals(state)) {
+ unprocessedData.append(data);
+ process();
+ } else {
+ writeBytestream.write(new ByteArray(data));
+ // onBytesReceived(data.size());
+ }
+ }
+
+ private void handleDisconnected(final Connection.Error error) {
+ logger_.fine((error != null ? (error.equals(Connection.Error.ReadError) ? "Read Error" : "Write Error") : "No Error") + "\n");
+ finish(error != null ? true : false);
+ }
+
+ private void handleDataAvailable() {
+ if (waitingForData) {
+ sendData();
+ }
+ }
+
+ private void sendData() {
+ if (!readBytestream.isFinished()) {
+ //try {
+ SafeByteArray dataToSend = new SafeByteArray(readBytestream.read((chunkSize)));
+ if (!dataToSend.isEmpty()) {
+ connection.write(dataToSend);
+ onBytesSent.emit((long)dataToSend.getSize());
+ waitingForData = false;
+ }
+ else {
+ waitingForData = true;
+ }
+ //}
+ //catch (BytestreamException e) {
+ // finish(true);
+ //}
+ }
+ else {
+ finish(false);
+ }
+ }
+} \ No newline at end of file