From 794fec2873e69ec047974416768b32b69754dad1 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Sun, 8 Mar 2015 16:36:49 +0100
Subject: Add resource management for S5B server and port forwardings

This patchs adds management of the SOCKS5 bytestream server and port
forwarding setup to Swiften. The first file-transfer, regardless of
incoming or outgoing transfer, will start and initialize the S5B server
and if configured try to set up port forwarding. The last file-transfer
to finish will will close the server and remove the port forwarding.

Test-Information:

Tested with upcoming ConcurrentFileTransferTest.cpp and running multiple
S5B file-transfers in parallel.

Change-Id: Idd09d3d0ef9498fc9435b0aee81f2b1061783342

diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp
index c684a90..6a059bf 100644
--- a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp
@@ -23,7 +23,8 @@
 #include <Swiften/Elements/JingleS5BTransportPayload.h>
 #include <Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h>
 #include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.h>
 
 static const unsigned int LOCAL_PREFERENCE = 0;
 
@@ -39,7 +40,10 @@ LocalJingleTransportCandidateGenerator::LocalJingleTransportCandidateGenerator(
 			s5bProxy(s5bProxy), 
 			ownJID(ownJID),
 			idGenerator(idGenerator),
-			options_(options) {
+			options_(options),
+			triedServerInit_(false),
+			triedForwarding_(false),
+			triedProxyDiscovery_(false) {
 }
 
 LocalJingleTransportCandidateGenerator::~LocalJingleTransportCandidateGenerator() {
@@ -48,43 +52,69 @@ LocalJingleTransportCandidateGenerator::~LocalJingleTransportCandidateGenerator(
 
 void LocalJingleTransportCandidateGenerator::start() {
 	assert(!s5bServerInitializeRequest);
-	s5bServerInitializeRequest = s5bServerManager->createInitializeRequest();
-	s5bServerInitializeRequest->onFinished.connect(
-			boost::bind(&LocalJingleTransportCandidateGenerator::handleS5BServerInitialized, this, _1));
-	s5bServerInitializeRequest->start();
-}
+	if (options_.isDirectAllowed() || options_.isAssistedAllowed()) {
+		s5bServerResourceUser_ = s5bServerManager->aquireResourceUser();
+		if (s5bServerResourceUser_->isInitialized()) {
+			handleS5BServerInitialized(true);
+		}
+		else {
+			s5bServerResourceUser_->onSuccessfulInitialized.connect(boost::bind(&LocalJingleTransportCandidateGenerator::handleS5BServerInitialized, this, _1));
+		}
+	} else {
+		handleS5BServerInitialized(false);
+	}
 
-void LocalJingleTransportCandidateGenerator::stop() {
-	if (s5bServerInitializeRequest) {
-		s5bServerInitializeRequest->stop();
-		s5bServerInitializeRequest.reset();
+	if (options_.isProxiedAllowed()) {
+		s5bProxy->onDiscoveredProxiesChanged.connect(boost::bind(&LocalJingleTransportCandidateGenerator::handleDiscoveredProxiesChanged, this));
+		if (s5bProxy->getOrDiscoverS5BProxies().is_initialized()) {
+			handleDiscoveredProxiesChanged();
+		}
 	}
+}
 
-	s5bServerManager->stop();
+void LocalJingleTransportCandidateGenerator::stop() {
+	s5bServerResourceUser_.reset();
 }
 
 void LocalJingleTransportCandidateGenerator::handleS5BServerInitialized(bool success) {
+	triedServerInit_ = true;
 	if (success) {
-		if (options_.isProxiedAllowed()) {
-			if (s5bProxy->getOrDiscoverS5BProxies()) {
-				emitOnLocalTransportCandidatesGenerated();
-			} else {
-				s5bProxy->onDiscoveredProxiesChanged.connect(boost::bind(&LocalJingleTransportCandidateGenerator::handleDiscoveredProxiesChanged, this));
+		if (options_.isAssistedAllowed()) {
+			// try to setup port forwarding
+			s5bServerPortForwardingUser_ = s5bServerManager->aquirePortForwardingUser();
+			if (s5bServerPortForwardingUser_->isForwardingSetup()) {
+				handlePortForwardingSetup(true);
 			}
 		}
-		else {
-			emitOnLocalTransportCandidatesGenerated();
-		}
 	}
 	else {
 		SWIFT_LOG(warning) << "Unable to start SOCKS5 server" << std::endl;
+		s5bServerResourceUser_.reset();
+		handlePortForwardingSetup(false);
 	}
+	checkS5BCandidatesReady();
+}
 
-	s5bServerInitializeRequest->stop();
-	s5bServerInitializeRequest.reset();
+void LocalJingleTransportCandidateGenerator::handlePortForwardingSetup(bool success) {
+	triedForwarding_ = true;
+	checkS5BCandidatesReady();
+}
 
+void LocalJingleTransportCandidateGenerator::handleDiscoveredProxiesChanged() {
+	s5bProxy->onDiscoveredProxiesChanged.disconnect(boost::bind(&LocalJingleTransportCandidateGenerator::handleDiscoveredProxiesChanged, this));
+	triedProxyDiscovery_ = true;
+	checkS5BCandidatesReady();
 }
 
+void LocalJingleTransportCandidateGenerator::checkS5BCandidatesReady() {
+	if ((!options_.isDirectAllowed()  || (options_.isDirectAllowed()  && triedServerInit_)) &&
+		(!options_.isProxiedAllowed() || (options_.isProxiedAllowed() && triedProxyDiscovery_)) &&
+		(!options_.isDirectAllowed()  || (options_.isDirectAllowed()  && triedServerInit_))) {
+		emitOnLocalTransportCandidatesGenerated();
+	}
+}
+
+
 void LocalJingleTransportCandidateGenerator::emitOnLocalTransportCandidatesGenerated() {
 	std::vector<JingleS5BTransportPayload::Candidate> candidates;
 
@@ -116,7 +146,7 @@ void LocalJingleTransportCandidateGenerator::emitOnLocalTransportCandidatesGener
 		}
 	}
 
-	if (options_.isProxiedAllowed()) {
+	if (options_.isProxiedAllowed() && s5bProxy->getOrDiscoverS5BProxies().is_initialized()) {
 		foreach(S5BProxyRequest::ref proxy, s5bProxy->getOrDiscoverS5BProxies().get()) {
 			if (proxy->getStreamHost()) { // FIXME: Added this test, because there were cases where this wasn't initialized. Investigate this. (Remko)
 				JingleS5BTransportPayload::Candidate candidate;
@@ -135,29 +165,4 @@ void LocalJingleTransportCandidateGenerator::emitOnLocalTransportCandidatesGener
 	onLocalTransportCandidatesGenerated(candidates);
 }
 
-void LocalJingleTransportCandidateGenerator::handleDiscoveredProxiesChanged() {
-	s5bProxy->onDiscoveredProxiesChanged.disconnect(boost::bind(&LocalJingleTransportCandidateGenerator::handleDiscoveredProxiesChanged, this));
-	emitOnLocalTransportCandidatesGenerated();
-}
-
-/*void LocalJingleTransportCandidateGenerator::handleS5BProxiesDiscovered() {
-	foreach(S5BProxyRequest::ref proxy, s5bProxiesDiscoverRequest->getS5BProxies()) {
-		if (proxy->getStreamHost()) { // FIXME: Added this test, because there were cases where this wasn't initialized. Investigate this. (Remko)
-			JingleS5BTransportPayload::Candidate candidate;
-			candidate.type = JingleS5BTransportPayload::Candidate::ProxyType;
-			candidate.jid = (*proxy->getStreamHost()).jid;
-			candidate.hostPort = (*proxy->getStreamHost()).addressPort;
-			candidate.priority = 65536 * 10 + LOCAL_PREFERENCE;
-			candidate.cid = idGenerator.generateID();
-			s5bCandidatesPayload->addCandidate(candidate);
-		}
-	}
-
-	haveS5BProxyCandidates = true;
-	s5bProxiesDiscoverRequest->stop();
-	s5bProxiesDiscoverRequest.reset();
-
-	checkS5BCandidatesReady();
-}*/
-
 }
diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
index ed2b4f0..bbf17a1 100644
--- a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
@@ -26,6 +26,8 @@ namespace Swift {
 	class SOCKS5BytestreamServerManager;
 	class SOCKS5BytestreamProxiesManager;
 	class SOCKS5BytestreamServerInitializeRequest;
+	class SOCKS5BytestreamServerResourceUser;
+	class SOCKS5BytestreamServerPortForwardingUser;
 	class JingleS5BTransportPayload;
 
 	class LocalJingleTransportCandidateGenerator {
@@ -45,6 +47,7 @@ namespace Swift {
 
 		private:
 			void handleS5BServerInitialized(bool success);
+			void handlePortForwardingSetup(bool success);
 			void handleDiscoveredProxiesChanged();
 
 			void checkS5BCandidatesReady();
@@ -56,6 +59,11 @@ namespace Swift {
 			JID ownJID;
 			IDGenerator* idGenerator;
 			boost::shared_ptr<SOCKS5BytestreamServerInitializeRequest> s5bServerInitializeRequest;
+			boost::shared_ptr<SOCKS5BytestreamServerResourceUser> s5bServerResourceUser_;
+			boost::shared_ptr<SOCKS5BytestreamServerPortForwardingUser> s5bServerPortForwardingUser_;
+			bool triedServerInit_;
+			bool triedForwarding_;
+			bool triedProxyDiscovery_;
 			FileTransferOptions options_;
 	};
 }
diff --git a/Swiften/FileTransfer/SConscript b/Swiften/FileTransfer/SConscript
index 88cfc00..ff162ad 100644
--- a/Swiften/FileTransfer/SConscript
+++ b/Swiften/FileTransfer/SConscript
@@ -26,7 +26,6 @@ sources = [
 		"IBBSendTransportSession.cpp",
 		"IBBReceiveTransportSession.cpp",
 		"SOCKS5BytestreamClientSession.cpp",
-		"SOCKS5BytestreamServerInitializeRequest.cpp",
 		"SOCKS5BytestreamServerManager.cpp",
 		"SOCKS5BytestreamServer.cpp",
 		"SOCKS5BytestreamServerSession.cpp",
@@ -38,6 +37,8 @@ sources = [
 		"FileTransferManager.cpp",
 		"FileTransferManagerImpl.cpp",
 		"IncrementalBytestreamHashCalculator.cpp",
+		"SOCKS5BytestreamServerResourceUser.cpp",
+		"SOCKS5BytestreamServerPortForwardingUser.cpp",
 	]
 
 swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources))
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.cpp
deleted file mode 100644
index c32d142..0000000
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2012 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.h>
-
-#include <boost/bind.hpp>
-
-#include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h>
-
-using namespace Swift;
-
-SOCKS5BytestreamServerInitializeRequest::SOCKS5BytestreamServerInitializeRequest(SOCKS5BytestreamServerManager* manager) : manager(manager) {
-}
-
-SOCKS5BytestreamServerInitializeRequest::~SOCKS5BytestreamServerInitializeRequest() {
-}
-
-void SOCKS5BytestreamServerInitializeRequest::start() {
-	if (manager->isInitialized()) {
-		handleInitialized(true);
-	}
-	else {
-		manager->onInitialized.connect(
-				boost::bind(&SOCKS5BytestreamServerInitializeRequest::handleInitialized, this, _1));
-		manager->initialize();
-	}
-}
-
-void SOCKS5BytestreamServerInitializeRequest::stop() {
-	manager->onInitialized.disconnect(
-			boost::bind(&SOCKS5BytestreamServerInitializeRequest::handleInitialized, this, _1));
-}
-
-void SOCKS5BytestreamServerInitializeRequest::handleInitialized(bool success) {
-	onFinished(success);
-}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.h b/Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.h
deleted file mode 100644
index 57e8d36..0000000
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2012 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#pragma once
-
-#include <Swiften/Base/API.h>
-#include <Swiften/Base/boost_bsignals.h>
-
-namespace Swift {
-	class SOCKS5BytestreamServerManager;
-
-	class SWIFTEN_API SOCKS5BytestreamServerInitializeRequest {
-		public:
-			SOCKS5BytestreamServerInitializeRequest(SOCKS5BytestreamServerManager* manager);
-			~SOCKS5BytestreamServerInitializeRequest();
-
-			void start();
-			void stop();
-
-		public:
-			boost::signal<void (bool /* success */)> onFinished;
-
-		private:
-			void handleInitialized(bool success);
-
-		private:
-			SOCKS5BytestreamServerManager* manager;
-	};
-}
-
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp
index cf4ae84..43d3e46 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp
@@ -15,7 +15,8 @@
 #include <boost/smart_ptr/make_shared.hpp>
 #include <boost/bind.hpp>
 
-#include <Swiften/FileTransfer/SOCKS5BytestreamServerInitializeRequest.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.h>
 #include <Swiften/Base/foreach.h>
 #include <Swiften/Base/Log.h>
 #include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
@@ -42,7 +43,8 @@ SOCKS5BytestreamServerManager::SOCKS5BytestreamServerManager(
 			networkEnvironment(networkEnvironment),
 			natTraverser(natTraverser),
 			state(Start),
-			server(NULL) {
+			server(NULL),
+			attemptedPortMapping_(false) {
 }
 
 SOCKS5BytestreamServerManager::~SOCKS5BytestreamServerManager() {
@@ -57,9 +59,28 @@ SOCKS5BytestreamServerManager::~SOCKS5BytestreamServerManager() {
 	}
 }
 
+boost::shared_ptr<SOCKS5BytestreamServerResourceUser> SOCKS5BytestreamServerManager::aquireResourceUser() {
+	boost::shared_ptr<SOCKS5BytestreamServerResourceUser> resourceUser;
+	if (s5bServerResourceUser_.expired()) {
+		resourceUser = boost::make_shared<SOCKS5BytestreamServerResourceUser>(this);
+		s5bServerResourceUser_ = resourceUser;
+	}
+	else {
+		resourceUser = s5bServerResourceUser_.lock();
+	}
+	return resourceUser;
+}
 
-boost::shared_ptr<SOCKS5BytestreamServerInitializeRequest> SOCKS5BytestreamServerManager::createInitializeRequest() {
-	return boost::make_shared<SOCKS5BytestreamServerInitializeRequest>(this);
+boost::shared_ptr<SOCKS5BytestreamServerPortForwardingUser> SOCKS5BytestreamServerManager::aquirePortForwardingUser() {
+	boost::shared_ptr<SOCKS5BytestreamServerPortForwardingUser> portForwardingUser;
+	if (s5bServerPortForwardingUser_.expired()) {
+		portForwardingUser = boost::make_shared<SOCKS5BytestreamServerPortForwardingUser>(this);
+		s5bServerPortForwardingUser_ = portForwardingUser;
+	}
+	else {
+		portForwardingUser = s5bServerPortForwardingUser_.lock();
+	}
+	return portForwardingUser;
 }
 
 std::vector<HostAddressPort> SOCKS5BytestreamServerManager::getHostAddressPorts() const {
@@ -121,24 +142,44 @@ void SOCKS5BytestreamServerManager::initialize() {
 		assert(!server);
 		server = new SOCKS5BytestreamServer(connectionServer, bytestreamRegistry);
 		server->start();
+		checkInitializeFinished();
+	}
+}
 
-		// Retrieve public addresses
-		assert(!getPublicIPRequest);
-		publicAddress = boost::optional<HostAddress>();
-		if ((getPublicIPRequest = natTraverser->createGetPublicIPRequest())) {
-			getPublicIPRequest->onResult.connect(
-					boost::bind(&SOCKS5BytestreamServerManager::handleGetPublicIPResult, this, _1));
-			getPublicIPRequest->start();
-		}
+bool SOCKS5BytestreamServerManager::isPortForwardingReady() const {
+	return attemptedPortMapping_ && !getPublicIPRequest && !forwardPortRequest;
+}
 
-		// Forward ports
-		assert(!forwardPortRequest);
-		portMapping = boost::optional<NATPortMapping>();
-		if ((forwardPortRequest = natTraverser->createForwardPortRequest(port, port))) {
-			forwardPortRequest->onResult.connect(
-					boost::bind(&SOCKS5BytestreamServerManager::handleForwardPortResult, this, _1));
-			forwardPortRequest->start();
-		}
+void SOCKS5BytestreamServerManager::setupPortForwarding() {
+	assert(server);
+	attemptedPortMapping_ = true;
+
+	// Retrieve public addresses
+	assert(!getPublicIPRequest);
+	publicAddress = boost::optional<HostAddress>();
+	if ((getPublicIPRequest = natTraverser->createGetPublicIPRequest())) {
+		getPublicIPRequest->onResult.connect(
+				boost::bind(&SOCKS5BytestreamServerManager::handleGetPublicIPResult, this, _1));
+		getPublicIPRequest->start();
+	}
+
+	// Forward ports
+	int port = server->getAddressPort().getPort();
+	assert(!forwardPortRequest);
+	portMapping = boost::optional<NATPortMapping>();
+	if ((forwardPortRequest = natTraverser->createForwardPortRequest(port, port))) {
+		forwardPortRequest->onResult.connect(
+				boost::bind(&SOCKS5BytestreamServerManager::handleForwardPortResult, this, _1));
+		forwardPortRequest->start();
+	}
+}
+
+void SOCKS5BytestreamServerManager::removePortForwarding() {
+	// remove port forwards
+	if (portMapping) {
+		unforwardPortRequest = natTraverser->createRemovePortForwardingRequest(portMapping.get().getLocalPort(), portMapping.get().getPublicPort());
+		unforwardPortRequest->onResult.connect(boost::bind(&SOCKS5BytestreamServerManager::handleUnforwardPortResult, this, _1));
+		unforwardPortRequest->start();
 	}
 }
 
@@ -156,13 +197,6 @@ void SOCKS5BytestreamServerManager::stop() {
 		connectionServer.reset();
 	}
 
-	// remove port forwards
-	if (portMapping) {
-		unforwardPortRequest = natTraverser->createRemovePortForwardingRequest(portMapping.get().getLocalPort(), portMapping.get().getPublicPort());
-		unforwardPortRequest->onResult.connect(boost::bind(&SOCKS5BytestreamServerManager::handleUnforwardPortResult, this, _1));
-		unforwardPortRequest->start();
-	}
-
 	state = Start;
 }
 
@@ -178,8 +212,6 @@ void SOCKS5BytestreamServerManager::handleGetPublicIPResult(boost::optional<Host
 
 	getPublicIPRequest->stop();
 	getPublicIPRequest.reset();
-
-	checkInitializeFinished();
 }
 
 void SOCKS5BytestreamServerManager::handleForwardPortResult(boost::optional<NATPortMapping> mapping) {
@@ -194,8 +226,6 @@ void SOCKS5BytestreamServerManager::handleForwardPortResult(boost::optional<NATP
 
 	forwardPortRequest->stop();
 	forwardPortRequest.reset();
-
-	checkInitializeFinished();
 }
 
 void SOCKS5BytestreamServerManager::handleUnforwardPortResult(boost::optional<bool> result) {
@@ -205,13 +235,12 @@ void SOCKS5BytestreamServerManager::handleUnforwardPortResult(boost::optional<bo
 	else {
 		SWIFT_LOG(warning) << "Failed to remove port forwarding." << std::endl;
 	}
+	attemptedPortMapping_ = false;
 	unforwardPortRequest.reset();
 }
 
 void SOCKS5BytestreamServerManager::checkInitializeFinished() {
 	assert(state == Initializing);
-	if (!getPublicIPRequest && !forwardPortRequest) {
-		state = Initialized;
-		onInitialized(true);
-	}
+	state = Initialized;
+	onInitialized(true);
 }
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerManager.h b/Swiften/FileTransfer/SOCKS5BytestreamServerManager.h
index abb28b0..e8a43f9 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerManager.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerManager.h
@@ -8,6 +8,7 @@
 
 #include <vector>
 #include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
 
 #include <Swiften/Base/boost_bsignals.h>
 #include <Swiften/Network/HostAddressPort.h>
@@ -22,10 +23,14 @@ namespace Swift {
 	class SOCKS5BytestreamRegistry;
 	class ConnectionServerFactory;
 	class ConnectionServer;
-	class SOCKS5BytestreamServerInitializeRequest;
 	class SOCKS5BytestreamServer;
+	class SOCKS5BytestreamServerResourceUser;
+	class SOCKS5BytestreamServerPortForwardingUser;
 
 	class SOCKS5BytestreamServerManager {
+		friend class SOCKS5BytestreamServerResourceUser;
+		friend class SOCKS5BytestreamServerPortForwardingUser;
+
 		public:
 			SOCKS5BytestreamServerManager(
 					SOCKS5BytestreamRegistry* bytestreamRegistry,
@@ -34,7 +39,9 @@ namespace Swift {
 					NATTraverser* natTraverser);
 			~SOCKS5BytestreamServerManager();
 
-			boost::shared_ptr<SOCKS5BytestreamServerInitializeRequest> createInitializeRequest();
+			boost::shared_ptr<SOCKS5BytestreamServerResourceUser> aquireResourceUser();
+			boost::shared_ptr<SOCKS5BytestreamServerPortForwardingUser> aquirePortForwardingUser();
+
 			void stop();
 			
 			std::vector<HostAddressPort> getHostAddressPorts() const;
@@ -47,6 +54,11 @@ namespace Swift {
 		private:
 			bool isInitialized() const;
 			void initialize();
+
+			bool isPortForwardingReady() const;
+			void setupPortForwarding();
+			void removePortForwarding();
+
 			void checkInitializeFinished();
 
 			void handleGetPublicIPResult(boost::optional<HostAddress> address);
@@ -54,7 +66,7 @@ namespace Swift {
 			void handleUnforwardPortResult(boost::optional<bool> result);
 
 			boost::signal<void (bool /* success */)> onInitialized;
-
+			boost::signal<void (bool /* success */)> onPortForwardingSetup;
 
 		private:
 			friend class SOCKS5BytestreamServerInitializeRequest;
@@ -66,11 +78,16 @@ namespace Swift {
 			SOCKS5BytestreamServer* server;
 			boost::shared_ptr<ConnectionServer> connectionServer;
 			int connectionServerPort;
+
 			boost::shared_ptr<NATTraversalGetPublicIPRequest> getPublicIPRequest;
 			boost::shared_ptr<NATTraversalForwardPortRequest> forwardPortRequest;
 			boost::shared_ptr<NATTraversalRemovePortForwardingRequest> unforwardPortRequest;
 			boost::optional<HostAddress> publicAddress;
 			boost::optional<NATPortMapping> portMapping;
+			bool attemptedPortMapping_;
+
+			boost::weak_ptr<SOCKS5BytestreamServerResourceUser> s5bServerResourceUser_;
+			boost::weak_ptr<SOCKS5BytestreamServerPortForwardingUser> s5bServerPortForwardingUser_;
 	};
 }
 
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.cpp
new file mode 100644
index 0000000..9ab097b
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.h>
+
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h>
+
+#include <boost/bind.hpp>
+
+namespace Swift {
+
+SOCKS5BytestreamServerPortForwardingUser::SOCKS5BytestreamServerPortForwardingUser(SOCKS5BytestreamServerManager* s5bServerManager) : s5bServerManager_(s5bServerManager) {
+	// the server should be initialized, so we know what port to setup a forward for
+	assert(s5bServerManager->isInitialized());
+	assert(!s5bServerManager_->isPortForwardingReady());
+
+	s5bServerManager_->onPortForwardingSetup.connect(boost::bind(&SOCKS5BytestreamServerPortForwardingUser::handleServerManagerPortForwardingSetup, this, _1));
+	s5bServerManager_->setupPortForwarding();
+}
+
+SOCKS5BytestreamServerPortForwardingUser::~SOCKS5BytestreamServerPortForwardingUser() {
+	s5bServerManager_->onPortForwardingSetup.disconnect(boost::bind(&SOCKS5BytestreamServerPortForwardingUser::handleServerManagerPortForwardingSetup, this, _1));
+	if (s5bServerManager_->isPortForwardingReady()) {
+		s5bServerManager_->removePortForwarding();
+	}
+}
+
+bool SOCKS5BytestreamServerPortForwardingUser::isForwardingSetup() const {
+	return s5bServerManager_->isPortForwardingReady();
+}
+
+void SOCKS5BytestreamServerPortForwardingUser::handleServerManagerPortForwardingSetup(bool successful) {
+	onSetup(successful);
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.h b/Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.h
new file mode 100644
index 0000000..f486836
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+
+class SOCKS5BytestreamServerManager;
+
+class SOCKS5BytestreamServerPortForwardingUser {
+	public:
+		SOCKS5BytestreamServerPortForwardingUser(SOCKS5BytestreamServerManager* s5bServerManager);
+		~SOCKS5BytestreamServerPortForwardingUser();
+
+		bool isForwardingSetup() const;
+
+		boost::signal<void (bool /* success */)> onSetup;
+
+	private:
+		void handleServerManagerPortForwardingSetup(bool successful);
+
+	private:
+		SOCKS5BytestreamServerManager* s5bServerManager_;
+};
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.cpp
new file mode 100644
index 0000000..bf7d4e8
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.h>
+
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h>
+
+#include <boost/bind.hpp>
+
+namespace Swift {
+
+SOCKS5BytestreamServerResourceUser::SOCKS5BytestreamServerResourceUser(SOCKS5BytestreamServerManager* s5bServerManager) : s5bServerManager_(s5bServerManager) {
+	assert(!s5bServerManager_->isInitialized());
+	s5bServerManager_->onInitialized.connect(boost::bind(&SOCKS5BytestreamServerResourceUser::handleServerManagerInitialized, this, _1));
+	s5bServerManager_->initialize();
+}
+
+SOCKS5BytestreamServerResourceUser::~SOCKS5BytestreamServerResourceUser() {
+	if (s5bServerManager_->isInitialized()) {
+		s5bServerManager_->stop();
+	}
+}
+
+bool SOCKS5BytestreamServerResourceUser::isInitialized() const {
+	return s5bServerManager_->isInitialized();
+}
+
+void SOCKS5BytestreamServerResourceUser::handleServerManagerInitialized(bool successfulInitialize) {
+	onSuccessfulInitialized(successfulInitialize);
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.h b/Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.h
new file mode 100644
index 0000000..014689b
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+
+class SOCKS5BytestreamServerManager;
+
+class SOCKS5BytestreamServerResourceUser {
+	public:
+		SOCKS5BytestreamServerResourceUser(SOCKS5BytestreamServerManager* s5bServerManager);
+		~SOCKS5BytestreamServerResourceUser();
+
+		bool isInitialized() const;
+
+		boost::signal<void (bool /* success */)> onSuccessfulInitialized;
+
+	private:
+		void handleServerManagerInitialized(bool successfulInitialize);
+
+	private:
+		SOCKS5BytestreamServerManager* s5bServerManager_;
+};
+
+}
-- 
cgit v0.10.2-6-g49f6