summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/isode/stroke/filetransfer/DefaultFileTransferTransporter.java')
-rw-r--r--src/com/isode/stroke/filetransfer/DefaultFileTransferTransporter.java317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/com/isode/stroke/filetransfer/DefaultFileTransferTransporter.java b/src/com/isode/stroke/filetransfer/DefaultFileTransferTransporter.java
new file mode 100644
index 0000000..f06407e
--- /dev/null
+++ b/src/com/isode/stroke/filetransfer/DefaultFileTransferTransporter.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2013-2015 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.elements.JingleS5BTransportPayload;
+import com.isode.stroke.signals.Signal2;
+import com.isode.stroke.signals.Signal3;
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.jid.JID;
+import java.util.Vector;
+import java.util.logging.Logger;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.Slot2;
+import com.isode.stroke.base.IDGenerator;
+import com.isode.stroke.base.SafeByteArray;
+import com.isode.stroke.network.ConnectionFactory;
+import com.isode.stroke.network.TimerFactory;
+import com.isode.stroke.elements.S5BProxyRequest;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.stringcodecs.Hexify;
+
+public class DefaultFileTransferTransporter extends FileTransferTransporter {
+
+ private JID initiator;
+ private JID responder;
+ private Role role;
+ private SOCKS5BytestreamRegistry s5bRegistry;
+ private SOCKS5BytestreamServerManager s5bServerManager;
+ private SOCKS5BytestreamProxiesManager s5bProxy;
+ private CryptoProvider crypto;
+ private IQRouter router;
+ private LocalJingleTransportCandidateGenerator localCandidateGenerator;
+ private RemoteJingleTransportCandidateSelector remoteCandidateSelector;
+ private String s5bSessionID;
+ private SOCKS5BytestreamClientSession remoteS5BClientSession;
+ private Logger logger_ = Logger.getLogger(this.getClass().getName());
+
+ public enum Role {
+ Initiator,
+ Responder
+ };
+
+ public DefaultFileTransferTransporter(
+ final JID initiator,
+ final JID responder,
+ Role role,
+ SOCKS5BytestreamRegistry s5bRegistry,
+ SOCKS5BytestreamServerManager s5bServerManager,
+ SOCKS5BytestreamProxiesManager s5bProxy,
+ IDGenerator idGenerator,
+ ConnectionFactory connectionFactory,
+ TimerFactory timerFactory,
+ CryptoProvider crypto,
+ IQRouter router,
+ final FileTransferOptions options) {
+ this.initiator = initiator;
+ this.responder = responder;
+ this.role = role;
+ this.s5bRegistry = s5bRegistry;
+ this.s5bProxy = s5bProxy;
+ this.crypto = crypto;
+ this.router = router;
+ localCandidateGenerator = new LocalJingleTransportCandidateGenerator(
+ s5bServerManager,
+ s5bProxy,
+ (Role.Initiator.equals(role) ? initiator : responder),
+ idGenerator,
+ options);
+ localCandidateGenerator.onLocalTransportCandidatesGenerated.connect(new Slot1<Vector<JingleS5BTransportPayload.Candidate>>() {
+ @Override
+ public void call(Vector<JingleS5BTransportPayload.Candidate> e) {
+ handleLocalCandidatesGenerated(e);
+ }
+ });
+
+ remoteCandidateSelector = new RemoteJingleTransportCandidateSelector(
+ connectionFactory,
+ timerFactory,
+ options);
+ remoteCandidateSelector.onCandidateSelectFinished.connect(new Slot2<JingleS5BTransportPayload.Candidate, SOCKS5BytestreamClientSession>() {
+ @Override
+ public void call(JingleS5BTransportPayload.Candidate c, SOCKS5BytestreamClientSession s) {
+ handleRemoteCandidateSelectFinished(c, s);
+ }
+ });
+ }
+
+ public void initialize() {
+ s5bSessionID = s5bRegistry.generateSessionID();
+ }
+
+ public void initialize(final String s5bSessionID) {
+ this.s5bSessionID = s5bSessionID;
+ }
+
+ public void startGeneratingLocalCandidates() {
+ localCandidateGenerator.start();
+ }
+
+ public void stopGeneratingLocalCandidates() {
+ localCandidateGenerator.stop();
+ }
+
+ public void addRemoteCandidates(
+ final Vector<JingleS5BTransportPayload.Candidate> candidates, final String dstAddr) {
+ remoteCandidateSelector.setSOCKS5DstAddr(dstAddr.isEmpty() ? getRemoteCandidateSOCKS5DstAddr() : dstAddr);
+ remoteCandidateSelector.addCandidates(candidates);
+ }
+
+ public void startTryingRemoteCandidates() {
+ remoteCandidateSelector.startSelectingCandidate();
+ }
+
+ public void stopTryingRemoteCandidates() {
+ remoteCandidateSelector.stopSelectingCandidate();
+ }
+
+ public void startActivatingProxy(final JID proxyServiceJID) {
+ // activate proxy
+ logger_.fine("Start activating proxy " + proxyServiceJID.toString() + " with sid = " + s5bSessionID + ".\n");
+ S5BProxyRequest proxyRequest = new S5BProxyRequest();
+ proxyRequest.setSID(s5bSessionID);
+ proxyRequest.setActivate(Role.Initiator.equals(role) ? responder : initiator);
+
+ GenericRequest<S5BProxyRequest> request = new GenericRequest<S5BProxyRequest>(IQ.Type.Set, proxyServiceJID, proxyRequest, router);
+ request.onResponse.connect(new Slot2<S5BProxyRequest, ErrorPayload>() {
+ @Override
+ public void call(S5BProxyRequest s, ErrorPayload e) {
+ handleActivateProxySessionResult(s5bSessionID, e);
+ }
+ });
+ request.send();
+ }
+
+ public void stopActivatingProxy() {
+ // TODO
+ assert(false);
+ }
+
+ public TransportSession createIBBSendSession(
+ final String sessionID, int blockSize, ReadBytestream stream) {
+ if (s5bServerManager.getServer() != null) {
+ closeLocalSession();
+ }
+ closeRemoteSession();
+ IBBSendSession ibbSession = new IBBSendSession(
+ sessionID, initiator, responder, stream, router);
+ ibbSession.setBlockSize(blockSize);
+ return new IBBSendTransportSession(ibbSession);
+ }
+
+ public TransportSession createIBBReceiveSession(
+ final String sessionID, int size, WriteBytestream stream) {
+ if (s5bServerManager.getServer() != null) {
+ closeLocalSession();
+ }
+ closeRemoteSession();
+ IBBReceiveSession ibbSession = new IBBReceiveSession(
+ sessionID, initiator, responder, size, stream, router);
+ return new IBBReceiveTransportSession(ibbSession);
+ }
+
+ public TransportSession createRemoteCandidateSession(
+ ReadBytestream stream, final JingleS5BTransportPayload.Candidate candidate) {
+ closeLocalSession();
+ return new S5BTransportSession<SOCKS5BytestreamClientSession>(
+ remoteS5BClientSession, stream);
+ }
+
+ public TransportSession createRemoteCandidateSession(
+ WriteBytestream stream, final JingleS5BTransportPayload.Candidate candidate) {
+ closeLocalSession();
+ return new S5BTransportSession<SOCKS5BytestreamClientSession>(
+ remoteS5BClientSession, stream);
+ }
+
+ public TransportSession createLocalCandidateSession(
+ ReadBytestream stream, final JingleS5BTransportPayload.Candidate candidate) {
+ closeRemoteSession();
+ TransportSession transportSession = null;
+ if (JingleS5BTransportPayload.Candidate.Type.ProxyType.equals(candidate.type)) {
+ SOCKS5BytestreamClientSession proxySession = s5bProxy.getProxySessionAndCloseOthers(candidate.jid, getLocalCandidateSOCKS5DstAddr());
+ assert(proxySession != null);
+ transportSession = new S5BTransportSession<SOCKS5BytestreamClientSession>(proxySession, stream);
+ }
+
+ if (transportSession == null) {
+ SOCKS5BytestreamServerSession serverSession = getServerSession();
+ if (serverSession != null) {
+ transportSession = new S5BTransportSession<SOCKS5BytestreamServerSession>(serverSession, stream);
+ }
+ }
+
+ if (transportSession == null) {
+ transportSession = new FailingTransportSession();
+ }
+ return transportSession;
+ }
+
+ public TransportSession createLocalCandidateSession(
+ WriteBytestream stream, final JingleS5BTransportPayload.Candidate candidate) {
+ closeRemoteSession();
+ TransportSession transportSession = null;
+ if (JingleS5BTransportPayload.Candidate.Type.ProxyType.equals(candidate.type)) {
+ SOCKS5BytestreamClientSession proxySession = s5bProxy.getProxySessionAndCloseOthers(candidate.jid, getLocalCandidateSOCKS5DstAddr());
+ assert(proxySession != null);
+ transportSession = new S5BTransportSession<SOCKS5BytestreamClientSession>(proxySession, stream);
+ }
+
+ if (transportSession == null) {
+ SOCKS5BytestreamServerSession serverSession = getServerSession();
+ if (serverSession != null) {
+ transportSession = new S5BTransportSession<SOCKS5BytestreamServerSession>(serverSession, stream);
+ }
+ }
+
+ if (transportSession == null) {
+ transportSession = new FailingTransportSession();
+ }
+ return transportSession;
+ }
+
+ private void handleLocalCandidatesGenerated(final Vector<JingleS5BTransportPayload.Candidate> candidates) {
+ s5bRegistry.setHasBytestream(getSOCKS5DstAddr(), true);
+ s5bProxy.connectToProxies(getSOCKS5DstAddr());
+ onLocalCandidatesGenerated.emit(s5bSessionID, candidates, getSOCKS5DstAddr());
+ }
+
+ private void handleRemoteCandidateSelectFinished(
+ final JingleS5BTransportPayload.Candidate candidate,
+ SOCKS5BytestreamClientSession session) {
+ remoteS5BClientSession = session;
+ onRemoteCandidateSelectFinished.emit(s5bSessionID, candidate);
+ }
+
+ private void handleActivateProxySessionResult(final String sessionID, ErrorPayload error) {
+ onProxyActivated.emit(sessionID, error);
+ }
+
+ private void closeLocalSession() {
+ s5bRegistry.setHasBytestream(getSOCKS5DstAddr(), false);
+ if (s5bServerManager.getServer() != null) {
+ Vector<SOCKS5BytestreamServerSession> serverSessions = s5bServerManager.getServer().getSessions(getSOCKS5DstAddr());
+ for(SOCKS5BytestreamServerSession session : serverSessions) {
+ session.stop();
+ }
+ }
+ }
+ private void closeRemoteSession() {
+ if (remoteS5BClientSession != null) {
+ remoteS5BClientSession.stop();
+ remoteS5BClientSession = null;
+ }
+ }
+
+ private SOCKS5BytestreamServerSession getServerSession() {
+ s5bRegistry.setHasBytestream(getSOCKS5DstAddr(), false);
+ Vector<SOCKS5BytestreamServerSession> serverSessions = s5bServerManager.getServer().getSessions(getSOCKS5DstAddr());
+ while (serverSessions.size() > 1) {
+ SOCKS5BytestreamServerSession session = serverSessions.lastElement();
+ serverSessions.remove(serverSessions.lastElement());
+ session.stop();
+ }
+ return !serverSessions.isEmpty() ? serverSessions.get(0) : null;
+ }
+
+ private String getSOCKS5DstAddr() {
+ String result = "";
+ if (Role.Initiator.equals(role)) {
+ result = getInitiatorCandidateSOCKS5DstAddr();
+ logger_.fine("Initiator S5B DST.ADDR = " + s5bSessionID + " + " + initiator.toString() + " + " + responder.toString() + " : " + result + "\n");
+ }
+ else {
+ result = getResponderCandidateSOCKS5DstAddr();
+ logger_.fine("Responder S5B DST.ADDR = " + s5bSessionID + " + " + responder.toString() + " + " + initiator.toString() + " : " + result + "\n");
+ }
+ return result;
+ }
+
+ private String getInitiatorCandidateSOCKS5DstAddr() {
+ return Hexify.hexify(crypto.getSHA1Hash(new SafeByteArray(s5bSessionID + initiator.toString() + responder.toString())));
+ }
+
+ private String getResponderCandidateSOCKS5DstAddr() {
+ return Hexify.hexify(crypto.getSHA1Hash(new SafeByteArray(s5bSessionID + responder.toString() + initiator.toString())));
+ }
+
+ private String getRemoteCandidateSOCKS5DstAddr() {
+ if (Role.Initiator.equals(role)) {
+ return getResponderCandidateSOCKS5DstAddr();
+ }
+ else {
+ return getInitiatorCandidateSOCKS5DstAddr();
+ }
+ }
+
+ private String getLocalCandidateSOCKS5DstAddr() {
+ if (Role.Responder.equals(role)) {
+ return getResponderCandidateSOCKS5DstAddr();
+ }
+ else {
+ return getInitiatorCandidateSOCKS5DstAddr();
+ }
+ }
+} \ No newline at end of file