summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2011-05-26 18:46:49 (GMT)
committerRemko Tronçon <git@el-tramo.be>2011-09-25 17:42:32 (GMT)
commit4f62e5ec4b42929fe3c1a68667e63cb1b7a35509 (patch)
tree0d19fac3f578dec00ccf3e58930312951e38de89 /Swiften/FileTransfer
parentde660b763459cdd707876ec244b6866abca07fa2 (diff)
downloadswift-contrib-4f62e5ec4b42929fe3c1a68667e63cb1b7a35509.zip
swift-contrib-4f62e5ec4b42929fe3c1a68667e63cb1b7a35509.tar.bz2
Google Summer of Code 2011 Project: Adding support for Jingle File Transfers (XEP-0234), Jingle SOCKS5 Bytestreams Transport Method (XEP-0260), Jingle In-Band Bytestreams Transport Method (XEP-0261) and SOCKS5 Bytestreams (XEP-0065).
License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php
Diffstat (limited to 'Swiften/FileTransfer')
-rw-r--r--Swiften/FileTransfer/ByteArrayReadBytestream.h2
-rw-r--r--Swiften/FileTransfer/ByteArrayWriteBytestream.h1
-rw-r--r--Swiften/FileTransfer/ConnectivityManager.cpp101
-rw-r--r--Swiften/FileTransfer/ConnectivityManager.h43
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp97
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h39
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp25
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h33
-rw-r--r--Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp121
-rw-r--r--Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h58
-rw-r--r--Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp25
-rw-r--r--Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h28
-rw-r--r--Swiften/FileTransfer/FileReadBytestream.cpp1
-rw-r--r--Swiften/FileTransfer/FileTransfer.h58
-rw-r--r--Swiften/FileTransfer/FileTransferManager.cpp14
-rw-r--r--Swiften/FileTransfer/FileTransferManager.h34
-rw-r--r--Swiften/FileTransfer/FileTransferManagerImpl.cpp123
-rw-r--r--Swiften/FileTransfer/FileTransferManagerImpl.h80
-rw-r--r--Swiften/FileTransfer/FileWriteBytestream.cpp8
-rw-r--r--Swiften/FileTransfer/FileWriteBytestream.h1
-rw-r--r--Swiften/FileTransfer/IBBReceiveSession.cpp9
-rw-r--r--Swiften/FileTransfer/IBBSendSession.cpp3
-rw-r--r--Swiften/FileTransfer/IBBSendSession.h2
-rw-r--r--Swiften/FileTransfer/IncomingFileTransfer.h5
-rw-r--r--Swiften/FileTransfer/IncomingFileTransferManager.cpp20
-rw-r--r--Swiften/FileTransfer/IncomingFileTransferManager.h11
-rw-r--r--Swiften/FileTransfer/IncomingJingleFileTransfer.cpp408
-rw-r--r--Swiften/FileTransfer/IncomingJingleFileTransfer.h60
-rw-r--r--Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp61
-rw-r--r--Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h33
-rw-r--r--Swiften/FileTransfer/JingleIncomingIBBTransport.cpp3
-rw-r--r--Swiften/FileTransfer/JingleTransport.h2
-rw-r--r--Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h6
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransfer.h8
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransferManager.cpp46
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransferManager.h47
-rw-r--r--Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp401
-rw-r--r--Swiften/FileTransfer/OutgoingJingleFileTransfer.h115
-rw-r--r--Swiften/FileTransfer/OutgoingSIFileTransfer.cpp4
-rw-r--r--Swiften/FileTransfer/ReadBytestream.h5
-rw-r--r--Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h4
-rw-r--r--Swiften/FileTransfer/SConscript16
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp235
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamClientSession.h94
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp72
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamProxy.h46
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp54
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamRegistry.h49
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServer.cpp14
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServer.h8
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp86
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerSession.h20
-rw-r--r--Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h36
-rw-r--r--Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp282
-rw-r--r--Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp283
-rw-r--r--Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp306
-rw-r--r--Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp16
-rw-r--r--Swiften/FileTransfer/WriteBytestream.h4
58 files changed, 3667 insertions, 99 deletions
diff --git a/Swiften/FileTransfer/ByteArrayReadBytestream.h b/Swiften/FileTransfer/ByteArrayReadBytestream.h
index 4704db6..a6945c3 100644
--- a/Swiften/FileTransfer/ByteArrayReadBytestream.h
+++ b/Swiften/FileTransfer/ByteArrayReadBytestream.h
@@ -22,6 +22,8 @@ namespace Swift {
readSize = data.size() - position;
}
std::vector<unsigned char> result(data.begin() + position, data.begin() + position + readSize);
+
+ onRead(result);
position += readSize;
return result;
}
diff --git a/Swiften/FileTransfer/ByteArrayWriteBytestream.h b/Swiften/FileTransfer/ByteArrayWriteBytestream.h
index 6c360e6..ef97ed9 100644
--- a/Swiften/FileTransfer/ByteArrayWriteBytestream.h
+++ b/Swiften/FileTransfer/ByteArrayWriteBytestream.h
@@ -16,6 +16,7 @@ namespace Swift {
virtual void write(const std::vector<unsigned char>& bytes) {
data.insert(data.end(), bytes.begin(), bytes.end());
+ onWrite(bytes);
}
const std::vector<unsigned char>& getData() const {
diff --git a/Swiften/FileTransfer/ConnectivityManager.cpp b/Swiften/FileTransfer/ConnectivityManager.cpp
new file mode 100644
index 0000000..174d6ab
--- /dev/null
+++ b/Swiften/FileTransfer/ConnectivityManager.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "ConnectivityManager.h"
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h>
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+ConnectivityManager::ConnectivityManager(PlatformNATTraversalWorker* worker) : natTraversalWorker(worker) {
+
+}
+
+ConnectivityManager::~ConnectivityManager() {
+ std::set<int> leftOpenPorts = ports;
+ foreach(int port, leftOpenPorts) {
+ removeListeningPort(port);
+ }
+}
+
+void ConnectivityManager::addListeningPort(int port) {
+ ports.insert(port);
+ boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest> getIPRequest = natTraversalWorker->createGetPublicIPRequest();
+ if (getIPRequest) {
+ getIPRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalGetPublicIPResult, this, _1));
+ getIPRequest->run();
+ }
+
+ boost::shared_ptr<PlatformNATTraversalForwardPortRequest> forwardPortRequest = natTraversalWorker->createForwardPortRequest(port, port);
+ if (forwardPortRequest) {
+ forwardPortRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalForwardPortResult, this, _1));
+ forwardPortRequest->run();
+ }
+}
+
+void ConnectivityManager::removeListeningPort(int port) {
+ SWIFT_LOG(debug) << "remove listening port " << port << std::endl;
+ ports.erase(port);
+ boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest> removePortForwardingRequest = natTraversalWorker->createRemovePortForwardingRequest(port, port);
+ if (removePortForwardingRequest) {
+ removePortForwardingRequest->run();
+ }
+}
+
+std::vector<HostAddressPort> ConnectivityManager::getHostAddressPorts() const {
+ PlatformNetworkEnvironment env;
+ std::vector<HostAddressPort> results;
+
+ std::vector<HostAddress> addresses;
+
+ foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+ foreach (HostAddress address, iface->getAddresses()) {
+ foreach (int port, ports) {
+ results.push_back(HostAddressPort(address, port));
+ }
+ }
+ }
+
+ return results;
+}
+
+std::vector<HostAddressPort> ConnectivityManager::getAssistedHostAddressPorts() const {
+ std::vector<HostAddressPort> results;
+
+ if (publicAddress) {
+ foreach (int port, ports) {
+ results.push_back(HostAddressPort(publicAddress.get(), port));
+ }
+ }
+
+ return results;
+}
+
+void ConnectivityManager::natTraversalGetPublicIPResult(boost::optional<HostAddress> address) {
+ if (address) {
+ publicAddress = address;
+ SWIFT_LOG(debug) << "Public IP discovered as " << publicAddress.get().toString() << "." << std::endl;
+ } else {
+ SWIFT_LOG(debug) << "No public IP discoverable." << std::endl;
+ }
+}
+
+void ConnectivityManager::natTraversalForwardPortResult(boost::optional<PlatformNATTraversalForwardPortRequest::PortMapping> mapping) {
+ if (mapping) {
+ SWIFT_LOG(debug) << "Mapping port was successful." << std::endl;
+ } else {
+ SWIFT_LOG(debug) << "Mapping port has failed." << std::endl;
+ }
+}
+
+
+}
diff --git a/Swiften/FileTransfer/ConnectivityManager.h b/Swiften/FileTransfer/ConnectivityManager.h
new file mode 100644
index 0000000..87041b2
--- /dev/null
+++ b/Swiften/FileTransfer/ConnectivityManager.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <set>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/PlatformNATTraversalForwardPortRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class ConnectivityManager {
+public:
+ ConnectivityManager(PlatformNATTraversalWorker*);
+ ~ConnectivityManager();
+public:
+ void addListeningPort(int port);
+ void removeListeningPort(int port);
+
+ std::vector<HostAddressPort> getHostAddressPorts() const;
+ std::vector<HostAddressPort> getAssistedHostAddressPorts() const;
+
+private:
+ void natTraversalGetPublicIPResult(boost::optional<HostAddress> address);
+ void natTraversalForwardPortResult(boost::optional<PlatformNATTraversalForwardPortRequest::PortMapping> mapping);
+
+private:
+ PlatformNATTraversalWorker* natTraversalWorker;
+
+ std::set<int> ports;
+ boost::optional<HostAddress> publicAddress;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp
new file mode 100644
index 0000000..5b6da4c
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultLocalJingleTransportCandidateGenerator.h"
+
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/FileTransfer/ConnectivityManager.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+
+namespace Swift {
+
+DefaultLocalJingleTransportCandidateGenerator::DefaultLocalJingleTransportCandidateGenerator(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, JID& ownJID) : connectivityManager(connectivityManager), s5bRegistry(s5bRegistry), s5bProxy(s5bProxy), ownJID(ownJID) {
+}
+
+DefaultLocalJingleTransportCandidateGenerator::~DefaultLocalJingleTransportCandidateGenerator() {
+}
+
+void DefaultLocalJingleTransportCandidateGenerator::generateLocalTransportCandidates(JingleTransportPayload::ref transportPayload) {
+ if (boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transportPayload)) {
+ JingleTransportPayload::ref payL = boost::make_shared<JingleTransportPayload>();
+ payL->setSessionID(transportPayload->getSessionID());
+ onLocalTransportCandidatesGenerated(payL);
+ }
+ if (boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload)) {
+ JingleS5BTransportPayload::ref payL = boost::make_shared<JingleS5BTransportPayload>();
+ payL->setSessionID(transportPayload->getSessionID());
+ payL->setMode(JingleS5BTransportPayload::TCPMode);
+
+ const unsigned long localPreference = 0;
+
+ // get direct candidates
+ std::vector<HostAddressPort> directCandidates = connectivityManager->getHostAddressPorts();
+ foreach(HostAddressPort addressPort, directCandidates) {
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate.type = JingleS5BTransportPayload::Candidate::DirectType;
+ candidate.jid = ownJID;
+ candidate.hostPort = addressPort;
+ candidate.priority = 65536 * 126 + localPreference;
+ candidate.cid = idGenerator.generateID();
+ payL->addCandidate(candidate);
+ }
+
+ // get assissted candidates
+ std::vector<HostAddressPort> assisstedCandidates = connectivityManager->getAssistedHostAddressPorts();
+ foreach(HostAddressPort addressPort, assisstedCandidates) {
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate.type = JingleS5BTransportPayload::Candidate::AssistedType;
+ candidate.jid = ownJID;
+ candidate.hostPort = addressPort;
+ candidate.priority = 65536 * 120 + localPreference;
+ candidate.cid = idGenerator.generateID();
+ payL->addCandidate(candidate);
+ }
+
+ // get proxy candidates
+ std::vector<S5BProxyRequest::ref> proxyCandidates = s5bProxy->getS5BProxies();
+ foreach(S5BProxyRequest::ref proxy, proxyCandidates) {
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate.type = JingleS5BTransportPayload::Candidate::ProxyType;
+ candidate.jid = proxy->getStreamHost().get().jid;
+ candidate.hostPort = proxy->getStreamHost().get().addressPort;
+ candidate.priority = 65536 * 10 + localPreference;
+ candidate.cid = idGenerator.generateID();
+ payL->addCandidate(candidate);
+ }
+
+ onLocalTransportCandidatesGenerated(payL);
+ }
+
+}
+
+bool DefaultLocalJingleTransportCandidateGenerator::isActualCandidate(JingleTransportPayload::ref transportPayload) {
+ if (!transportPayload.get()) return false;
+ return false;
+}
+
+int DefaultLocalJingleTransportCandidateGenerator::getPriority(JingleTransportPayload::ref /* transportPayload */) {
+ return 0;
+}
+
+JingleTransport::ref DefaultLocalJingleTransportCandidateGenerator::selectTransport(JingleTransportPayload::ref /* transportPayload */) {
+ return JingleTransport::ref();
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h
new file mode 100644
index 0000000..7d45491
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+class ConnectivityManager;
+
+class DefaultLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+ DefaultLocalJingleTransportCandidateGenerator(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, JID& ownJID);
+ virtual ~DefaultLocalJingleTransportCandidateGenerator();
+
+ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref);
+
+ virtual bool isActualCandidate(JingleTransportPayload::ref);
+ virtual int getPriority(JingleTransportPayload::ref);
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref);
+
+private:
+ IDGenerator idGenerator;
+ ConnectivityManager* connectivityManager;
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
+ JID ownJID;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp
new file mode 100644
index 0000000..ed0386e
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultLocalJingleTransportCandidateGeneratorFactory.h"
+
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+DefaultLocalJingleTransportCandidateGeneratorFactory::DefaultLocalJingleTransportCandidateGeneratorFactory(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, const JID& ownJID) : connectivityManager(connectivityManager), s5bRegistry(s5bRegistry), s5bProxy(s5bProxy), ownJID(ownJID) {
+}
+
+DefaultLocalJingleTransportCandidateGeneratorFactory::~DefaultLocalJingleTransportCandidateGeneratorFactory() {
+}
+
+LocalJingleTransportCandidateGenerator* DefaultLocalJingleTransportCandidateGeneratorFactory::createCandidateGenerator() {
+ return new DefaultLocalJingleTransportCandidateGenerator(connectivityManager, s5bRegistry, s5bProxy, ownJID);
+}
+
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h
new file mode 100644
index 0000000..511d0a1
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class ConnectivityManager;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class DefaultLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory{
+public:
+ DefaultLocalJingleTransportCandidateGeneratorFactory(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, const JID& ownJID);
+ virtual ~DefaultLocalJingleTransportCandidateGeneratorFactory();
+
+ LocalJingleTransportCandidateGenerator* createCandidateGenerator();
+
+private:
+ ConnectivityManager* connectivityManager;
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
+ JID ownJID;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp
new file mode 100644
index 0000000..32b4df8
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultRemoteJingleTransportCandidateSelector.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+
+namespace Swift {
+
+DefaultRemoteJingleTransportCandidateSelector::DefaultRemoteJingleTransportCandidateSelector(ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : connectionFactory(connectionFactory), timerFactory(timerFactory) {
+}
+
+DefaultRemoteJingleTransportCandidateSelector::~DefaultRemoteJingleTransportCandidateSelector() {
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::addRemoteTransportCandidates(JingleTransportPayload::ref transportPayload) {
+ JingleS5BTransportPayload::ref s5bPayload;
+ transportSID = transportPayload->getSessionID();
+ if ((s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload))) {
+ foreach(JingleS5BTransportPayload::Candidate c, s5bPayload->getCandidates()) {
+ candidates.push(c);
+ }
+ }
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::selectCandidate() {
+ tryNextCandidate(true);
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::tryNextCandidate(bool error) {
+ if (error) {
+ if (s5bSession) {
+ SWIFT_LOG(debug) << "failed to connect" << std::endl;
+ }
+ if (candidates.empty()) {
+ // failed to connect to any of the candidates
+ // issue an error
+ SWIFT_LOG(debug) << "out of candidates )=" << std::endl;
+ JingleS5BTransportPayload::ref failed = boost::make_shared<JingleS5BTransportPayload>();
+ failed->setCandidateError(true);
+ failed->setSessionID(transportSID);
+ onRemoteTransportCandidateSelectFinished(failed);
+ } else {
+ lastCandidate = candidates.top();
+ // only try direct or assisted for now
+ if (lastCandidate.type == JingleS5BTransportPayload::Candidate::DirectType ||
+ lastCandidate.type == JingleS5BTransportPayload::Candidate::AssistedType || lastCandidate.type == JingleS5BTransportPayload::Candidate::ProxyType ) {
+ // create connection
+ connection = connectionFactory->createConnection();
+ s5bSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, lastCandidate.hostPort, SOCKS5BytestreamRegistry::getHostname(transportSID, requester, target), timerFactory);
+
+ // bind onReady to this method
+ s5bSession->onSessionReady.connect(boost::bind(&DefaultRemoteJingleTransportCandidateSelector::tryNextCandidate, this, _1));
+
+ std::string candidateType;
+ if (lastCandidate.type == JingleS5BTransportPayload::Candidate::DirectType) {
+ candidateType = "direct";
+ } else if (lastCandidate.type == JingleS5BTransportPayload::Candidate::AssistedType) {
+ candidateType = "assisted";
+ } else if (lastCandidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ candidateType = "proxy";
+ }
+
+ // initiate connect
+ SWIFT_LOG(debug) << "try to connect to candidate of type " << candidateType << " : " << lastCandidate.hostPort.toString() << std::endl;
+ s5bSession->start();
+
+ // that's it. we're gonna be called back
+ candidates.pop();
+ } else {
+ s5bSession.reset();
+ candidates.pop();
+ tryNextCandidate(true);
+ }
+ }
+ } else {
+ // we have a working connection, hooray
+ JingleS5BTransportPayload::ref success = boost::make_shared<JingleS5BTransportPayload>();
+ success->setCandidateUsed(lastCandidate.cid);
+ success->setSessionID(transportSID);
+ onRemoteTransportCandidateSelectFinished(success);
+ }
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::setMinimumPriority(int priority) {
+ SWIFT_LOG(debug) << "priority: " << priority << std::endl;
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::setRequesterTargtet(const JID& requester, const JID& target) {
+ this->requester = requester;
+ this->target = target;
+}
+
+SOCKS5BytestreamClientSession::ref DefaultRemoteJingleTransportCandidateSelector::getS5BSession() {
+ return s5bSession;
+}
+
+bool DefaultRemoteJingleTransportCandidateSelector::isActualCandidate(JingleTransportPayload::ref /* transportPayload */) {
+ return false;
+}
+
+int DefaultRemoteJingleTransportCandidateSelector::getPriority(JingleTransportPayload::ref /* transportPayload */) {
+ return 0;
+}
+
+JingleTransport::ref DefaultRemoteJingleTransportCandidateSelector::selectTransport(JingleTransportPayload::ref /* transportPayload */) {
+ return JingleTransport::ref();
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h
new file mode 100644
index 0000000..255acd9
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <queue>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/Connection.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+
+
+namespace Swift {
+
+class ConnectionFactory;
+class TimerFactory;
+
+class DefaultRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+public:
+ DefaultRemoteJingleTransportCandidateSelector(ConnectionFactory*, TimerFactory*);
+ virtual ~DefaultRemoteJingleTransportCandidateSelector();
+
+ virtual void addRemoteTransportCandidates(JingleTransportPayload::ref);
+ virtual void selectCandidate();
+ virtual void setMinimumPriority(int);
+ void setRequesterTargtet(const JID& requester, const JID& target);
+ virtual SOCKS5BytestreamClientSession::ref getS5BSession();
+
+ virtual bool isActualCandidate(JingleTransportPayload::ref);
+ virtual int getPriority(JingleTransportPayload::ref);
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref);
+
+private:
+ void tryNextCandidate(bool error);
+
+private:
+ ConnectionFactory* connectionFactory;
+ TimerFactory* timerFactory;
+
+ std::priority_queue<JingleS5BTransportPayload::Candidate, std::vector<JingleS5BTransportPayload::Candidate>, JingleS5BTransportPayload::CompareCandidate> candidates;
+
+ std::string transportSID;
+ boost::shared_ptr<Connection> connection;
+ boost::shared_ptr<SOCKS5BytestreamClientSession> s5bSession;
+ JingleS5BTransportPayload::Candidate lastCandidate;
+ JID requester;
+ JID target;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp
new file mode 100644
index 0000000..8ebbf46
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultRemoteJingleTransportCandidateSelectorFactory.h"
+
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+DefaultRemoteJingleTransportCandidateSelectorFactory::DefaultRemoteJingleTransportCandidateSelectorFactory(ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : connectionFactory(connectionFactory), timerFactory(timerFactory) {
+}
+
+DefaultRemoteJingleTransportCandidateSelectorFactory::~DefaultRemoteJingleTransportCandidateSelectorFactory() {
+}
+
+RemoteJingleTransportCandidateSelector* DefaultRemoteJingleTransportCandidateSelectorFactory::createCandidateSelector() {
+ return new DefaultRemoteJingleTransportCandidateSelector(connectionFactory, timerFactory);
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h
new file mode 100644
index 0000000..ca29e1f
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+
+namespace Swift {
+
+class ConnectionFactory;
+class TimerFactory;
+
+class DefaultRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+ DefaultRemoteJingleTransportCandidateSelectorFactory(ConnectionFactory*, TimerFactory*);
+ virtual ~DefaultRemoteJingleTransportCandidateSelectorFactory();
+
+ RemoteJingleTransportCandidateSelector* createCandidateSelector();
+
+private:
+ ConnectionFactory* connectionFactory;
+ TimerFactory* timerFactory;
+};
+
+}
diff --git a/Swiften/FileTransfer/FileReadBytestream.cpp b/Swiften/FileTransfer/FileReadBytestream.cpp
index 84d6490..f0139b8 100644
--- a/Swiften/FileTransfer/FileReadBytestream.cpp
+++ b/Swiften/FileTransfer/FileReadBytestream.cpp
@@ -30,6 +30,7 @@ std::vector<unsigned char> FileReadBytestream::read(size_t size) {
assert(stream->good());
stream->read(reinterpret_cast<char*>(&result[0]), size);
result.resize(stream->gcount());
+ onRead(result);
return result;
}
diff --git a/Swiften/FileTransfer/FileTransfer.h b/Swiften/FileTransfer/FileTransfer.h
new file mode 100644
index 0000000..6c37d8d
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransfer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
+
+namespace Swift {
+
+class FileTransfer {
+public:
+ struct State {
+ enum FTState {
+ Canceled,
+ Failed,
+ Finished,
+ Negotiating,
+ Transferring,
+ WaitingForStart,
+ WaitingForAccept,
+ };
+
+ FTState state;
+ std::string message;
+
+ State(FTState state) : state(state), message("") {}
+ State(FTState state, std::string message) : state(state), message(message) {}
+ };
+
+public:
+ typedef boost::shared_ptr<FileTransfer> ref;
+
+public:
+ uintmax_t fileSizeInBytes;
+ std::string filename;
+ std::string algo;
+ std::string hash;
+
+public:
+ virtual void cancel() = 0;
+
+public:
+ boost::signal<void (int /* proccessedBytes */)> onProcessedBytes;
+ boost::signal<void (State)> onStateChange;
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+
+public:
+ virtual ~FileTransfer() {}
+};
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManager.cpp b/Swiften/FileTransfer/FileTransferManager.cpp
new file mode 100644
index 0000000..69be852
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManager.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+namespace Swift {
+
+FileTransferManager::~FileTransferManager() {
+}
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManager.h b/Swiften/FileTransfer/FileTransferManager.h
new file mode 100644
index 0000000..3a1628f
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManager.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
+
+namespace Swift {
+ class ReadBytestream;
+ class S5BProxyRequest;
+
+ class FileTransferManager {
+ public:
+ virtual ~FileTransferManager();
+
+ virtual void startListeningOnPort(int port) = 0;
+ virtual void addS5BProxy(boost::shared_ptr<S5BProxyRequest> proxy) = 0;
+
+ virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream) = 0;
+ virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream) = 0;
+
+ boost::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;
+ };
+}
diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.cpp b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
new file mode 100644
index 0000000..f89a3e9
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/FileTransferManagerImpl.h>
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include "Swiften/Disco/EntityCapsProvider.h"
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/FileTransfer/ConnectivityManager.h>
+#include <Swiften/FileTransfer/OutgoingFileTransferManager.h>
+#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Presence/PresenceOracle.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/ConnectionServerFactory.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+
+namespace Swift {
+
+FileTransferManagerImpl::FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, PlatformNATTraversalWorker* natTraversalWorker) : ownJID(ownFullJID), jingleSM(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), presenceOracle(presOracle), timerFactory(timerFactory), connectionFactory(connectionFactory), connectionServerFactory(connectionServerFactory), natTraversalWorker(natTraversalWorker), bytestreamServer(NULL) {
+ assert(!ownFullJID.isBare());
+
+ connectivityManager = new ConnectivityManager(natTraversalWorker);
+ bytestreamRegistry = new SOCKS5BytestreamRegistry();
+ bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+
+ localCandidateGeneratorFactory = new DefaultLocalJingleTransportCandidateGeneratorFactory(connectivityManager, bytestreamRegistry, bytestreamProxy, ownFullJID);
+ remoteCandidateSelectorFactory = new DefaultRemoteJingleTransportCandidateSelectorFactory(connectionFactory, timerFactory);
+ outgoingFTManager = new OutgoingFileTransferManager(ownJID, jingleSM, iqRouter, capsProvider, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy);
+ incomingFTManager = new IncomingFileTransferManager(ownJID, jingleSM, iqRouter, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy, timerFactory);
+ incomingFTManager->onIncomingFileTransfer.connect(onIncomingFileTransfer);
+}
+
+FileTransferManagerImpl::~FileTransferManagerImpl() {
+ if (bytestreamServer) {
+ bytestreamServer->stop();
+ }
+ delete incomingFTManager;
+ delete outgoingFTManager;
+ delete remoteCandidateSelectorFactory;
+ delete localCandidateGeneratorFactory;
+ delete connectivityManager;
+}
+
+void FileTransferManagerImpl::startListeningOnPort(int port) {
+ // TODO: create a server for each interface we're on
+ SWIFT_LOG(debug) << "Start listening on port " << port << " and hope it's not in use." << std::endl;
+ boost::shared_ptr<ConnectionServer> server = connectionServerFactory->createConnectionServer(HostAddress("0.0.0.0"), port);
+ server->start();
+ bytestreamServer = new SOCKS5BytestreamServer(server, bytestreamRegistry);
+ bytestreamServer->start();
+ connectivityManager->addListeningPort(port);
+}
+
+void FileTransferManagerImpl::addS5BProxy(S5BProxyRequest::ref proxy) {
+ bytestreamProxy->addS5BProxy(proxy);
+}
+
+boost::optional<JID> FileTransferManagerImpl::highestPriorityJIDSupportingFileTransfer(const JID& bareJID) {
+ JID fullReceipientJID;
+ int priority = INT_MIN;
+
+ //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
+ std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
+
+ //iterate over them
+ foreach(Presence::ref pres, presences) {
+ if (pres->getPriority() > priority) {
+ // look up caps from the jid
+ DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
+ if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) &&
+ info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
+
+ priority = pres->getPriority();
+ fullReceipientJID = pres->getFrom();
+ }
+ }
+ }
+
+ return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
+}
+
+OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream) {
+ std::string filename = filepath.filename();
+ uintmax_t sizeInBytes = boost::filesystem::file_size(filepath);
+ boost::posix_time::ptime lastModified = boost::posix_time::from_time_t(boost::filesystem::last_write_time(filepath));
+ return createOutgoingFileTransfer(to, filename, description, sizeInBytes, lastModified, bytestream);
+}
+
+OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream) {
+ StreamInitiationFileInfo fileInfo;
+ fileInfo.setDate(lastModified);
+ fileInfo.setSize(sizeInBytes);
+ fileInfo.setName(filename);
+ fileInfo.setDescription(description);
+
+ JID receipient = to;
+
+ if(receipient.isBare()) {
+ boost::optional<JID> fullJID = highestPriorityJIDSupportingFileTransfer(receipient);
+ if (fullJID.is_initialized()) {
+ receipient = fullJID.get();
+ } else {
+ return OutgoingFileTransfer::ref();
+ }
+ }
+
+ return outgoingFTManager->createOutgoingFileTransfer(receipient, bytestream, fileInfo);
+}
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.h b/Swiften/FileTransfer/FileTransferManagerImpl.h
new file mode 100644
index 0000000..b38eaea
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/optional.hpp>
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+
+namespace Swift {
+
+class Client;
+class ConnectionFactory;
+class ConnectionServerFactory;
+class ConnectivityManager;
+class EntityCapsProvider;
+class IQRouter;
+class IncomingFileTransferManager;
+class JingleSessionManager;
+class LocalJingleTransportCandidateGeneratorFactory;
+class OutgoingFileTransferManager;
+class PlatformNATTraversalWorker;
+class PresenceOracle;
+class ReadBytestream;
+class RemoteJingleTransportCandidateSelectorFactory;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamServer;
+class SOCKS5BytestreamProxy;
+class TimerFactory;
+
+class FileTransferManagerImpl : public FileTransferManager {
+public:
+ FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, PlatformNATTraversalWorker* natTraversalWorker);
+ ~FileTransferManagerImpl();
+
+ void startListeningOnPort(int port);
+ void addS5BProxy(S5BProxyRequest::ref proxy);
+
+ OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream);
+ OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream);
+
+ private:
+ boost::optional<JID> highestPriorityJIDSupportingFileTransfer(const JID& bareJID);
+
+private:
+ JID ownJID;
+
+ OutgoingFileTransferManager* outgoingFTManager;
+ IncomingFileTransferManager* incomingFTManager;
+ RemoteJingleTransportCandidateSelectorFactory* remoteCandidateSelectorFactory;
+ LocalJingleTransportCandidateGeneratorFactory* localCandidateGeneratorFactory;
+ JingleSessionManager* jingleSM;
+ IQRouter* iqRouter;
+ EntityCapsProvider* capsProvider;
+ PresenceOracle* presenceOracle;
+
+ TimerFactory* timerFactory;
+ ConnectionFactory* connectionFactory;
+ ConnectionServerFactory* connectionServerFactory;
+ PlatformNATTraversalWorker* natTraversalWorker;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ SOCKS5BytestreamServer* bytestreamServer;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+ ConnectivityManager* connectivityManager;
+};
+
+}
diff --git a/Swiften/FileTransfer/FileWriteBytestream.cpp b/Swiften/FileTransfer/FileWriteBytestream.cpp
index c38338a..6a22c6a 100644
--- a/Swiften/FileTransfer/FileWriteBytestream.cpp
+++ b/Swiften/FileTransfer/FileWriteBytestream.cpp
@@ -27,6 +27,14 @@ void FileWriteBytestream::write(const std::vector<unsigned char>& data) {
}
assert(stream->good());
stream->write(reinterpret_cast<const char*>(&data[0]), data.size());
+ onWrite(data);
+}
+
+void FileWriteBytestream::close() {
+ if (stream) {
+ stream->close();
+ stream = NULL;
+ }
}
}
diff --git a/Swiften/FileTransfer/FileWriteBytestream.h b/Swiften/FileTransfer/FileWriteBytestream.h
index 8cfa718..82c4a65 100644
--- a/Swiften/FileTransfer/FileWriteBytestream.h
+++ b/Swiften/FileTransfer/FileWriteBytestream.h
@@ -18,6 +18,7 @@ namespace Swift {
~FileWriteBytestream();
virtual void write(const std::vector<unsigned char>&);
+ void close();
private:
boost::filesystem::path file;
diff --git a/Swiften/FileTransfer/IBBReceiveSession.cpp b/Swiften/FileTransfer/IBBReceiveSession.cpp
index 566dcca..43c26be 100644
--- a/Swiften/FileTransfer/IBBReceiveSession.cpp
+++ b/Swiften/FileTransfer/IBBReceiveSession.cpp
@@ -14,6 +14,8 @@
#include <Swiften/FileTransfer/BytestreamException.h>
#include <Swiften/Queries/SetResponder.h>
+#include <cassert>
+
namespace Swift {
class IBBReceiveSession::IBBResponder : public SetResponder<IBB> {
@@ -43,14 +45,17 @@ class IBBReceiveSession::IBBResponder : public SetResponder<IBB> {
}
}
else if (ibb->getAction() == IBB::Open) {
+ SWIFT_LOG(debug) << "IBB open received" << std::endl;
sendResponse(from, id, IBB::ref());
}
else if (ibb->getAction() == IBB::Close) {
+ SWIFT_LOG(debug) << "IBB close received" << std::endl;
sendResponse(from, id, IBB::ref());
session->finish(FileTransferError(FileTransferError::ClosedError));
}
return true;
}
+ SWIFT_LOG(debug) << "wrong from/sessionID: " << from << " == " << session->from << " / " <<ibb->getStreamID() << " == " << session->id << std::endl;
return false;
}
@@ -71,6 +76,8 @@ IBBReceiveSession::IBBReceiveSession(
size(size),
router(router),
active(false) {
+ assert(!id.empty());
+ assert(from.isValid());
responder = new IBBResponder(this, router);
}
@@ -82,11 +89,13 @@ IBBReceiveSession::~IBBReceiveSession() {
}
void IBBReceiveSession::start() {
+ SWIFT_LOG(debug) << "receive session started" << std::endl;
active = true;
responder->start();
}
void IBBReceiveSession::stop() {
+ SWIFT_LOG(debug) << "receive session stopped" << std::endl;
responder->stop();
if (active) {
if (router->isAvailable()) {
diff --git a/Swiften/FileTransfer/IBBSendSession.cpp b/Swiften/FileTransfer/IBBSendSession.cpp
index 5376276..a434cfb 100644
--- a/Swiften/FileTransfer/IBBSendSession.cpp
+++ b/Swiften/FileTransfer/IBBSendSession.cpp
@@ -35,7 +35,7 @@ void IBBSendSession::stop() {
}
void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
- if (!error) {
+ if (!error && active) {
if (!bytestream->isFinished()) {
try {
std::vector<unsigned char> data = bytestream->read(blockSize);
@@ -43,6 +43,7 @@ void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
sequenceNumber++;
request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
request->send();
+ onBytesSent(data.size());
}
catch (const BytestreamException&) {
finish(FileTransferError(FileTransferError::ReadError));
diff --git a/Swiften/FileTransfer/IBBSendSession.h b/Swiften/FileTransfer/IBBSendSession.h
index 6c741cf..325f66c 100644
--- a/Swiften/FileTransfer/IBBSendSession.h
+++ b/Swiften/FileTransfer/IBBSendSession.h
@@ -32,7 +32,7 @@ namespace Swift {
}
boost::signal<void (boost::optional<FileTransferError>)> onFinished;
-
+ boost::signal<void (int)> onBytesSent;
private:
void handleIBBResponse(IBB::ref, ErrorPayload::ref);
void finish(boost::optional<FileTransferError>);
diff --git a/Swiften/FileTransfer/IncomingFileTransfer.h b/Swiften/FileTransfer/IncomingFileTransfer.h
index 1ccd76c..a6cf05e 100644
--- a/Swiften/FileTransfer/IncomingFileTransfer.h
+++ b/Swiften/FileTransfer/IncomingFileTransfer.h
@@ -9,15 +9,18 @@
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
#include <Swiften/FileTransfer/WriteBytestream.h>
namespace Swift {
- class IncomingFileTransfer {
+ class IncomingFileTransfer : public FileTransfer {
public:
typedef boost::shared_ptr<IncomingFileTransfer> ref;
virtual ~IncomingFileTransfer();
virtual void accept(WriteBytestream::ref) = 0;
+ virtual const JID& getSender() const = 0;
};
}
diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.cpp b/Swiften/FileTransfer/IncomingFileTransferManager.cpp
index 79d2391..c01c906 100644
--- a/Swiften/FileTransfer/IncomingFileTransferManager.cpp
+++ b/Swiften/FileTransfer/IncomingFileTransferManager.cpp
@@ -18,7 +18,9 @@
namespace Swift {
-IncomingFileTransferManager::IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router) : jingleSessionManager(jingleSessionManager), router(router) {
+IncomingFileTransferManager::IncomingFileTransferManager(const JID& ourFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router,
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory,
+ LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory) : ourJID(ourFullJID), jingleSessionManager(jingleSessionManager), router(router), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), timerFactory(timerFactory) {
jingleSessionManager->addIncomingSessionHandler(this);
}
@@ -29,13 +31,19 @@ IncomingFileTransferManager::~IncomingFileTransferManager() {
bool IncomingFileTransferManager::handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents) {
if (JingleContentPayload::ref content = Jingle::getContentWithDescription<JingleFileTransferDescription>(contents)) {
if (content->getTransport<JingleIBBTransportPayload>() || content->getTransport<JingleS5BTransportPayload>()) {
- RemoteJingleTransportCandidateSelectorFactory* a;
- LocalJingleTransportCandidateGeneratorFactory* b;
- IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(session, content, a, b, router);
- onIncomingFileTransfer(transfer);
+
+ JingleFileTransferDescription::ref description = content->getDescription<JingleFileTransferDescription>();
+
+ if (description && description->getOffers().size() == 1) {
+ IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(ourJID, session, content, remoteFactory, localFactory, router, bytestreamRegistry, bytestreamProxy, timerFactory);
+ onIncomingFileTransfer(transfer);
+ } else {
+ std::cerr << "Received a file-transfer request with no description or more than one file!" << std::endl;
+ session->sendTerminate(JinglePayload::Reason::FailedApplication);
+ }
}
else {
- session->terminate(JinglePayload::Reason::UnsupportedTransports);
+ session->sendTerminate(JinglePayload::Reason::UnsupportedTransports);
}
return true;
}
diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.h b/Swiften/FileTransfer/IncomingFileTransferManager.h
index 428a838..d1573f6 100644
--- a/Swiften/FileTransfer/IncomingFileTransferManager.h
+++ b/Swiften/FileTransfer/IncomingFileTransferManager.h
@@ -17,10 +17,13 @@ namespace Swift {
class JingleSessionManager;
class RemoteJingleTransportCandidateSelectorFactory;
class LocalJingleTransportCandidateGeneratorFactory;
+ class SOCKS5BytestreamRegistry;
+ class SOCKS5BytestreamProxy;
+ class TimerFactory;
class IncomingFileTransferManager : public IncomingJingleSessionHandler {
public:
- IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router);
+ IncomingFileTransferManager(const JID& ourFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory);
~IncomingFileTransferManager();
boost::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;
@@ -29,7 +32,13 @@ namespace Swift {
bool handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents);
private:
+ JID ourJID;
JingleSessionManager* jingleSessionManager;
IQRouter* router;
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+ LocalJingleTransportCandidateGeneratorFactory* localFactory;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+ TimerFactory* timerFactory;
};
}
diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
index 904b53e..1189830 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
@@ -9,29 +9,47 @@
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
-#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
-#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
-#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/foreach.h>
#include <Swiften/Elements/JingleIBBTransportPayload.h>
#include <Swiften/Elements/JingleS5BTransportPayload.h>
-#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Queries/GenericRequest.h>
namespace Swift {
IncomingJingleFileTransfer::IncomingJingleFileTransfer(
+ const JID& ourJID,
JingleSession::ref session,
JingleContentPayload::ref content,
RemoteJingleTransportCandidateSelectorFactory* candidateSelectorFactory,
LocalJingleTransportCandidateGeneratorFactory* candidateGeneratorFactory,
- IQRouter* router) :
+ IQRouter* router,
+ SOCKS5BytestreamRegistry* registry,
+ SOCKS5BytestreamProxy* proxy,
+ TimerFactory* timerFactory) :
+ ourJID(ourJID),
session(session),
router(router),
+ timerFactory(timerFactory),
initialContent(content),
- contentID(content->getName(), content->getCreator()),
state(Initial),
+ receivedBytes(0),
+ s5bRegistry(registry),
+ s5bProxy(proxy),
remoteTransportCandidateSelectFinished(false),
- localTransportCandidateSelectFinished(false) {
+ localTransportCandidateSelectFinished(false),
+ serverSession(0) {
candidateSelector = candidateSelectorFactory->createCandidateSelector();
candidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
@@ -41,13 +59,26 @@ IncomingJingleFileTransfer::IncomingJingleFileTransfer(
session->onTransportInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
session->onTransportReplaceReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2));
- session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this));
+ session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this, _1));
+ session->onSessionInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionInfoReceived, this, _1));
description = initialContent->getDescription<JingleFileTransferDescription>();
assert(description);
+ assert(description->getOffers().size() == 1);
+ StreamInitiationFileInfo fileInfo = description->getOffers().front();
+ fileSizeInBytes = fileInfo.getSize();
+ filename = fileInfo.getName();
+ hash = fileInfo.getHash();
+ algo = fileInfo.getAlgo();
+
+ waitOnHashTimer = timerFactory->createTimer(5000);
+ waitOnHashTimer->onTick.connect(boost::bind(&IncomingJingleFileTransfer::finishOffTransfer, this));
}
IncomingJingleFileTransfer::~IncomingJingleFileTransfer() {
+ stream->onWrite.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+ delete hashCalculator;
+
session->onSessionTerminateReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this));
session->onTransportReplaceReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2));
session->onTransportInfoReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
@@ -60,44 +91,87 @@ IncomingJingleFileTransfer::~IncomingJingleFileTransfer() {
}
void IncomingJingleFileTransfer::accept(WriteBytestream::ref stream) {
- assert(!stream);
+ assert(!this->stream);
this->stream = stream;
+ hashCalculator = new IncrementalBytestreamHashCalculator( algo == "md5" || hash.empty() , algo == "sha-1" || hash.empty() );
+ stream->onWrite.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+ stream->onWrite.connect(boost::bind(&IncomingJingleFileTransfer::handleWriteStreamDataReceived, this, _1));
+ onStateChange(FileTransfer::State(FileTransfer::State::Negotiating));
if (JingleIBBTransportPayload::ref ibbTransport = initialContent->getTransport<JingleIBBTransportPayload>()) {
+ SWIFT_LOG(debug) << "Got IBB transport payload!" << std::endl;
setActiveTransport(createIBBTransport(ibbTransport));
- session->accept();
+ session->sendAccept(getContentID(), initialContent->getDescriptions()[0], ibbTransport);
}
else if (JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport<JingleS5BTransportPayload>()) {
+ SWIFT_LOG(debug) << "Got S5B transport payload!" << std::endl;
state = CreatingInitialTransports;
+ s5bSessionID = s5bTransport->getSessionID().empty() ? idGenerator.generateID() : s5bTransport->getSessionID();
+ s5bDestination = SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator());
+ s5bRegistry->addWriteBytestream(s5bDestination, stream);
+ fillCandidateMap(theirCandidates, s5bTransport);
candidateSelector->addRemoteTransportCandidates(s5bTransport);
- candidateGenerator->generateLocalTransportCandidates();
+ candidateSelector->setRequesterTargtet(session->getInitiator(), ourJID);
+ s5bTransport->setSessionID(s5bSessionID);
+ candidateGenerator->generateLocalTransportCandidates(s5bTransport);
}
else {
assert(false);
}
}
+const JID& IncomingJingleFileTransfer::getSender() const {
+ return session->getInitiator();
+}
+
+void IncomingJingleFileTransfer::cancel() {
+ session->sendTerminate(JinglePayload::Reason::Cancel);
+
+ if (activeTransport) activeTransport->stop();
+ if (serverSession) serverSession->stop();
+ if (clientSession) clientSession->stop();
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+}
void IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates) {
if (state == CreatingInitialTransports) {
- if (!candidates) {
- localTransportCandidateSelectFinished = true;
+ if (JingleS5BTransportPayload::ref s5bCandidates = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(candidates)) {
+ //localTransportCandidateSelectFinished = true;
+ //JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared<JingleS5BTransportPayload>();
+ //emptyCandidates->setSessionID(s5bCandidates->getSessionID());
+ fillCandidateMap(ourCandidates, s5bCandidates);
+ session->sendAccept(getContentID(), initialContent->getDescriptions()[0], s5bCandidates);
+
+ state = NegotiatingTransport;
+ candidateSelector->selectCandidate();
}
- session->accept(candidates);
- state = NegotiatingTransport;
- candidateSelector->selectCandidate();
+ }
+ else {
+ SWIFT_LOG(debug) << "Unhandled state!" << std::endl;
}
}
void IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref transport) {
- remoteTransportCandidateSelectFinished = true;
- selectedRemoteTransportCandidate = transport;
- session->sendTransportInfo(contentID, transport);
- checkCandidateSelected();
+ SWIFT_LOG(debug) << std::endl;
+ if (state == Terminated) {
+ return;
+ }
+ if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+ //remoteTransportCandidateSelectFinished = true;
+ //selectedRemoteTransportCandidate = transport;
+ ourCandidate = s5bPayload;
+ //checkCandidateSelected();
+ decideOnUsedTransport();
+ session->sendTransportInfo(getContentID(), s5bPayload);
+ }
+ else {
+ SWIFT_LOG(debug) << "Expected something different here." << std::endl;
+ }
}
void IncomingJingleFileTransfer::checkCandidateSelected() {
+ assert(false);
if (localTransportCandidateSelectFinished && remoteTransportCandidateSelectFinished) {
if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate) && candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) {
if (candidateGenerator->getPriority(selectedLocalTransportCandidate) > candidateSelector->getPriority(selectedRemoteTransportCandidate)) {
@@ -121,37 +195,293 @@ void IncomingJingleFileTransfer::checkCandidateSelected() {
void IncomingJingleFileTransfer::setActiveTransport(JingleTransport::ref transport) {
state = Transferring;
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
activeTransport = transport;
activeTransport->onDataReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1));
+ activeTransport->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
activeTransport->start();
}
-void IncomingJingleFileTransfer::handleSessionTerminateReceived() {
- // TODO
+bool IncomingJingleFileTransfer::verifyReceviedData() {
+ if (algo.empty() || hash.empty()) {
+ SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
+ return true;
+ } else {
+ if (algo == "sha-1") {
+ SWIFT_LOG(debug) << "verify data via SHA-1 hash: " << (hash == hashCalculator->getSHA1String()) << std::endl;
+ return hash == hashCalculator->getSHA1String();
+ }
+ else if (algo == "md5") {
+ SWIFT_LOG(debug) << "verify data via MD5 hash: " << (hash == hashCalculator->getMD5String()) << std::endl;
+ return hash == hashCalculator->getMD5String();
+ }
+ else {
+ SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
+ return true;
+ }
+ }
+}
+
+void IncomingJingleFileTransfer::finishOffTransfer() {
+ if (verifyReceviedData()) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+ session->sendTerminate(JinglePayload::Reason::Success);
+ } else {
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed."));
+ session->sendTerminate(JinglePayload::Reason::MediaError);
+ }
+ state = Terminated;
+ waitOnHashTimer->stop();
+}
+
+void IncomingJingleFileTransfer::handleSessionInfoReceived(JinglePayload::ref jinglePayload) {
+ if (state == Terminated) {
+ return;
+ }
+ JingleFileTransferHash::ref transferHash = jinglePayload->getPayload<JingleFileTransferHash>();
+ if (transferHash) {
+ SWIFT_LOG(debug) << "Recevied hash information." << std::endl;
+ if (transferHash->getHashes().find("sha-1") != transferHash->getHashes().end()) {
+ algo = "sha-1";
+ hash = transferHash->getHashes().at("sha-1");
+ }
+ else if (transferHash->getHashes().find("md5") != transferHash->getHashes().end()) {
+ algo = "md5";
+ hash = transferHash->getHashes().at("md5");
+ }
+ checkIfAllDataReceived();
+ }
+}
+
+void IncomingJingleFileTransfer::handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason) {
+ SWIFT_LOG(debug) << "session terminate received" << std::endl;
+ if (activeTransport) activeTransport->stop();
+ if (reason && reason.get().type == JinglePayload::Reason::Cancel) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Other user canceled the transfer."));
+ }
+ else if (reason && reason.get().type == JinglePayload::Reason::Success) {
+ /*if (verifyReceviedData()) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+ } else {
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed."));
+ }*/
+ }
state = Terminated;
}
+void IncomingJingleFileTransfer::checkIfAllDataReceived() {
+ if (receivedBytes == fileSizeInBytes) {
+ SWIFT_LOG(debug) << "All data received." << std::endl;
+ if (hash.empty()) {
+ SWIFT_LOG(debug) << "No hash information yet. Waiting 5 seconds on hash info." << std::endl;
+ waitOnHashTimer->start();
+ } else {
+ SWIFT_LOG(debug) << "We already have hash info using " << algo << " algorithm. Finishing off transfer." << std::endl;
+ finishOffTransfer();
+ }
+ }
+ else if (receivedBytes > fileSizeInBytes) {
+ SWIFT_LOG(debug) << "We got more than we could handle!" << std::endl;
+ }
+}
+
void IncomingJingleFileTransfer::handleTransportDataReceived(const std::vector<unsigned char>& data) {
+ SWIFT_LOG(debug) << data.size() << " bytes received" << std::endl;
+ onProcessedBytes(data.size());
stream->write(data);
+ receivedBytes += data.size();
+ checkIfAllDataReceived();
+}
+
+void IncomingJingleFileTransfer::handleWriteStreamDataReceived(const std::vector<unsigned char>& data) {
+ receivedBytes += data.size();
+ checkIfAllDataReceived();
+}
+
+void IncomingJingleFileTransfer::useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) {
+ SWIFT_LOG(debug) << std::endl;
+ if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ // get proxy client session from remoteCandidateSelector
+ clientSession = candidateSelector->getS5BSession();
+
+ // wait on <activated/> transport-info
+ } else {
+ // ask s5b client
+ clientSession = candidateSelector->getS5BSession();
+ if (clientSession) {
+ state = Transferring;
+ SWIFT_LOG(debug) << clientSession->getAddressPort().toString() << std::endl;
+ clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startReceiving(stream);
+ } else {
+ SWIFT_LOG(debug) << "No S5B client session found!!!" << std::endl;
+ }
+ }
}
+void IncomingJingleFileTransfer::useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) {
+ SWIFT_LOG(debug) << std::endl;
+
+ if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ // get proxy client session from s5bRegistry
+ clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator()));
+ clientSession->onSessionReady.connect(boost::bind(&IncomingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1));
+ clientSession->start();
+
+ // on reply send activate
+ } else {
+ // ask s5b server
+ serverSession = s5bRegistry->getConnectedSession(s5bDestination);
+ if (serverSession) {
+ state = Transferring;
+ serverSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ serverSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ serverSession->startTransfer();
+ } else {
+ SWIFT_LOG(debug) << "No S5B server session found!!!" << std::endl;
+ }
+ }
+}
+
+void IncomingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) {
+ map.clear();
+ foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) {
+ map[candidate.cid] = candidate;
+ }
+}
+
+
+void IncomingJingleFileTransfer::decideOnUsedTransport() {
+ if (ourCandidate && theirCandidate) {
+ if (ourCandidate->hasCandidateError() && theirCandidate->hasCandidateError()) {
+ state = WaitingForFallbackOrTerminate;
+ return;
+ }
+ std::string our_cid = ourCandidate->getCandidateUsed();
+ std::string their_cid = theirCandidate->getCandidateUsed();
+ if (ourCandidate->hasCandidateError() && !their_cid.empty()) {
+ useTheirCandidateChoiceForTransfer(ourCandidates[their_cid]);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ else if (theirCandidate->hasCandidateError() && !our_cid.empty()) {
+ useOurCandidateChoiceForTransfer(theirCandidates[our_cid]);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ else if (!our_cid.empty() && !their_cid.empty()) {
+ // compare priorites, if same they win
+ if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) {
+ SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl;
+ session->sendTerminate(JinglePayload::Reason::FailedTransport);
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Failed to negotiate candidate."));
+ onFinished(FileTransferError(FileTransferError::PeerError));
+ return;
+ }
+
+ JingleS5BTransportPayload::Candidate our_candidate = theirCandidates[our_cid];
+ JingleS5BTransportPayload::Candidate their_candidate = ourCandidates[their_cid];
+ if (our_candidate.priority > their_candidate.priority) {
+ useOurCandidateChoiceForTransfer(our_candidate);
+ }
+ else if (our_candidate.priority < their_candidate.priority) {
+ useTheirCandidateChoiceForTransfer(their_candidate);
+ }
+ else {
+ useTheirCandidateChoiceForTransfer(their_candidate);
+ }
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ else {
+ assert(false);
+ }
+ } else {
+ SWIFT_LOG(debug) << "Can't make a transport decision yet." << std::endl;
+ }
+}
+
+void IncomingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) {
+ if (error) {
+ // indicate proxy error
+ } else {
+ // activate proxy
+ activateProxySession(proxy);
+ }
+}
+
+void IncomingJingleFileTransfer::activateProxySession(const JID &proxy) {
+ S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+ proxyRequest->setSID(s5bSessionID);
+ proxyRequest->setActivate(session->getInitiator());
+
+ boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Set, proxy, proxyRequest, router);
+ request->onResponse.connect(boost::bind(&IncomingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2));
+ request->send();
+}
+
+void IncomingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> /*request*/, ErrorPayload::ref error) {
+ SWIFT_LOG(debug) << std::endl;
+ if (error) {
+ SWIFT_LOG(debug) << "ERROR" << std::endl;
+ } else {
+ // send activated to other jingle party
+ JingleS5BTransportPayload::ref proxyActivate = boost::make_shared<JingleS5BTransportPayload>();
+ proxyActivate->setActivated(theirCandidate->getCandidateUsed());
+ session->sendTransportInfo(getContentID(), proxyActivate);
+
+ // start transferring
+ clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startReceiving(stream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+}
void IncomingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref transport) {
- localTransportCandidateSelectFinished = true;
+ SWIFT_LOG(debug) << "transport info received" << std::endl;
+ if (state == Terminated) {
+ return;
+ }
+ if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+ if (!s5bPayload->getActivated().empty()) {
+ if (ourCandidate->getCandidateUsed() == s5bPayload->getActivated()) {
+ clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startReceiving(stream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ } else {
+ SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl;
+ JingleS5BTransportPayload::ref proxyError = boost::make_shared<JingleS5BTransportPayload>();
+ proxyError->setProxyError(true);
+ proxyError->setSessionID(s5bSessionID);
+ session->sendTransportInfo(getContentID(), proxyError);
+ }
+ } else {
+ theirCandidate = s5bPayload;
+ decideOnUsedTransport();
+ }
+ }
+ else {
+ SWIFT_LOG(debug) << "Expected something different here." << std::endl;
+ }
+ /*localTransportCandidateSelectFinished = true;
selectedLocalTransportCandidate = transport;
if (candidateGenerator->isActualCandidate(transport)) {
candidateSelector->setMinimumPriority(candidateGenerator->getPriority(transport));
- }
- checkCandidateSelected();
+ }*/
+ //checkCandidateSelected();
}
void IncomingJingleFileTransfer::handleTransportReplaceReceived(const JingleContentID& content, JingleTransportPayload::ref transport) {
+ if (state == Terminated) {
+ return;
+ }
if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) {
+ SWIFT_LOG(debug) << "transport replaced with IBB" << std::endl;
setActiveTransport(createIBBTransport(ibbTransport));
- session->acceptTransport(content, transport);
- }
- else {
- session->rejectTransport(content, transport);
+ session->sendTransportAccept(content, ibbTransport);
+ } else {
+ SWIFT_LOG(debug) << "transport replaced failed" << std::endl;
+ session->sendTransportReject(content, transport);
}
}
@@ -163,7 +493,25 @@ void IncomingJingleFileTransfer::stopActiveTransport() {
}
JingleIncomingIBBTransport::ref IncomingJingleFileTransfer::createIBBTransport(JingleIBBTransportPayload::ref ibbTransport) {
- return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), ibbTransport->getSessionID(), description->getOffer()->size, router);
+ // TODO: getOffer() -> getOffers correction
+ return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), ibbTransport->getSessionID(), description->getOffers()[0].getSize(), router);
+}
+
+JingleContentID IncomingJingleFileTransfer::getContentID() const {
+ return JingleContentID(initialContent->getName(), initialContent->getCreator());
+}
+
+void IncomingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) {
+ if (state == Terminated) {
+ return;
+ }
+
+ if (error) {
+ session->sendTerminate(JinglePayload::Reason::ConnectivityError);
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ onFinished(error);
+ }
+ //
}
}
diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.h b/Swiften/FileTransfer/IncomingJingleFileTransfer.h
index 164d868..4ae0d1d 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.h
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.h
@@ -8,14 +8,21 @@
#include <boost/shared_ptr.hpp>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/Network/Timer.h>
#include <Swiften/Jingle/JingleSession.h>
#include <Swiften/Jingle/JingleContentID.h>
#include <Swiften/FileTransfer/IncomingFileTransfer.h>
#include <Swiften/FileTransfer/JingleTransport.h>
#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
#include <Swiften/Elements/JingleContentPayload.h>
#include <Swiften/Elements/JingleFileTransferDescription.h>
#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Elements/ErrorPayload.h>
namespace Swift {
class IQRouter;
@@ -23,6 +30,9 @@ namespace Swift {
class LocalJingleTransportCandidateGeneratorFactory;
class RemoteJingleTransportCandidateSelector;
class LocalJingleTransportCandidateGenerator;
+ class SOCKS5BytestreamRegistry;
+ class SOCKS5BytestreamProxy;
+ class IncrementalBytestreamHashCalculator;
class IncomingJingleFileTransfer : public IncomingFileTransfer {
public:
@@ -37,42 +47,86 @@ namespace Swift {
};
IncomingJingleFileTransfer(
+ const JID& ourJID,
JingleSession::ref,
JingleContentPayload::ref content,
RemoteJingleTransportCandidateSelectorFactory*,
LocalJingleTransportCandidateGeneratorFactory*,
- IQRouter* router);
+ IQRouter* router,
+ SOCKS5BytestreamRegistry* bytestreamRegistry,
+ SOCKS5BytestreamProxy* bytestreamProxy,
+ TimerFactory*);
~IncomingJingleFileTransfer();
virtual void accept(WriteBytestream::ref);
+ virtual const JID& getSender() const;
+ void cancel();
private:
- void handleSessionTerminateReceived();
+ void handleSessionTerminateReceived(boost::optional<JinglePayload::Reason>);
+ void handleSessionInfoReceived(JinglePayload::ref);
void handleTransportReplaceReceived(const JingleContentID&, JingleTransportPayload::ref);
void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref);
void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates);
void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref candidate);
void setActiveTransport(JingleTransport::ref transport);
void handleTransportDataReceived(const std::vector<unsigned char>& data);
+ void handleWriteStreamDataReceived(const std::vector<unsigned char>& data);
void stopActiveTransport();
void checkCandidateSelected();
JingleIncomingIBBTransport::ref createIBBTransport(JingleIBBTransportPayload::ref ibbTransport);
+ JingleContentID getContentID() const;
+ void checkIfAllDataReceived();
+ bool verifyReceviedData();
+ void finishOffTransfer();
+ void handleTransferFinished(boost::optional<FileTransferError>);
private:
+ typedef std::map<std::string, JingleS5BTransportPayload::Candidate> CandidateMap;
+
+ private:
+ void activateProxySession(const JID &proxy);
+ void handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error);
+ void proxySessionReady(const JID& proxy, bool error);
+
+ private:
+ void useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate);
+ void useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate);
+ void decideOnUsedTransport();
+ void fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload);
+
+ private:
+ JID ourJID;
JingleSession::ref session;
IQRouter* router;
+ TimerFactory* timerFactory;
JingleContentPayload::ref initialContent;
- JingleContentID contentID;
State state;
JingleFileTransferDescription::ref description;
WriteBytestream::ref stream;
+ uintmax_t receivedBytes;
+ IncrementalBytestreamHashCalculator* hashCalculator;
+ Timer::ref waitOnHashTimer;
+ IDGenerator idGenerator;
+
RemoteJingleTransportCandidateSelector* candidateSelector;
LocalJingleTransportCandidateGenerator* candidateGenerator;
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
bool remoteTransportCandidateSelectFinished;
JingleTransportPayload::ref selectedRemoteTransportCandidate;
bool localTransportCandidateSelectFinished;
JingleTransportPayload::ref selectedLocalTransportCandidate;
+ JingleS5BTransportPayload::ref ourCandidate;
+ JingleS5BTransportPayload::ref theirCandidate;
+ CandidateMap ourCandidates;
+ CandidateMap theirCandidates;
+ SOCKS5BytestreamClientSession::ref clientSession;
+ std::string s5bDestination;
+ std::string s5bSessionID;
+ SOCKS5BytestreamServerSession* serverSession;
+
JingleTransport::ref activeTransport;
};
}
diff --git a/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp
new file mode 100644
index 0000000..6b53a1b
--- /dev/null
+++ b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/StringCodecs/MD5.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+namespace Swift {
+
+IncrementalBytestreamHashCalculator::IncrementalBytestreamHashCalculator(bool doMD5, bool doSHA1) {
+ md5Hasher = doMD5 ? new MD5() : NULL;
+ sha1Hasher = doSHA1 ? new SHA1() : NULL;
+}
+
+IncrementalBytestreamHashCalculator::~IncrementalBytestreamHashCalculator() {
+ delete md5Hasher;
+ delete sha1Hasher;
+}
+
+void IncrementalBytestreamHashCalculator::feedData(const ByteArray& data) {
+ if (md5Hasher) {
+ md5Hasher->update(data);
+ }
+ if (sha1Hasher) {
+ sha1Hasher->update(data);
+ }
+}
+/*
+void IncrementalBytestreamHashCalculator::feedData(const SafeByteArray& data) {
+ if (md5Hasher) {
+ md5Hasher->update(createByteArray(data.data(), data.size()));
+ }
+ if (sha1Hasher) {
+ sha1Hasher->update(createByteArray(data.data(), data.size()));
+ }
+}*/
+
+std::string IncrementalBytestreamHashCalculator::getSHA1String() {
+ if (sha1Hasher) {
+ ByteArray result = sha1Hasher->getHash();
+ return Hexify::hexify(result);
+ } else {
+ return std::string();
+ }
+}
+
+std::string IncrementalBytestreamHashCalculator::getMD5String() {
+ if (md5Hasher) {
+ ByteArray result = md5Hasher->getHash();
+ return Hexify::hexify(result);
+ } else {
+ return std::string();
+ }
+}
+
+}
diff --git a/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h
new file mode 100644
index 0000000..64f4b5f
--- /dev/null
+++ b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/SafeByteArray.h>
+
+namespace Swift {
+
+class MD5;
+class SHA1;
+
+class IncrementalBytestreamHashCalculator {
+public:
+ IncrementalBytestreamHashCalculator(bool doMD5, bool doSHA1);
+ ~IncrementalBytestreamHashCalculator();
+
+ void feedData(const ByteArray& data);
+ //void feedData(const SafeByteArray& data);
+
+ std::string getSHA1String();
+ std::string getMD5String();
+
+private:
+ MD5* md5Hasher;
+ SHA1* sha1Hasher;
+};
+
+}
diff --git a/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
index 0ca899f..9b5c354 100644
--- a/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
+++ b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
@@ -8,8 +8,9 @@
namespace Swift {
-JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const std::string& id, size_t size, IQRouter* router) : ibbSession(from, id, size, router) {
+JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const std::string& id, size_t size, IQRouter* router) : ibbSession(id, from, size, router) {
ibbSession.onDataReceived.connect(boost::ref(onDataReceived));
+ ibbSession.onFinished.connect(boost::ref(onFinished));
}
void JingleIncomingIBBTransport::start() {
diff --git a/Swiften/FileTransfer/JingleTransport.h b/Swiften/FileTransfer/JingleTransport.h
index 1d163d0..fa296e8 100644
--- a/Swiften/FileTransfer/JingleTransport.h
+++ b/Swiften/FileTransfer/JingleTransport.h
@@ -9,6 +9,7 @@
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
namespace Swift {
class JingleTransport {
@@ -21,5 +22,6 @@ namespace Swift {
virtual void stop() = 0;
boost::signal<void (const std::vector<unsigned char>&)> onDataReceived;
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
};
}
diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
index c111005..041afe3 100644
--- a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
@@ -15,8 +15,10 @@ namespace Swift {
class LocalJingleTransportCandidateGenerator {
public:
virtual ~LocalJingleTransportCandidateGenerator();
-
- virtual void generateLocalTransportCandidates() = 0;
+ /**
+ * Should call onLocalTransportCandidatesGenerated if it has finished discovering local candidates.
+ */
+ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref) = 0;
virtual bool isActualCandidate(JingleTransportPayload::ref) = 0;
virtual int getPriority(JingleTransportPayload::ref) = 0;
diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.h b/Swiften/FileTransfer/OutgoingFileTransfer.h
index a8c1e81..1ec1ae3 100644
--- a/Swiften/FileTransfer/OutgoingFileTransfer.h
+++ b/Swiften/FileTransfer/OutgoingFileTransfer.h
@@ -6,8 +6,14 @@
#pragma once
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/FileTransfer/FileTransfer.h>
+
namespace Swift {
- class OutgoingFileTransfer {
+ class OutgoingFileTransfer : public FileTransfer {
+ public:
+ typedef boost::shared_ptr<OutgoingFileTransfer> ref;
public:
virtual ~OutgoingFileTransfer();
diff --git a/Swiften/FileTransfer/OutgoingFileTransferManager.cpp b/Swiften/FileTransfer/OutgoingFileTransferManager.cpp
new file mode 100644
index 0000000..321a57a
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransferManager.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "OutgoingFileTransferManager.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/Jingle/JingleSessionImpl.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
+#include <Swiften/Base/IDGenerator.h>
+
+namespace Swift {
+
+OutgoingFileTransferManager::OutgoingFileTransferManager(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy) : ownJID(ownFullJID), jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy) {
+ idGenerator = new IDGenerator();
+}
+
+OutgoingFileTransferManager::~OutgoingFileTransferManager() {
+ delete idGenerator;
+}
+
+boost::shared_ptr<OutgoingFileTransfer> OutgoingFileTransferManager::createOutgoingFileTransfer(const JID& receipient, boost::shared_ptr<ReadBytestream> readBytestream, const StreamInitiationFileInfo& fileInfo) {
+ // check if receipient support Jingle FT
+
+
+ JingleSessionImpl::ref jingleSession = boost::make_shared<JingleSessionImpl>(ownJID, receipient, idGenerator->generateID(), iqRouter);
+
+ //jsManager->getSession(receipient, idGenerator->generateID());
+ assert(jingleSession);
+ jsManager->registerOutgoingSession(ownJID, jingleSession);
+ boost::shared_ptr<OutgoingJingleFileTransfer> jingleFT = boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, receipient, readBytestream, fileInfo, bytestreamRegistry, bytestreamProxy));
+
+ // otherwise try SI
+
+ // else fail
+
+ return jingleFT;
+}
+
+}
diff --git a/Swiften/FileTransfer/OutgoingFileTransferManager.h b/Swiften/FileTransfer/OutgoingFileTransferManager.h
new file mode 100644
index 0000000..d57c14d
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransferManager.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class JingleSessionManager;
+class IQRouter;
+class EntityCapsProvider;
+class RemoteJingleTransportCandidateSelectorFactory;
+class LocalJingleTransportCandidateGeneratorFactory;
+class OutgoingFileTransfer;
+class JID;
+class IDGenerator;
+class ReadBytestream;
+class StreamInitiationFileInfo;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class OutgoingFileTransferManager {
+public:
+ OutgoingFileTransferManager(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy);
+ ~OutgoingFileTransferManager();
+
+ boost::shared_ptr<OutgoingFileTransfer> createOutgoingFileTransfer(const JID&, boost::shared_ptr<ReadBytestream>, const StreamInitiationFileInfo&);
+
+private:
+ JID ownJID;
+ JingleSessionManager* jsManager;
+ IQRouter* iqRouter;
+ EntityCapsProvider* capsProvider;
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+ LocalJingleTransportCandidateGeneratorFactory* localFactory;
+ IDGenerator *idGenerator;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+};
+
+}
diff --git a/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp
new file mode 100644
index 0000000..9b71165
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "OutgoingJingleFileTransfer.h"
+
+#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/JingleTransportPayload.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/FileTransfer/IBBSendSession.h>
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+OutgoingJingleFileTransfer::OutgoingJingleFileTransfer(JingleSession::ref session,
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory,
+ LocalJingleTransportCandidateGeneratorFactory* localFactory,
+ IQRouter* router,
+ IDGenerator *idGenerator,
+ const JID& toJID,
+ boost::shared_ptr<ReadBytestream> readStream,
+ const StreamInitiationFileInfo &fileInfo,
+ SOCKS5BytestreamRegistry* bytestreamRegistry,
+ SOCKS5BytestreamProxy* bytestreamProxy) :
+ session(session), remoteFactory(remoteFactory), localFactory(localFactory), router(router), idGenerator(idGenerator), toJID(toJID), readStream(readStream), fileInfo(fileInfo), s5bRegistry(bytestreamRegistry), s5bProxy(bytestreamProxy), serverSession(NULL), contentID(JingleContentID(idGenerator->generateID(), JingleContentPayload::InitiatorCreator)), canceled(false) {
+ session->onSessionAcceptReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleSessionAcceptReceived, this, _1, _2, _3));
+ session->onSessionTerminateReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleSessionTerminateReceived, this, _1));
+ session->onTransportInfoReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
+ session->onTransportAcceptReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransportAcceptReceived, this, _1, _2));
+ fileSizeInBytes = fileInfo.getSize();
+ filename = fileInfo.getName();
+
+ localCandidateGenerator = localFactory->createCandidateGenerator();
+ localCandidateGenerator->onLocalTransportCandidatesGenerated.connect(boost::bind(&OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1));
+
+ remoteCandidateSelector = remoteFactory->createCandidateSelector();
+ remoteCandidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
+ // calculate both, MD5 and SHA-1 since we don't know which one the other side supports
+ hashCalculator = new IncrementalBytestreamHashCalculator(true, true);
+ this->readStream->onRead.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+}
+
+OutgoingJingleFileTransfer::~OutgoingJingleFileTransfer() {
+ readStream->onRead.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+ delete hashCalculator;
+}
+
+void OutgoingJingleFileTransfer::start() {
+ onStateChange(FileTransfer::State(FileTransfer::State::WaitingForStart));
+
+ s5bSessionID = s5bRegistry->generateSessionID();
+ SWIFT_LOG(debug) << "S5B SessionID: " << s5bSessionID << std::endl;
+
+ //s5bProxy->connectToProxies(s5bSessionID);
+
+ JingleS5BTransportPayload::ref transport = boost::make_shared<JingleS5BTransportPayload>();
+ localCandidateGenerator->generateLocalTransportCandidates(transport);
+}
+
+void OutgoingJingleFileTransfer::stop() {
+
+}
+
+void OutgoingJingleFileTransfer::cancel() {
+ canceled = true;
+ session->sendTerminate(JinglePayload::Reason::Cancel);
+
+ if (ibbSession) {
+ ibbSession->stop();
+ }
+ SOCKS5BytestreamServerSession *serverSession = s5bRegistry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+ if (serverSession) {
+ serverSession->stop();
+ }
+ if (clientSession) {
+ clientSession->stop();
+ }
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+}
+
+void OutgoingJingleFileTransfer::handleSessionAcceptReceived(const JingleContentID& id, JingleDescription::ref /* decription */, JingleTransportPayload::ref transportPayload) {
+ if (canceled) {
+ return;
+ }
+ onStateChange(FileTransfer::State(FileTransfer::State::Negotiating));
+
+ JingleIBBTransportPayload::ref ibbPayload;
+ JingleS5BTransportPayload::ref s5bPayload;
+ if ((ibbPayload = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transportPayload))) {
+ ibbSession = boost::make_shared<IBBSendSession>(ibbPayload->getSessionID(), toJID, readStream, router);
+ ibbSession->setBlockSize(ibbPayload->getBlockSize());
+ ibbSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ ibbSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ ibbSession->start();
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ else if ((s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload))) {
+ fillCandidateMap(theirCandidates, s5bPayload);
+ remoteCandidateSelector->setRequesterTargtet(toJID, session->getInitiator());
+ remoteCandidateSelector->addRemoteTransportCandidates(s5bPayload);
+ remoteCandidateSelector->selectCandidate();
+ }
+ else {
+ // TODO: error handling
+ SWIFT_LOG(debug) << "Unknown transport payload! Try replaceing with IBB." << std::endl;
+ replaceTransportWithIBB(id);
+ }
+}
+
+void OutgoingJingleFileTransfer::handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason) {
+ if (canceled) {
+ return;
+ }
+
+ if (ibbSession) {
+ ibbSession->stop();
+ }
+ if (clientSession) {
+ clientSession->stop();
+ }
+ if (serverSession) {
+ serverSession->stop();
+ }
+
+ if (reason.is_initialized() && reason.get().type == JinglePayload::Reason::Cancel) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+ onFinished(FileTransferError(FileTransferError::PeerError));
+ } else if (reason.is_initialized() && reason.get().type == JinglePayload::Reason::Success) {
+ onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+ onFinished(boost::optional<FileTransferError>());
+ } else {
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ onFinished(FileTransferError(FileTransferError::PeerError));
+ }
+ canceled = true;
+}
+
+void OutgoingJingleFileTransfer::handleTransportAcceptReceived(const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) {
+ if (canceled) {
+ return;
+ }
+
+ if (JingleIBBTransportPayload::ref ibbPayload = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) {
+ ibbSession = boost::make_shared<IBBSendSession>(ibbPayload->getSessionID(), toJID, readStream, router);
+ ibbSession->setBlockSize(ibbPayload->getBlockSize());
+ ibbSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ ibbSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ ibbSession->start();
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ } else {
+ // error handling
+ SWIFT_LOG(debug) << "Replacing with anything other than IBB isn't supported yet." << std::endl;
+ session->sendTerminate(JinglePayload::Reason::FailedTransport);
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ }
+}
+
+void OutgoingJingleFileTransfer::startTransferViaOurCandidateChoice(JingleS5BTransportPayload::Candidate candidate) {
+ SWIFT_LOG(debug) << "Transferring data using our candidate." << std::endl;
+ if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ // get proxy client session from remoteCandidateSelector
+ clientSession = remoteCandidateSelector->getS5BSession();
+
+ // wait on <activated/> transport-info
+ } else {
+ clientSession = remoteCandidateSelector->getS5BSession();
+ clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startSending(readStream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+ assert(clientSession);
+}
+
+void OutgoingJingleFileTransfer::startTransferViaTheirCandidateChoice(JingleS5BTransportPayload::Candidate candidate) {
+ SWIFT_LOG(debug) << "Transferring data using their candidate." << std::endl;
+ if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ // connect to proxy
+ clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+ clientSession->onSessionReady.connect(boost::bind(&OutgoingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1));
+ clientSession->start();
+
+ // on reply send activate
+
+ } else {
+ serverSession = s5bRegistry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+ serverSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ serverSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ serverSession->startTransfer();
+ }
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+}
+
+// decide on candidates according to http://xmpp.org/extensions/xep-0260.html#complete
+void OutgoingJingleFileTransfer::decideOnCandidates() {
+ if (ourCandidateChoice && theirCandidateChoice) {
+ std::string our_cid = ourCandidateChoice->getCandidateUsed();
+ std::string their_cid = theirCandidateChoice->getCandidateUsed();
+ if (ourCandidateChoice->hasCandidateError() && theirCandidateChoice->hasCandidateError()) {
+ replaceTransportWithIBB(contentID);
+ }
+ else if (!our_cid.empty() && theirCandidateChoice->hasCandidateError()) {
+ // use our candidate
+ startTransferViaOurCandidateChoice(theirCandidates[our_cid]);
+ }
+ else if (!their_cid.empty() && ourCandidateChoice->hasCandidateError()) {
+ // use their candidate
+ startTransferViaTheirCandidateChoice(ourCandidates[their_cid]);
+ }
+ else if (!our_cid.empty() && !their_cid.empty()) {
+ // compare priorites, if same we win
+ if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) {
+ SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl;
+ session->sendTerminate(JinglePayload::Reason::FailedTransport);
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ onFinished(FileTransferError(FileTransferError::PeerError));
+ return;
+ }
+
+ JingleS5BTransportPayload::Candidate ourCandidate = theirCandidates[our_cid];
+ JingleS5BTransportPayload::Candidate theirCandidate = ourCandidates[their_cid];
+ if (ourCandidate.priority > theirCandidate.priority) {
+ startTransferViaOurCandidateChoice(ourCandidate);
+ }
+ else if (ourCandidate.priority < theirCandidate.priority) {
+ startTransferViaTheirCandidateChoice(theirCandidate);
+ }
+ else {
+ startTransferViaOurCandidateChoice(ourCandidate);
+ }
+ }
+ } else {
+ SWIFT_LOG(debug) << "Can't make a decision yet!" << std::endl;
+ }
+}
+
+void OutgoingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) {
+ map.clear();
+ foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) {
+ map[candidate.cid] = candidate;
+ }
+}
+
+void OutgoingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) {
+ if (error) {
+ // indicate proxy error
+ } else {
+ // activate proxy
+ activateProxySession(proxy);
+ }
+}
+
+void OutgoingJingleFileTransfer::activateProxySession(const JID& proxy) {
+ S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+ proxyRequest->setSID(s5bSessionID);
+ proxyRequest->setActivate(toJID);
+
+ boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Set, proxy, proxyRequest, router);
+ request->onResponse.connect(boost::bind(&OutgoingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2));
+ request->send();
+}
+
+void OutgoingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> /*request*/, ErrorPayload::ref error) {
+ if (error) {
+ SWIFT_LOG(debug) << "ERROR" << std::endl;
+ } else {
+ // send activated to other jingle party
+ JingleS5BTransportPayload::ref proxyActivate = boost::make_shared<JingleS5BTransportPayload>();
+ proxyActivate->setActivated(theirCandidateChoice->getCandidateUsed());
+ session->sendTransportInfo(contentID, proxyActivate);
+
+ // start transferring
+ clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startSending(readStream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ }
+}
+
+void OutgoingJingleFileTransfer::sendSessionInfoHash() {
+ SWIFT_LOG(debug) << std::endl;
+ JingleFileTransferHash::ref hashElement = boost::make_shared<JingleFileTransferHash>();
+ hashElement->setHash("sha-1", hashCalculator->getSHA1String());
+ hashElement->setHash("md5", hashCalculator->getMD5String());
+ session->sendInfo(hashElement);
+}
+
+void OutgoingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) {
+ if (canceled) {
+ return;
+ }
+ if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+ if (s5bPayload->hasCandidateError() || !s5bPayload->getCandidateUsed().empty()) {
+ theirCandidateChoice = s5bPayload;
+ decideOnCandidates();
+ } else if(!s5bPayload->getActivated().empty()) {
+ if (ourCandidateChoice->getCandidateUsed() == s5bPayload->getActivated()) {
+ clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+ clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+ clientSession->startSending(readStream);
+ onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ } else {
+ SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl;
+ JingleS5BTransportPayload::ref proxyError = boost::make_shared<JingleS5BTransportPayload>();
+ proxyError->setProxyError(true);
+ proxyError->setSessionID(s5bSessionID);
+ session->sendTransportInfo(contentID, proxyError);
+ }
+ }
+ }
+}
+
+void OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+ if (canceled) {
+ return;
+ }
+ JingleFileTransferDescription::ref description = boost::make_shared<JingleFileTransferDescription>();
+ description->addOffer(fileInfo);
+
+ JingleTransportPayload::ref transport;
+ if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(payload)) {
+ ibbTransport->setBlockSize(4096);
+ ibbTransport->setSessionID(idGenerator->generateID());
+ transport = ibbTransport;
+ }
+ else if (JingleS5BTransportPayload::ref s5bTransport = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(payload)) {
+ //fillCandidateMap(ourCandidates, s5bTransport);
+ //s5bTransport->setSessionID(s5bSessionID);
+
+ JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared<JingleS5BTransportPayload>();
+ emptyCandidates->setSessionID(s5bTransport->getSessionID());
+ fillCandidateMap(ourCandidates, emptyCandidates);
+
+ transport = emptyCandidates;
+ s5bRegistry->addReadBytestream(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID), readStream);
+ }
+ else {
+ SWIFT_LOG(debug) << "Unknown tranport payload: " << typeid(*payload).name() << std::endl;
+ return;
+ }
+ session->sendInitiate(contentID, description, transport);
+ onStateChange(FileTransfer::State(FileTransfer::State::WaitingForAccept));
+}
+
+void OutgoingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref payload) {
+ if (canceled) {
+ return;
+ }
+ if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(payload)) {
+ ourCandidateChoice = s5bPayload;
+ session->sendTransportInfo(contentID, s5bPayload);
+ decideOnCandidates();
+ }
+}
+
+void OutgoingJingleFileTransfer::replaceTransportWithIBB(const JingleContentID& id) {
+ SWIFT_LOG(debug) << "Both parties failed. Replace transport with IBB." << std::endl;
+ JingleIBBTransportPayload::ref ibbTransport = boost::make_shared<JingleIBBTransportPayload>();
+ ibbTransport->setBlockSize(4096);
+ ibbTransport->setSessionID(idGenerator->generateID());
+ session->sendTransportReplace(id, ibbTransport);
+}
+
+void OutgoingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) {
+ if (error) {
+ session->sendTerminate(JinglePayload::Reason::ConnectivityError);
+ onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+ onFinished(error);
+ } else {
+ sendSessionInfoHash();
+ /*
+ session->terminate(JinglePayload::Reason::Success);
+ onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+ */
+ }
+ //
+}
+
+}
diff --git a/Swiften/FileTransfer/OutgoingJingleFileTransfer.h b/Swiften/FileTransfer/OutgoingJingleFileTransfer.h
new file mode 100644
index 0000000..fecfbdb
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Elements/ErrorPayload.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/Jingle/JingleSession.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+namespace Swift {
+
+class RemoteJingleTransportCandidateSelectorFactory;
+class RemoteJingleTransportCandidateSelector;
+class LocalJingleTransportCandidateGeneratorFactory;
+class LocalJingleTransportCandidateGenerator;
+class IQRouter;
+class ReadBytestream;
+class IBBSendSession;
+class IDGenerator;
+class IncrementalBytestreamHashCalculator;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class OutgoingJingleFileTransfer : public OutgoingFileTransfer {
+public:
+ OutgoingJingleFileTransfer(JingleSession::ref,
+ RemoteJingleTransportCandidateSelectorFactory*,
+ LocalJingleTransportCandidateGeneratorFactory*,
+ IQRouter*,
+ IDGenerator*,
+ const JID&,
+ boost::shared_ptr<ReadBytestream>,
+ const StreamInitiationFileInfo&,
+ SOCKS5BytestreamRegistry*,
+ SOCKS5BytestreamProxy*);
+ virtual ~OutgoingJingleFileTransfer();
+
+ void start();
+ void stop();
+
+ void cancel();
+
+private:
+ void handleSessionAcceptReceived(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
+ void handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason);
+ void handleTransportAcceptReceived(const JingleContentID&, JingleTransportPayload::ref);
+ void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref);
+
+ void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref);
+ void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref);
+
+private:
+ void replaceTransportWithIBB(const JingleContentID&);
+ void handleTransferFinished(boost::optional<FileTransferError>);
+ void activateProxySession(const JID &proxy);
+ void handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error);
+ void proxySessionReady(const JID& proxy, bool error);
+
+private:
+ typedef std::map<std::string, JingleS5BTransportPayload::Candidate> CandidateMap;
+
+private:
+ void startTransferViaOurCandidateChoice(JingleS5BTransportPayload::Candidate);
+ void startTransferViaTheirCandidateChoice(JingleS5BTransportPayload::Candidate);
+ void decideOnCandidates();
+ void fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload);
+
+private:
+ void sendSessionInfoHash();
+
+private:
+ JingleSession::ref session;
+ RemoteJingleTransportCandidateSelector* remoteCandidateSelector;
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+ LocalJingleTransportCandidateGenerator* localCandidateGenerator;
+ LocalJingleTransportCandidateGeneratorFactory* localFactory;
+
+ IQRouter* router;
+ IDGenerator* idGenerator;
+ JID toJID;
+ boost::shared_ptr<ReadBytestream> readStream;
+ StreamInitiationFileInfo fileInfo;
+ IncrementalBytestreamHashCalculator *hashCalculator;
+
+ boost::shared_ptr<IBBSendSession> ibbSession;
+
+ JingleS5BTransportPayload::ref ourCandidateChoice;
+ JingleS5BTransportPayload::ref theirCandidateChoice;
+ CandidateMap ourCandidates;
+ CandidateMap theirCandidates;
+
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
+ SOCKS5BytestreamClientSession::ref clientSession;
+ SOCKS5BytestreamServerSession* serverSession;
+ JingleContentID contentID;
+ std::string s5bSessionID;
+
+ bool canceled;
+};
+
+}
diff --git a/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
index 85ac505..dfcf028 100644
--- a/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
+++ b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
@@ -38,7 +38,7 @@ void OutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiat
}
else {
if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
- socksServer->addBytestream(id, from, to, bytestream);
+ socksServer->addReadBytestream(id, from, to, bytestream);
Bytestreams::ref bytestreams(new Bytestreams());
bytestreams->setStreamID(id);
HostAddressPort addressPort = socksServer->getAddressPort();
@@ -67,7 +67,7 @@ void OutgoingSIFileTransfer::finish(boost::optional<FileTransferError> error) {
ibbSession->onFinished.disconnect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
ibbSession.reset();
}
- socksServer->removeBytestream(id, from, to);
+ socksServer->removeReadBytestream(id, from, to);
onFinished(error);
}
diff --git a/Swiften/FileTransfer/ReadBytestream.h b/Swiften/FileTransfer/ReadBytestream.h
index 2601192..9e070f7 100644
--- a/Swiften/FileTransfer/ReadBytestream.h
+++ b/Swiften/FileTransfer/ReadBytestream.h
@@ -9,11 +9,16 @@
#include <vector>
#include <cstring>
+#include <Swiften/Base/boost_bsignals.h>
+
namespace Swift {
class ReadBytestream {
public:
virtual ~ReadBytestream();
virtual std::vector<unsigned char> read(size_t size) = 0;
virtual bool isFinished() const = 0;
+
+ public:
+ boost::signal<void (std::vector<unsigned char>)> onRead;
};
}
diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
index b12b06b..f8df8f9 100644
--- a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
+++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
@@ -8,8 +8,10 @@
#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
#include <Swiften/Elements/JingleTransportPayload.h>
#include <Swiften/FileTransfer/JingleTransport.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
namespace Swift {
class RemoteJingleTransportCandidateSelector {
@@ -19,6 +21,8 @@ namespace Swift {
virtual void addRemoteTransportCandidates(JingleTransportPayload::ref) = 0;
virtual void selectCandidate() = 0;
virtual void setMinimumPriority(int) = 0;
+ virtual void setRequesterTargtet(const JID&, const JID&) {}
+ virtual SOCKS5BytestreamClientSession::ref getS5BSession() { return SOCKS5BytestreamClientSession::ref(); }
virtual bool isActualCandidate(JingleTransportPayload::ref) = 0;
virtual int getPriority(JingleTransportPayload::ref) = 0;
diff --git a/Swiften/FileTransfer/SConscript b/Swiften/FileTransfer/SConscript
index 24fc9e8..ef02557 100644
--- a/Swiften/FileTransfer/SConscript
+++ b/Swiften/FileTransfer/SConscript
@@ -3,6 +3,9 @@ Import("swiften_env", "env")
sources = [
"OutgoingFileTransfer.cpp",
"OutgoingSIFileTransfer.cpp",
+ "OutgoingJingleFileTransfer.cpp",
+ "OutgoingFileTransferManager.cpp",
+ "OutgoingFileTransferManager.cpp",
"IncomingFileTransfer.cpp",
"IncomingJingleFileTransfer.cpp",
"IncomingFileTransferManager.cpp",
@@ -10,23 +13,36 @@ sources = [
"RemoteJingleTransportCandidateSelectorFactory.cpp",
"LocalJingleTransportCandidateGenerator.cpp",
"LocalJingleTransportCandidateGeneratorFactory.cpp",
+ "DefaultRemoteJingleTransportCandidateSelectorFactory.cpp",
+ "DefaultLocalJingleTransportCandidateGeneratorFactory.cpp",
+ "DefaultRemoteJingleTransportCandidateSelector.cpp",
+ "DefaultLocalJingleTransportCandidateGenerator.cpp",
"JingleTransport.cpp",
"JingleIncomingIBBTransport.cpp",
"ReadBytestream.cpp",
"WriteBytestream.cpp",
"FileReadBytestream.cpp",
"FileWriteBytestream.cpp",
+ "SOCKS5BytestreamClientSession.cpp",
"SOCKS5BytestreamServer.cpp",
"SOCKS5BytestreamServerSession.cpp",
"SOCKS5BytestreamRegistry.cpp",
+ "SOCKS5BytestreamProxy.cpp",
"IBBSendSession.cpp",
"IBBReceiveSession.cpp",
+ "FileTransferManager.cpp",
+ "FileTransferManagerImpl.cpp",
+ "IncrementalBytestreamHashCalculator.cpp",
+ "ConnectivityManager.cpp",
]
swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources))
env.Append(UNITTEST_SOURCES = [
File("UnitTest/SOCKS5BytestreamServerSessionTest.cpp"),
+ File("UnitTest/SOCKS5BytestreamClientSessionTest.cpp"),
File("UnitTest/IBBSendSessionTest.cpp"),
File("UnitTest/IBBReceiveSessionTest.cpp"),
+ File("UnitTest/IncomingJingleFileTransferTest.cpp"),
+ File("UnitTest/OutgoingJingleFileTransferTest.cpp"),
])
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp
new file mode 100644
index 0000000..f90b73b
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "SOCKS5BytestreamClientSession.h"
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/SafeByteArray.h>
+#include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/StringCodecs/SHA1.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/FileTransfer/BytestreamException.h>
+#include <Swiften/Network/TimerFactory.h>
+
+namespace Swift {
+
+SOCKS5BytestreamClientSession::SOCKS5BytestreamClientSession(boost::shared_ptr<Connection> connection, const HostAddressPort& addressPort, const std::string& destination, TimerFactory* timerFactory) :
+ connection(connection), addressPort(addressPort), destination(destination), state(Initial), chunkSize(131072) {
+ connection->onConnectFinished.connect(boost::bind(&SOCKS5BytestreamClientSession::handleConnectFinished, this, _1));
+ connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamClientSession::handleDisconnected, this, _1));
+ weFailedTimeout = timerFactory->createTimer(2000);
+ weFailedTimeout->onTick.connect(boost::bind(&SOCKS5BytestreamClientSession::handleWeFailedTimeout, this));
+}
+
+void SOCKS5BytestreamClientSession::start() {
+ assert(state == Initial);
+ SWIFT_LOG(debug) << "Trying to connect via TCP to " << addressPort.toString() << "." << std::endl;
+ weFailedTimeout->start();
+ connection->connect(addressPort);
+}
+
+void SOCKS5BytestreamClientSession::stop() {
+ connection->disconnect();
+ connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+ connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+ readBytestream.reset();
+ state = Finished;
+}
+
+void SOCKS5BytestreamClientSession::process() {
+ SWIFT_LOG(debug) << "unprocessedData.size(): " << unprocessedData.size() << std::endl;
+ ByteArray bndAddress;
+ switch(state) {
+ case Initial:
+ hello();
+ break;
+ case Hello:
+ if (unprocessedData.size() > 1) {
+ unsigned char version = unprocessedData[0];
+ unsigned char authMethod = unprocessedData[1];
+ if (version != 5 || authMethod != 0) {
+ // signal failure to upper level
+ finish(true);
+ return;
+ }
+ unprocessedData.clear();
+ authenticate();
+ }
+ break;
+ case Authenticating:
+ if (unprocessedData.size() < 5) {
+ // need more data to start progressing
+ break;
+ }
+ if (unprocessedData[0] != '\x05') {
+ // wrong version
+ // disconnect & signal failure
+ finish(true);
+ break;
+ }
+ if (unprocessedData[1] != '\x00') {
+ // no success
+ // disconnect & signal failure
+ finish(true);
+ break;
+ }
+ if (unprocessedData[3] != '\x03') {
+ // we expect x'03' = DOMAINNAME here
+ // discconect & signal failure
+ finish(true);
+ break;
+ }
+ if (static_cast<size_t>(unprocessedData[4]) + 1 > unprocessedData.size() + 5) {
+ // complete domainname and port not available yet
+ break;
+ }
+ bndAddress = createByteArray(&(unprocessedData.data()[5]), unprocessedData[4]);
+ if (unprocessedData[unprocessedData[4] + 5] != 0 && bndAddress == createByteArray(destination)) {
+ // we expect a 0 as port
+ // disconnect and fail
+ finish(true);
+ }
+ unprocessedData.clear();
+ state = Ready;
+ SWIFT_LOG(debug) << "session ready" << std::endl;
+ // issue ready signal so the bytestream can be used for reading or writing
+ weFailedTimeout->stop();
+ onSessionReady(false);
+ break;
+ case Ready:
+ SWIFT_LOG(debug) << "Received further data in Ready state." << std::endl;
+ break;
+ case Reading:
+ case Writing:
+ case Finished:
+ SWIFT_LOG(debug) << "Unexpected receive of data. Current state: " << state << std::endl;
+ SWIFT_LOG(debug) << "Data: " << Hexify::hexify(unprocessedData) << std::endl;
+ unprocessedData.clear();
+ //assert(false);
+ }
+}
+
+void SOCKS5BytestreamClientSession::hello() {
+ // Version 5, 1 auth method, No authentication
+ const SafeByteArray hello = createSafeByteArray("\x05\x01\x00", 3);
+ connection->write(hello);
+ state = Hello;
+}
+
+void SOCKS5BytestreamClientSession::authenticate() {
+ SWIFT_LOG(debug) << std::endl;
+ SafeByteArray header = createSafeByteArray("\x05\x01\x00\x03", 4);
+ SafeByteArray message = header;
+ append(message, createSafeByteArray(destination.size()));
+ authenticateAddress = createByteArray(destination);
+ append(message, authenticateAddress);
+ append(message, createSafeByteArray("\x00\x00", 2)); // 2 byte for port
+ connection->write(message);
+ state = Authenticating;
+}
+
+void SOCKS5BytestreamClientSession::startReceiving(boost::shared_ptr<WriteBytestream> writeStream) {
+ if (state == Ready) {
+ state = Reading;
+ writeBytestream = writeStream;
+ writeBytestream->write(unprocessedData);
+ onBytesReceived(unprocessedData.size());
+ unprocessedData.clear();
+ } else {
+ SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl;
+ }
+}
+
+void SOCKS5BytestreamClientSession::startSending(boost::shared_ptr<ReadBytestream> readStream) {
+ if (state == Ready) {
+ state = Writing;
+ readBytestream = readStream;
+ connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+ sendData();
+ } else {
+ SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl;
+ }
+}
+
+HostAddressPort SOCKS5BytestreamClientSession::getAddressPort() const {
+ return addressPort;
+}
+
+void SOCKS5BytestreamClientSession::sendData() {
+ if (!readBytestream->isFinished()) {
+ try {
+ SafeByteArray dataToSend = createSafeByteArray(readBytestream->read(chunkSize));
+ connection->write(dataToSend);
+ onBytesSent(dataToSend.size());
+ }
+ catch (const BytestreamException&) {
+ finish(true);
+ }
+ }
+ else {
+ finish(false);
+ }
+}
+
+void SOCKS5BytestreamClientSession::finish(bool error) {
+ weFailedTimeout->stop();
+ connection->disconnect();
+ connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+ connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+ readBytestream.reset();
+ if (state == Initial || state == Hello || state == Authenticating) {
+ onSessionReady(true);
+ }
+ else {
+ state = Finished;
+ if (error) {
+ onFinished(boost::optional<FileTransferError>(FileTransferError::ReadError));
+ } else {
+ onFinished(boost::optional<FileTransferError>());
+ }
+ }
+}
+
+void SOCKS5BytestreamClientSession::handleConnectFinished(bool error) {
+ if (error) {
+ SWIFT_LOG(debug) << "Failed to connect via TCP to " << addressPort.toString() << "." << std::endl;
+ finish(true);
+ } else {
+ SWIFT_LOG(debug) << "Successfully connected via TCP" << addressPort.toString() << "." << std::endl;
+ weFailedTimeout->start();
+ connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+ process();
+ }
+}
+
+void SOCKS5BytestreamClientSession::handleDataRead(const SafeByteArray& data) {
+ SWIFT_LOG(debug) << "state: " << state << " data.size() = " << data.size() << std::endl;
+ if (state != Reading) {
+ append(unprocessedData, data);
+ process();
+ }
+ else {
+ writeBytestream->write(createByteArray(data.data(), data.size()));
+ onBytesReceived(data.size());
+ }
+}
+
+void SOCKS5BytestreamClientSession::handleDisconnected(const boost::optional<Connection::Error>& error) {
+ SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl;
+ if (error) {
+ finish(true);
+ }
+}
+
+void SOCKS5BytestreamClientSession::handleWeFailedTimeout() {
+ SWIFT_LOG(debug) << "Failed due to timeout!" << std::endl;
+ finish(true);
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h
new file mode 100644
index 0000000..894e977
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/Connection.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/Timer.h>
+
+namespace Swift {
+
+class SOCKS5BytestreamRegistry;
+class Connection;
+class TimerFactory;
+
+/**
+ * A session which has been connected to a SOCKS5 server (requester).
+ *
+ */
+class SOCKS5BytestreamClientSession {
+public:
+ enum State {
+ Initial,
+ Hello,
+ Authenticating,
+ Ready,
+ Writing,
+ Reading,
+ Finished,
+ };
+
+public:
+ typedef boost::shared_ptr<SOCKS5BytestreamClientSession> ref;
+
+public:
+ SOCKS5BytestreamClientSession(boost::shared_ptr<Connection> connection, const HostAddressPort&, const std::string&, TimerFactory*);
+
+ void start();
+ void stop();
+
+ void startReceiving(boost::shared_ptr<WriteBytestream>);
+ void startSending(boost::shared_ptr<ReadBytestream>);
+
+ HostAddressPort getAddressPort() const;
+
+ boost::signal<void (bool /*error*/)> onSessionReady;
+
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+ boost::signal<void (int)> onBytesSent;
+ boost::signal<void (int)> onBytesReceived;
+
+private:
+ void process();
+ void hello();
+ void authenticate();
+
+ void handleConnectFinished(bool error);
+ void handleDataRead(const SafeByteArray&);
+ void handleDisconnected(const boost::optional<Connection::Error>&);
+ void handleWeFailedTimeout();
+
+ void finish(bool error);
+ void sendData();
+
+private:
+ boost::shared_ptr<Connection> connection;
+ HostAddressPort addressPort;
+ std::string destination; // hexify(SHA1(sessionID + requester + target))
+
+ State state;
+ int destinationPort;
+
+ ByteArray unprocessedData;
+ ByteArray authenticateAddress;
+
+ int chunkSize;
+ boost::shared_ptr<WriteBytestream> writeBytestream;
+ boost::shared_ptr<ReadBytestream> readBytestream;
+
+ Timer::ref weFailedTimeout;
+};
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp b/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp
new file mode 100644
index 0000000..9599fd1
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "SOCKS5BytestreamProxy.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+SOCKS5BytestreamProxy::SOCKS5BytestreamProxy(ConnectionFactory *connFactory, TimerFactory *timeFactory) : connectionFactory(connFactory), timerFactory(timeFactory) {
+
+}
+
+void SOCKS5BytestreamProxy::addS5BProxy(S5BProxyRequest::ref proxy) {
+ localS5BProxies.push_back(proxy);
+}
+
+const std::vector<S5BProxyRequest::ref>& SOCKS5BytestreamProxy::getS5BProxies() const {
+ return localS5BProxies;
+}
+
+void SOCKS5BytestreamProxy::connectToProxies(const std::string& sessionID) {
+ SWIFT_LOG(debug) << "session ID: " << sessionID << std::endl;
+ ProxyJIDClientSessionMap clientSessions;
+
+ foreach(S5BProxyRequest::ref proxy, localS5BProxies) {
+ boost::shared_ptr<Connection> conn = connectionFactory->createConnection();
+
+ boost::shared_ptr<SOCKS5BytestreamClientSession> session = boost::make_shared<SOCKS5BytestreamClientSession>(conn, proxy->getStreamHost().get().addressPort, sessionID, timerFactory);
+ clientSessions[proxy->getStreamHost().get().jid] = session;
+ session->start();
+ }
+
+ proxySessions[sessionID] = clientSessions;
+}
+
+boost::shared_ptr<SOCKS5BytestreamClientSession> SOCKS5BytestreamProxy::getProxySessionAndCloseOthers(const JID& proxyJID, const std::string& sessionID) {
+ // checking parameters
+ if (proxySessions.find(sessionID) == proxySessions.end()) {
+ return boost::shared_ptr<SOCKS5BytestreamClientSession>();
+ }
+ if (proxySessions[sessionID].find(proxyJID) == proxySessions[sessionID].end()) {
+ return boost::shared_ptr<SOCKS5BytestreamClientSession>();
+ }
+
+ // get active session
+ boost::shared_ptr<SOCKS5BytestreamClientSession> activeSession = proxySessions[sessionID][proxyJID];
+ proxySessions[sessionID].erase(proxyJID);
+
+ // close other sessions
+ foreach(const ProxyJIDClientSessionMap::value_type& myPair, proxySessions[sessionID]) {
+ myPair.second->stop();
+ }
+
+ proxySessions.erase(sessionID);
+
+ return activeSession;
+}
+
+boost::shared_ptr<SOCKS5BytestreamClientSession> SOCKS5BytestreamProxy::createSOCKS5BytestreamClientSession(HostAddressPort addressPort, const std::string& destAddr) {
+ SOCKS5BytestreamClientSession::ref connection = boost::make_shared<SOCKS5BytestreamClientSession>(connectionFactory->createConnection(), addressPort, destAddr, timerFactory);
+ return connection;
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxy.h b/Swiften/FileTransfer/SOCKS5BytestreamProxy.h
new file mode 100644
index 0000000..8f80cea
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamProxy.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/TimerFactory.h>
+
+namespace Swift {
+
+/**
+ * - manages list of working S5B proxies
+ * - creates initial connections (for the candidates you provide)
+ */
+class SOCKS5BytestreamProxy {
+public:
+ SOCKS5BytestreamProxy(ConnectionFactory*, TimerFactory*);
+
+ void addS5BProxy(S5BProxyRequest::ref);
+ const std::vector<S5BProxyRequest::ref>& getS5BProxies() const;
+
+ void connectToProxies(const std::string& sessionID);
+ boost::shared_ptr<SOCKS5BytestreamClientSession> getProxySessionAndCloseOthers(const JID& proxyJID, const std::string& sessionID);
+
+ boost::shared_ptr<SOCKS5BytestreamClientSession> createSOCKS5BytestreamClientSession(HostAddressPort addressPort, const std::string& destAddr);
+
+private:
+ ConnectionFactory* connectionFactory;
+ TimerFactory* timerFactory;
+
+ typedef std::map<JID, boost::shared_ptr<SOCKS5BytestreamClientSession> > ProxyJIDClientSessionMap;
+ std::map<std::string, ProxyJIDClientSessionMap> proxySessions;
+
+ std::vector<S5BProxyRequest::ref> localS5BProxies;
+};
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
index 429c7f2..ffc4298 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
@@ -6,27 +6,67 @@
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/StringCodecs/SHA1.h>
+#include <Swiften/StringCodecs/Hexify.h>
+
namespace Swift {
SOCKS5BytestreamRegistry::SOCKS5BytestreamRegistry() {
}
-void SOCKS5BytestreamRegistry::addBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream) {
- byteStreams[destination] = byteStream;
+void SOCKS5BytestreamRegistry::addReadBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream) {
+ readBytestreams[destination] = byteStream;
}
-void SOCKS5BytestreamRegistry::removeBytestream(const std::string& destination) {
- byteStreams.erase(destination);
+void SOCKS5BytestreamRegistry::removeReadBytestream(const std::string& destination) {
+ readBytestreams.erase(destination);
}
-boost::shared_ptr<ReadBytestream> SOCKS5BytestreamRegistry::getBytestream(const std::string& destination) const {
- BytestreamMap::const_iterator i = byteStreams.find(destination);
- if (i != byteStreams.end()) {
+boost::shared_ptr<ReadBytestream> SOCKS5BytestreamRegistry::getReadBytestream(const std::string& destination) const {
+ ReadBytestreamMap::const_iterator i = readBytestreams.find(destination);
+ if (i != readBytestreams.end()) {
return i->second;
}
return boost::shared_ptr<ReadBytestream>();
}
+void SOCKS5BytestreamRegistry::addWriteBytestream(const std::string& destination, boost::shared_ptr<WriteBytestream> byteStream) {
+ writeBytestreams[destination] = byteStream;
+}
+
+void SOCKS5BytestreamRegistry::removeWriteBytestream(const std::string& destination) {
+ writeBytestreams.erase(destination);
+}
+
+boost::shared_ptr<WriteBytestream> SOCKS5BytestreamRegistry::getWriteBytestream(const std::string& destination) const {
+ WriteBytestreamMap::const_iterator i = writeBytestreams.find(destination);
+ if (i != writeBytestreams.end()) {
+ return i->second;
+ }
+ return boost::shared_ptr<WriteBytestream>();
+}
+
+std::string SOCKS5BytestreamRegistry::generateSessionID() {
+ return idGenerator.generateID();
+}
+
+SOCKS5BytestreamServerSession* SOCKS5BytestreamRegistry::getConnectedSession(const std::string& destination) {
+ if (serverSessions.find(destination) != serverSessions.end()) {
+ return serverSessions[destination];
+ } else {
+ SWIFT_LOG(debug) << "No active connction for stream ID " << destination << " found!" << std::endl;
+ return NULL;
+ }
+}
+
+std::string SOCKS5BytestreamRegistry::getHostname(const std::string& sessionID, const JID& requester, const JID& target) {
+ return Hexify::hexify(SHA1::getHash(createSafeByteArray(sessionID + requester.toString() + target.toString())));
+}
}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
index 955b900..779aabb 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
@@ -7,23 +7,58 @@
#pragma once
#include <boost/shared_ptr.hpp>
-#include <map>
+#include <map>
#include <string>
+#include <vector>
+#include <set>
+
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/Network/HostAddressPort.h>
namespace Swift {
class SOCKS5BytestreamRegistry {
public:
SOCKS5BytestreamRegistry();
- boost::shared_ptr<ReadBytestream> getBytestream(const std::string& destination) const;
- void addBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream);
- void removeBytestream(const std::string& destination);
+ boost::shared_ptr<ReadBytestream> getReadBytestream(const std::string& destination) const;
+ void addReadBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream);
+ void removeReadBytestream(const std::string& destination);
+
+ boost::shared_ptr<WriteBytestream> getWriteBytestream(const std::string& destination) const;
+ void addWriteBytestream(const std::string& destination, boost::shared_ptr<WriteBytestream> byteStream);
+ void removeWriteBytestream(const std::string& destination);
+
+ /**
+ * Generate a new session ID to use for new S5B streams.
+ */
+ std::string generateSessionID();
+
+ /**
+ * Start an actual transfer.
+ */
+ SOCKS5BytestreamServerSession* getConnectedSession(const std::string& destination);
+
+ public:
+ static std::string getHostname(const std::string& sessionID, const JID& requester, const JID& target);
private:
- typedef std::map<std::string, boost::shared_ptr<ReadBytestream> > BytestreamMap;
- BytestreamMap byteStreams;
+ friend class SOCKS5BytestreamServerSession;
+
+ typedef std::map<std::string, boost::shared_ptr<ReadBytestream> > ReadBytestreamMap;
+ ReadBytestreamMap readBytestreams;
+
+ typedef std::map<std::string, boost::shared_ptr<WriteBytestream> > WriteBytestreamMap;
+ WriteBytestreamMap writeBytestreams;
+
+ std::map<std::string, SOCKS5BytestreamServerSession*> serverSessions;
+
+ IDGenerator idGenerator;
};
}
-
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
index 9731d2d..90fed7a 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
@@ -8,13 +8,15 @@
#include <boost/bind.hpp>
+#include <Swiften/Base/Log.h>
#include <Swiften/StringCodecs/Hexify.h>
#include <Swiften/StringCodecs/SHA1.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
namespace Swift {
-SOCKS5BytestreamServer::SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer) : connectionServer(connectionServer) {
+SOCKS5BytestreamServer::SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer, SOCKS5BytestreamRegistry* registry) : connectionServer(connectionServer), registry(registry) {
}
void SOCKS5BytestreamServer::start() {
@@ -25,12 +27,12 @@ void SOCKS5BytestreamServer::stop() {
connectionServer->onNewConnection.disconnect(boost::bind(&SOCKS5BytestreamServer::handleNewConnection, this, _1));
}
-void SOCKS5BytestreamServer::addBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream) {
- bytestreams.addBytestream(getSOCKSDestinationAddress(id, from, to), byteStream);
+void SOCKS5BytestreamServer::addReadBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream) {
+ registry->addReadBytestream(getSOCKSDestinationAddress(id, from, to), byteStream);
}
-void SOCKS5BytestreamServer::removeBytestream(const std::string& id, const JID& from, const JID& to) {
- bytestreams.removeBytestream(getSOCKSDestinationAddress(id, from, to));
+void SOCKS5BytestreamServer::removeReadBytestream(const std::string& id, const JID& from, const JID& to) {
+ registry->removeReadBytestream(getSOCKSDestinationAddress(id, from, to));
}
std::string SOCKS5BytestreamServer::getSOCKSDestinationAddress(const std::string& id, const JID& from, const JID& to) {
@@ -38,7 +40,7 @@ std::string SOCKS5BytestreamServer::getSOCKSDestinationAddress(const std::string
}
void SOCKS5BytestreamServer::handleNewConnection(boost::shared_ptr<Connection> connection) {
- boost::shared_ptr<SOCKS5BytestreamServerSession> session(new SOCKS5BytestreamServerSession(connection, &bytestreams));
+ boost::shared_ptr<SOCKS5BytestreamServerSession> session(new SOCKS5BytestreamServerSession(connection, registry));
sessions.push_back(session);
session->start();
}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServer.h b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
index 7fa709e..6bb598e 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServer.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
@@ -20,15 +20,15 @@ namespace Swift {
class SOCKS5BytestreamServer {
public:
- SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer);
+ SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer, SOCKS5BytestreamRegistry* registry);
HostAddressPort getAddressPort() const;
void start();
void stop();
- void addBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream);
- void removeBytestream(const std::string& id, const JID& from, const JID& to);
+ void addReadBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream);
+ void removeReadBytestream(const std::string& id, const JID& from, const JID& to);
/*protected:
boost::shared_ptr<ReadBytestream> getBytestream(const std::string& dest);*/
@@ -42,7 +42,7 @@ namespace Swift {
friend class SOCKS5BytestreamServerSession;
boost::shared_ptr<ConnectionServer> connectionServer;
- SOCKS5BytestreamRegistry bytestreams;
+ SOCKS5BytestreamRegistry* registry;
std::vector<boost::shared_ptr<SOCKS5BytestreamServerSession> > sessions;
};
}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
index 477af4b..6f33862 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
@@ -13,12 +13,14 @@
#include <Swiften/Base/SafeByteArray.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
#include <Swiften/FileTransfer/BytestreamException.h>
namespace Swift {
-SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(4096) {
+SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(131072) {
+ connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1));
}
SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() {
@@ -29,17 +31,55 @@ SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() {
}
void SOCKS5BytestreamServerSession::start() {
+ SWIFT_LOG(debug) << std::endl;
connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
state = WaitingForAuthentication;
}
void SOCKS5BytestreamServerSession::stop() {
- finish(false);
+ connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+ connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
+ connection->disconnect();
+ state = Finished;
+}
+
+void SOCKS5BytestreamServerSession::startTransfer() {
+ if (state == ReadyForTransfer) {
+ if (readBytestream) {
+ state = WritingData;
+ connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+ sendData();
+ }
+ else if(writeBytestream) {
+ state = ReadingData;
+ writeBytestream->write(unprocessedData);
+ onBytesReceived(unprocessedData.size());
+ unprocessedData.clear();
+ }
+ } else {
+ SWIFT_LOG(debug) << "Not ready for transfer!" << std::endl;
+ }
+}
+
+HostAddressPort SOCKS5BytestreamServerSession::getAddressPort() const {
+ return connection->getLocalAddress();
}
void SOCKS5BytestreamServerSession::handleDataRead(const SafeByteArray& data) {
- append(unprocessedData, data);
- process();
+ if (state != ReadingData) {
+ append(unprocessedData, data);
+ process();
+ } else {
+ writeBytestream->write(createByteArray(data.data(), data.size()));
+ onBytesReceived(data.size());
+ }
+}
+
+void SOCKS5BytestreamServerSession::handleDisconnected(const boost::optional<Connection::Error>& error) {
+ SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl;
+ if (error) {
+ finish(true);
+ }
}
void SOCKS5BytestreamServerSession::process() {
@@ -54,7 +94,7 @@ void SOCKS5BytestreamServerSession::process() {
if (i == 2 + authCount) {
// Authentication message is complete
if (i != unprocessedData.size()) {
- std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+ SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl;
}
unprocessedData.clear();
connection->write(createSafeByteArray("\x05\x00", 2));
@@ -71,26 +111,31 @@ void SOCKS5BytestreamServerSession::process() {
requestID.push_back(unprocessedData[i]);
++i;
}
- // Skip the port:
+ // Skip the port: 2 byte large, one already skipped. Add one for comparison with size
i += 2;
- if (i >= unprocessedData.size()) {
+ if (i <= unprocessedData.size()) {
if (i != unprocessedData.size()) {
- std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+ SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl;
}
- bytestream = bytestreams->getBytestream(byteArrayToString(requestID));
+ unprocessedData.clear();
+ std::string streamID = byteArrayToString(requestID);
+ readBytestream = bytestreams->getReadBytestream(streamID);
+ writeBytestream = bytestreams->getWriteBytestream(streamID);
SafeByteArray result = createSafeByteArray("\x05", 1);
- result.push_back(bytestream ? 0x0 : 0x4);
+ result.push_back((readBytestream || writeBytestream) ? 0x0 : 0x4);
append(result, createByteArray("\x00\x03", 2));
result.push_back(static_cast<char>(requestID.size()));
append(result, concat(requestID, createByteArray("\x00\x00", 2)));
- if (!bytestream) {
+ if (!readBytestream && !writeBytestream) {
+ SWIFT_LOG(debug) << "Readstream or Wrtiestream with ID " << streamID << " not found!" << std::endl;
connection->write(result);
finish(true);
}
else {
- state = SendingData;
- connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+ SWIFT_LOG(deubg) << "Found " << (readBytestream ? "Readstream" : "Writestream") << ". Sent OK." << std::endl;
connection->write(result);
+ bytestreams->serverSessions[streamID] = this;
+ state = ReadyForTransfer;
}
}
}
@@ -98,9 +143,11 @@ void SOCKS5BytestreamServerSession::process() {
}
void SOCKS5BytestreamServerSession::sendData() {
- if (!bytestream->isFinished()) {
+ if (!readBytestream->isFinished()) {
try {
- connection->write(createSafeByteArray(bytestream->read(chunkSize)));
+ SafeByteArray dataToSend = createSafeByteArray(readBytestream->read(chunkSize));
+ connection->write(dataToSend);
+ onBytesSent(dataToSend.size());
}
catch (const BytestreamException&) {
finish(true);
@@ -114,9 +161,14 @@ void SOCKS5BytestreamServerSession::sendData() {
void SOCKS5BytestreamServerSession::finish(bool error) {
connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
- bytestream.reset();
+ connection->onDisconnected.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1));
+ readBytestream.reset();
state = Finished;
- onFinished(error);
+ if (error) {
+ onFinished(boost::optional<FileTransferError>(FileTransferError::PeerError));
+ } else {
+ onFinished(boost::optional<FileTransferError>());
+ }
}
}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
index 761a365..3e1018f 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
@@ -11,17 +11,24 @@
#include <Swiften/Base/boost_bsignals.h>
#include <Swiften/Network/Connection.h>
#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
namespace Swift {
class SOCKS5BytestreamRegistry;
class SOCKS5BytestreamServerSession {
public:
+ typedef boost::shared_ptr<SOCKS5BytestreamServerSession> ref;
+
+ public:
enum State {
Initial,
WaitingForAuthentication,
WaitingForRequest,
- SendingData,
+ ReadyForTransfer,
+ ReadingData,
+ WritingData,
Finished,
};
@@ -35,12 +42,18 @@ namespace Swift {
void start();
void stop();
- boost::signal<void (bool /* error */)> onFinished;
+ void startTransfer();
+ HostAddressPort getAddressPort() const;
+
+ boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+ boost::signal<void (int)> onBytesSent;
+ boost::signal<void (int)> onBytesReceived;
private:
void finish(bool error);
void process();
void handleDataRead(const SafeByteArray&);
+ void handleDisconnected(const boost::optional<Connection::Error>&);
void sendData();
private:
@@ -49,6 +62,7 @@ namespace Swift {
ByteArray unprocessedData;
State state;
int chunkSize;
- boost::shared_ptr<ReadBytestream> bytestream;
+ boost::shared_ptr<ReadBytestream> readBytestream;
+ boost::shared_ptr<WriteBytestream> writeBytestream;
};
}
diff --git a/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h b/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h
new file mode 100644
index 0000000..ae06cd3
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+namespace Swift {
+ class DummyFileTransferManager : public FileTransferManager {
+ public:
+ DummyFileTransferManager() : FileTransferManager() {
+ }
+
+ virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const boost::filesystem::path&, const std::string&, boost::shared_ptr<ReadBytestream>) {
+ return OutgoingFileTransfer::ref();
+ }
+
+ virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const std::string&, const std::string&, const boost::uintmax_t, const boost::posix_time::ptime&, boost::shared_ptr<ReadBytestream>) {
+ return OutgoingFileTransfer::ref();
+ }
+
+ virtual void startListeningOnPort(int) {
+ }
+
+ virtual void addS5BProxy(boost::shared_ptr<S5BProxyRequest>) {
+ }
+
+ };
+}
diff --git a/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp
new file mode 100644
index 0000000..7407f44
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/Elements/IBB.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/FileTransfer/ByteArrayWriteBytestream.h>
+#include <Swiften/FileTransfer/IncomingJingleFileTransfer.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Jingle/FakeJingleSession.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Network/DummyConnectionFactory.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Queries/IQRouter.h>
+
+#include <iostream>
+
+using namespace Swift;
+using namespace boost;
+
+class FakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+ void addRemoteTransportCandidates(JingleTransportPayload::ref cand) {
+ candidate = cand;
+ }
+
+ void selectCandidate() {
+ boost::shared_ptr<JingleS5BTransportPayload> payload = make_shared<JingleS5BTransportPayload>();
+ payload->setCandidateError(true);
+ payload->setSessionID(candidate->getSessionID());
+ onRemoteTransportCandidateSelectFinished(payload);
+ }
+
+ void setMinimumPriority(int) {
+
+ }
+
+ bool isActualCandidate(JingleTransportPayload::ref) {
+ return false;
+ }
+
+ int getPriority(JingleTransportPayload::ref) {
+ return 0;
+ }
+
+ JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+ return JingleTransport::ref();
+ }
+
+private:
+ JingleTransportPayload::ref candidate;
+};
+
+class FakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+ virtual ~FakeRemoteJingleTransportCandidateSelectorFactory() {
+
+ }
+
+ virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() {
+ return new FakeRemoteJingleTransportCandidateSelector();
+ }
+};
+
+class FakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref payload) {
+ JingleS5BTransportPayload::ref payL = make_shared<JingleS5BTransportPayload>();
+ payL->setSessionID(payload->getSessionID());
+ onLocalTransportCandidatesGenerated(payL);
+ }
+
+ void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+ onLocalTransportCandidatesGenerated(payload);
+ }
+
+ virtual bool isActualCandidate(JingleTransportPayload::ref) {
+ return false;
+ }
+
+ virtual int getPriority(JingleTransportPayload::ref) {
+ return 0;
+ }
+
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+ return JingleTransport::ref();
+ }
+};
+
+class FakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory {
+public:
+ virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() {
+ return new FakeLocalJingleTransportCandidateGenerator();
+ }
+};
+
+class IncomingJingleFileTransferTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(IncomingJingleFileTransferTest);
+ CPPUNIT_TEST(test_AcceptOnyIBBSendsSessionAccept);
+ CPPUNIT_TEST(test_OnlyIBBTransferReceiveWorks);
+ CPPUNIT_TEST(test_AcceptFailingS5BFallsBackToIBB);
+ CPPUNIT_TEST_SUITE_END();
+public:
+ shared_ptr<IncomingJingleFileTransfer> createTestling() {
+ JID ourJID("our@jid.org/full");
+ return make_shared<IncomingJingleFileTransfer>(ourJID, shared_ptr<JingleSession>(fakeJingleSession), jingleContentPayload, fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, bytestreamRegistry, bytestreamProxy, timerFactory);
+ }
+
+ IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) {
+ IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb);
+ request->setFrom(from);
+ return request;
+ }
+
+ void setUp() {
+ eventLoop = new DummyEventLoop();
+ fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession");
+ jingleContentPayload = make_shared<JingleContentPayload>();
+ fakeRJTCSF = make_shared<FakeRemoteJingleTransportCandidateSelectorFactory>();
+ fakeLJTCF = make_shared<FakeLocalJingleTransportCandidateGeneratorFactory>();
+ stanzaChannel = new DummyStanzaChannel();
+ iqRouter = new IQRouter(stanzaChannel);
+ bytestreamRegistry = new SOCKS5BytestreamRegistry();
+ timerFactory = new DummyTimerFactory();
+ connectionFactory = new DummyConnectionFactory(eventLoop);
+ bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+ }
+
+ void tearDown() {
+ delete bytestreamProxy;
+ delete connectionFactory;
+ delete timerFactory;
+ delete bytestreamRegistry;
+ delete iqRouter;
+ delete stanzaChannel;
+ delete eventLoop;
+ }
+
+ // Tests whether IncomingJingleFileTransfer would accept a IBB only file transfer.
+ void test_AcceptOnyIBBSendsSessionAccept() {
+ //1. create your test incoming file transfer
+ shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+ desc->addOffer(StreamInitiationFileInfo());
+ jingleContentPayload->addDescription(desc);
+ JingleIBBTransportPayload::ref tpRef = make_shared<JingleIBBTransportPayload>();
+ tpRef->setSessionID("mysession");
+ jingleContentPayload->addTransport(tpRef);
+
+ shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+ //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+ shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+ fileTransfer->accept(byteStream);
+
+ // check whether accept has been called
+ getCall<FakeJingleSession::AcceptCall>(0);
+ }
+
+ void test_OnlyIBBTransferReceiveWorks() {
+ //1. create your test incoming file transfer
+ shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+ desc->addOffer(StreamInitiationFileInfo());
+ jingleContentPayload->addDescription(desc);
+ JingleIBBTransportPayload::ref tpRef = make_shared<JingleIBBTransportPayload>();
+ tpRef->setSessionID("mysession");
+ jingleContentPayload->addTransport(tpRef);
+
+ shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+ //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+ shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+ fileTransfer->accept(byteStream);
+
+ // check whether accept has been called
+ getCall<FakeJingleSession::AcceptCall>(0);
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a"));
+ CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData());
+ }
+
+ void test_AcceptFailingS5BFallsBackToIBB() {
+ //1. create your test incoming file transfer
+ addFileTransferDescription();
+
+ // add SOCKS5BytestreamTransportPayload
+ JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload();
+
+ shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+ //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+ shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+ fileTransfer->accept(byteStream);
+
+ // check whether accept has been called
+ FakeJingleSession::AcceptCall acceptCall = getCall<FakeJingleSession::AcceptCall>(0);
+ CPPUNIT_ASSERT_EQUAL(payLoad->getSessionID(), acceptCall.payload->getSessionID());
+
+ // check for candidate error
+ FakeJingleSession::InfoTransportCall infoTransportCall = getCall<FakeJingleSession::InfoTransportCall>(1);
+ JingleS5BTransportPayload::ref s5bPayload = dynamic_pointer_cast<JingleS5BTransportPayload>(infoTransportCall.payload);
+ CPPUNIT_ASSERT(s5bPayload->hasCandidateError());
+
+ // indicate transport replace (Romeo)
+ fakeJingleSession->onTransportReplaceReceived(getContentID(), addJingleIBBPayload());
+
+ FakeJingleSession::AcceptTransportCall acceptTransportCall = getCall<FakeJingleSession::AcceptTransportCall>(2);
+
+ // send a bit of data
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a"));
+ CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData());
+ }
+
+ void test_S5BTransferReceiveTest() {
+ addFileTransferDescription();
+ JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload();
+ }
+
+private:
+ void addFileTransferDescription() {
+ shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+ desc->addOffer(StreamInitiationFileInfo());
+ jingleContentPayload->addDescription(desc);
+ }
+
+ shared_ptr<JingleS5BTransportPayload> addJingleS5BPayload() {
+ JingleS5BTransportPayload::ref payLoad = make_shared<JingleS5BTransportPayload>();
+ payLoad->setSessionID("mysession");
+ jingleContentPayload->addTransport(payLoad);
+ return payLoad;
+ }
+
+ shared_ptr<JingleIBBTransportPayload> addJingleIBBPayload() {
+ JingleIBBTransportPayload::ref payLoad = make_shared<JingleIBBTransportPayload>();
+ payLoad->setSessionID("mysession");
+ jingleContentPayload->addTransport(payLoad);
+ return payLoad;
+ }
+
+ JingleContentID getContentID() const {
+ return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator());
+ }
+
+ template <typename T> T getCall(int i) const {
+ size_t index = static_cast<size_t>(i);
+ CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size());
+ T* cmd = boost::get<T>(&fakeJingleSession->calledCommands[index]);
+ CPPUNIT_ASSERT(cmd);
+ return *cmd;
+ }
+
+private:
+ EventLoop* eventLoop;
+ FakeJingleSession* fakeJingleSession;
+ shared_ptr<JingleContentPayload> jingleContentPayload;
+ shared_ptr<FakeRemoteJingleTransportCandidateSelectorFactory> fakeRJTCSF;
+ shared_ptr<FakeLocalJingleTransportCandidateGeneratorFactory> fakeLJTCF;
+ DummyStanzaChannel* stanzaChannel;
+ IQRouter* iqRouter;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ DummyConnectionFactory* connectionFactory;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+ DummyTimerFactory* timerFactory;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IncomingJingleFileTransferTest);
diff --git a/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp
new file mode 100644
index 0000000..582c3e5
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/bind.hpp>
+#include <boost/optional.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
+#include <Swiften/Jingle/FakeJingleSession.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/FileTransfer/ByteArrayReadBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/IBB.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/Network/DummyConnection.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/DummyConnectionFactory.h>
+
+#include <Swiften/Base/Log.h>
+
+#include <iostream>
+
+using namespace Swift;
+
+class OFakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+ void addRemoteTransportCandidates(JingleTransportPayload::ref cand) {
+ candidate = cand;
+ }
+
+ void selectCandidate() {
+ JingleS5BTransportPayload::ref payload = boost::make_shared<JingleS5BTransportPayload>();
+ payload->setCandidateError(true);
+ payload->setSessionID(candidate->getSessionID());
+ onRemoteTransportCandidateSelectFinished(payload);
+ }
+
+ void setMinimumPriority(int) {
+
+ }
+
+ bool isActualCandidate(JingleTransportPayload::ref) {
+ return false;
+ }
+
+ int getPriority(JingleTransportPayload::ref) {
+ return 0;
+ }
+
+ JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+ return JingleTransport::ref();
+ }
+
+private:
+ JingleTransportPayload::ref candidate;
+};
+
+class OFakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+ virtual ~OFakeRemoteJingleTransportCandidateSelectorFactory() {
+
+ }
+
+ virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() {
+ return new OFakeRemoteJingleTransportCandidateSelector();
+ }
+};
+
+class OFakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref /* payload */) {
+ //JingleTransportPayload::ref payL = make_shared<JingleTransportPayload>();
+ //payL->setSessionID(payload->getSessionID());
+ JingleS5BTransportPayload::ref payL = boost::make_shared<JingleS5BTransportPayload>();
+
+ onLocalTransportCandidatesGenerated(payL);
+ }
+
+ void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+ onLocalTransportCandidatesGenerated(payload);
+ }
+
+ virtual bool isActualCandidate(JingleTransportPayload::ref) {
+ return false;
+ }
+
+ virtual int getPriority(JingleTransportPayload::ref) {
+ return 0;
+ }
+
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+ return JingleTransport::ref();
+ }
+};
+
+class OFakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory {
+public:
+ virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() {
+ return new OFakeLocalJingleTransportCandidateGenerator();
+ }
+};
+
+class OutgoingJingleFileTransferTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(OutgoingJingleFileTransferTest);
+ CPPUNIT_TEST(test_SendSessionInitiateOnStart);
+ CPPUNIT_TEST(test_IBBStartsAfterSendingSessionAccept);
+ CPPUNIT_TEST(test_ReceiveSessionTerminateAfterSessionInitiate);
+ CPPUNIT_TEST_SUITE_END();
+
+ class FTStatusHelper {
+ public:
+ bool finishedCalled;
+ FileTransferError::Type error;
+ void handleFileTransferFinished(boost::optional<FileTransferError> error) {
+ finishedCalled = true;
+ if (error.is_initialized()) this->error = error.get().getType();
+ }
+ };
+public:
+
+ boost::shared_ptr<OutgoingJingleFileTransfer> createTestling() {
+ JID to("test@foo.com/bla");
+ StreamInitiationFileInfo fileInfo;
+ fileInfo.setDescription("some file");
+ fileInfo.setName("test.bin");
+ fileInfo.setHash("asdjasdas");
+ fileInfo.setSize(1024 * 1024);
+ return boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(boost::shared_ptr<JingleSession>(fakeJingleSession), fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, idGen, to, stream, fileInfo, s5bRegistry, s5bProxy));
+ }
+
+ IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) {
+ IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb);
+ request->setFrom(from);
+ return request;
+ }
+
+ void setUp() {
+ fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession");
+ jingleContentPayload = boost::make_shared<JingleContentPayload>();
+ fakeRJTCSF = boost::make_shared<OFakeRemoteJingleTransportCandidateSelectorFactory>();
+ fakeLJTCF = boost::make_shared<OFakeLocalJingleTransportCandidateGeneratorFactory>();
+ stanzaChannel = new DummyStanzaChannel();
+ iqRouter = new IQRouter(stanzaChannel);
+ eventLoop = new DummyEventLoop();
+ timerFactory = new DummyTimerFactory();
+ connectionFactory = new DummyConnectionFactory(eventLoop);
+ s5bRegistry = new SOCKS5BytestreamRegistry();
+ s5bProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+
+ data.clear();
+ for (int n=0; n < 1024 * 1024; ++n) {
+ data.push_back(34);
+ }
+
+ stream = boost::make_shared<ByteArrayReadBytestream>(data);
+
+ idGen = new IDGenerator();
+ }
+
+ void tearDown() {
+ delete idGen;
+ delete s5bRegistry;
+ delete connectionFactory;
+ delete timerFactory;
+ delete eventLoop;
+ delete iqRouter;
+ delete stanzaChannel;
+ }
+
+
+ void test_SendSessionInitiateOnStart() {
+ boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+ transfer->start();
+ FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0);
+ JingleFileTransferDescription::ref description = boost::dynamic_pointer_cast<JingleFileTransferDescription>(call.description);
+ CPPUNIT_ASSERT(description);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), description->getOffers().size());
+ CPPUNIT_ASSERT(static_cast<size_t>(1048576) == description->getOffers()[0].getSize());
+
+ JingleS5BTransportPayload::ref transport = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(call.payload);
+ CPPUNIT_ASSERT(transport);
+ }
+
+ void test_IBBStartsAfterSendingSessionAccept() {
+ boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+ transfer->start();
+
+ FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0);
+ // FIXME: we initiate with SOCSK5 now and not IBB, needs to be fixed.
+ /*
+ fakeJingleSession->onSessionAcceptReceived(call.id, call.description, call.payload);
+ IQ::ref iqOpenStanza = stanzaChannel->getStanzaAtIndex<IQ>(0);
+ CPPUNIT_ASSERT(iqOpenStanza);
+ */
+ }
+
+ void test_ReceiveSessionTerminateAfterSessionInitiate() {
+ boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+ transfer->start();
+
+ getCall<FakeJingleSession::InitiateCall>(0);
+
+ FTStatusHelper helper;
+ helper.finishedCalled = false;
+ transfer->onFinished.connect(bind(&FTStatusHelper::handleFileTransferFinished, &helper, _1));
+ fakeJingleSession->onSessionTerminateReceived(JinglePayload::Reason(JinglePayload::Reason::Busy));
+ CPPUNIT_ASSERT_EQUAL(true, helper.finishedCalled);
+ CPPUNIT_ASSERT(FileTransferError::PeerError == helper.error);
+ }
+
+
+//TODO: some more testcases
+
+private:
+ void addFileTransferDescription() {
+ boost::shared_ptr<JingleFileTransferDescription> desc = boost::make_shared<JingleFileTransferDescription>();
+ desc->addOffer(StreamInitiationFileInfo());
+ jingleContentPayload->addDescription(desc);
+ }
+
+ boost::shared_ptr<JingleS5BTransportPayload> addJingleS5BPayload() {
+ JingleS5BTransportPayload::ref payLoad = boost::make_shared<JingleS5BTransportPayload>();
+ payLoad->setSessionID("mysession");
+ jingleContentPayload->addTransport(payLoad);
+ return payLoad;
+ }
+
+ boost::shared_ptr<JingleIBBTransportPayload> addJingleIBBPayload() {
+ JingleIBBTransportPayload::ref payLoad = boost::make_shared<JingleIBBTransportPayload>();
+ payLoad->setSessionID("mysession");
+ jingleContentPayload->addTransport(payLoad);
+ return payLoad;
+ }
+
+ JingleContentID getContentID() const {
+ return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator());
+ }
+
+ template <typename T> T getCall(int i) const {
+ size_t index = static_cast<size_t>(i);
+ CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size());
+ T* cmd = boost::get<T>(&fakeJingleSession->calledCommands[index]);
+ CPPUNIT_ASSERT(cmd);
+ return *cmd;
+ }
+
+private:
+ std::vector<unsigned char> data;
+ boost::shared_ptr<ByteArrayReadBytestream> stream;
+ FakeJingleSession* fakeJingleSession;
+ boost::shared_ptr<JingleContentPayload> jingleContentPayload;
+ boost::shared_ptr<OFakeRemoteJingleTransportCandidateSelectorFactory> fakeRJTCSF;
+ boost::shared_ptr<OFakeLocalJingleTransportCandidateGeneratorFactory> fakeLJTCF;
+ DummyStanzaChannel* stanzaChannel;
+ IQRouter* iqRouter;
+ IDGenerator* idGen;
+ EventLoop *eventLoop;
+ SOCKS5BytestreamRegistry* s5bRegistry;
+ SOCKS5BytestreamProxy* s5bProxy;
+ DummyTimerFactory* timerFactory;
+ DummyConnectionFactory* connectionFactory;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(OutgoingJingleFileTransferTest);
diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp
new file mode 100644
index 0000000..35580bf
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Base/ByteArray.h>
+#include <QA/Checker/IO.h>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/bind.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/variate_generator.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/StartStopper.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/FileTransfer/ByteArrayReadBytestream.h>
+#include <Swiften/FileTransfer/ByteArrayWriteBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/DummyConnection.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/StringCodecs/Hexify.h>
+
+using namespace Swift;
+
+boost::mt19937 randomGen;
+
+class SOCKS5BytestreamClientSessionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(SOCKS5BytestreamClientSessionTest);
+ CPPUNIT_TEST(testForSessionReady);
+ CPPUNIT_TEST(testErrorHandlingHello);
+ CPPUNIT_TEST(testErrorHandlingRequest);
+ CPPUNIT_TEST(testWriteBytestream);
+ CPPUNIT_TEST(testReadBytestream);
+ CPPUNIT_TEST_SUITE_END();
+
+ const HostAddressPort destinationAddressPort;
+ const std::string destination;
+
+public:
+ SOCKS5BytestreamClientSessionTest() : destinationAddressPort(HostAddressPort(HostAddress("127.0.0.1"), 8888)),
+ destination(SOCKS5BytestreamRegistry::getHostname("foo", JID("requester@example.com/test"), JID("target@example.com/test"))), eventLoop(NULL), timerFactory(NULL) { }
+
+ void setUp() {
+ randomGen.seed(time(NULL));
+ eventLoop = new DummyEventLoop();
+ timerFactory = new DummyTimerFactory();
+ connection = boost::make_shared<MockeryConnection>(failingPorts, true, eventLoop);
+ //connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1));
+ //stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg")));
+// connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSessionTest::handleDataRead, this, _1));
+ }
+
+ void tearDown() {
+ //connection.reset();
+ delete timerFactory;
+ delete eventLoop;
+ }
+
+ void testForSessionReady() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT(createByteArray("\x05\x01\x00", 3) == helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4));
+ CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4]));
+ CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size()));
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1));
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+ }
+
+ void testErrorHandlingHello() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloAuthFail();
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError);
+ CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled);
+ }
+
+ void testErrorHandlingRequest() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput);
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4));
+ CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4]));
+ CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size()));
+ CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1));
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestFail();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError);
+ CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled);
+ }
+
+ void testWriteBytestream() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop->processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+
+ boost::shared_ptr<ByteArrayWriteBytestream> output = boost::make_shared<ByteArrayWriteBytestream>();
+ clientSession->startReceiving(output);
+
+ ByteArray transferData = generateRandomByteArray(1024);
+ connection->onDataRead(createSafeByteArray(transferData.data(), transferData.size()));
+ CPPUNIT_ASSERT_EQUAL(transferData, output->getData());
+ }
+
+ void testReadBytestream() {
+ TestHelper helper;
+ connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+ SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+ clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+ clientSession->start();
+ eventLoop->processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondHelloOK();
+ eventLoop->processEvents();
+
+ helper.unprocessedInput.clear();
+ serverRespondRequestOK();
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+ CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+
+ helper.unprocessedInput.clear();
+ ByteArray transferData = generateRandomByteArray(1024 * 1024);
+ boost::shared_ptr<ByteArrayReadBytestream> input = boost::make_shared<ByteArrayReadBytestream>(transferData);
+ clientSession->startSending(input);
+ eventLoop->processEvents();
+
+ CPPUNIT_ASSERT_EQUAL(createByteArray(transferData.data(), transferData.size()), helper.unprocessedInput);
+ }
+
+
+
+private:
+ static ByteArray generateRandomByteArray(size_t len) {
+ boost::uniform_int<> dist(0, 255);
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomByte(randomGen, dist);
+ ByteArray result(len);
+ for (size_t i=0; i < len; ++i ) {
+ result[i] = randomByte();
+ }
+ return result;
+ }
+
+ // Server responses
+ void serverRespondHelloOK() {
+ connection->onDataRead(createSafeByteArray("\x05\00", 2));
+ }
+
+ void serverRespondHelloAuthFail() {
+ connection->onDataRead(createSafeByteArray("\x05\xFF", 2));
+ }
+
+ void serverRespondRequestOK() {
+ SafeByteArray dataToSend = createSafeByteArray("\x05\x00\x00\x03", 4);
+ append(dataToSend, createSafeByteArray(destination.size()));
+ append(dataToSend, createSafeByteArray(destination));
+ append(dataToSend, createSafeByteArray("\x00", 1));
+ connection->onDataRead(dataToSend);
+ }
+
+ void serverRespondRequestFail() {
+ SafeByteArray correctData = createSafeByteArray("\x05\x00\x00\x03", 4);
+ append(correctData, createSafeByteArray(destination.size()));
+ append(correctData, createSafeByteArray(destination));
+ append(correctData, createSafeByteArray("\x00", 1));
+
+ SafeByteArray dataToSend;
+ //ByteArray failingData = Hexify::unhexify("8417947d1d305c72c11520ea7d2c6e787396705e72c312c6ccc3f66613d7cae1b91b7ab48e8b59a17d559c15fb51");
+ //append(dataToSend, failingData);
+ //SWIFT_LOG(debug) << "hexed: " << Hexify::hexify(failingData) << std::endl;
+ do {
+ ByteArray rndArray = generateRandomByteArray(correctData.size());
+ dataToSend = createSafeByteArray(rndArray.data(), rndArray.size());
+ } while (dataToSend == correctData);
+ connection->onDataRead(dataToSend);
+ }
+
+private:
+ struct TestHelper {
+ TestHelper() : sessionReadyCalled(false), sessionReadyError(false) {}
+ ByteArray unprocessedInput;
+ bool sessionReadyCalled;
+ bool sessionReadyError;
+
+ void handleConnectionDataWritten(const SafeByteArray& data) {
+ append(unprocessedInput, data);
+ //SWIFT_LOG(debug) << "unprocessedInput (" << unprocessedInput.size() << "): " << Hexify::hexify(unprocessedInput) << std::endl;
+ }
+
+ void handleSessionReady(bool error) {
+ sessionReadyCalled = true;
+ sessionReadyError = error;
+ }
+ };
+
+
+private:
+ struct MockeryConnection : public Connection, public EventOwner, public boost::enable_shared_from_this<MockeryConnection> {
+ public:
+ MockeryConnection(const std::vector<HostAddressPort>& failingPorts, bool isResponsive, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), isResponsive(isResponsive), disconnectCalled(false) {}
+
+ void listen() { assert(false); }
+ void connect(const HostAddressPort& address) {
+ hostAddressPort = address;
+ if (isResponsive) {
+ bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end();
+ eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail));
+ }
+ }
+
+ HostAddressPort getLocalAddress() const { return HostAddressPort(); }
+ void disconnect() {
+ disconnectCalled = true;
+ }
+
+ void write(const SafeByteArray& data) {
+ eventLoop->postEvent(boost::ref(onDataWritten), shared_from_this());
+ onDataSent(data);
+ }
+
+ boost::signal<void (const SafeByteArray&)> onDataSent;
+
+ EventLoop* eventLoop;
+ boost::optional<HostAddressPort> hostAddressPort;
+ std::vector<HostAddressPort> failingPorts;
+ bool isResponsive;
+ bool disconnectCalled;
+ };
+
+private:
+ DummyEventLoop* eventLoop;
+ DummyTimerFactory* timerFactory;
+ boost::shared_ptr<MockeryConnection> connection;
+ const std::vector<HostAddressPort> failingPorts;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SOCKS5BytestreamClientSessionTest);
diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
index 06bc98f..cd480f0 100644
--- a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
+++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
@@ -34,6 +34,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
void setUp() {
receivedDataChunks = 0;
eventLoop = new DummyEventLoop();
+ bytestreams = new SOCKS5BytestreamRegistry();
connection = boost::shared_ptr<DummyConnection>(new DummyConnection(eventLoop));
connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1));
stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg")));
@@ -41,6 +42,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
void tearDown() {
connection.reset();
+ delete bytestreams;
delete eventLoop;
}
@@ -67,7 +69,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
void testRequest() {
boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- bytestreams.addBytestream("abcdef", stream1);
+ bytestreams->addReadBytestream("abcdef", stream1);
authenticate();
ByteArray hostname(createByteArray("abcdef"));
@@ -88,10 +90,11 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
void testReceiveData() {
boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- bytestreams.addBytestream("abcdef", stream1);
+ bytestreams->addReadBytestream("abcdef", stream1);
authenticate();
request("abcdef");
eventLoop->processEvents();
+ testling->startTransfer();
skipHeader("abcdef");
CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData);
@@ -102,11 +105,12 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
testling->setChunkSize(3);
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- bytestreams.addBytestream("abcdef", stream1);
+ bytestreams->addReadBytestream("abcdef", stream1);
authenticate();
request("abcdef");
eventLoop->processEvents();
-
+ testling->startTransfer();
+ eventLoop->processEvents();
skipHeader("abcdef");
CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData);
CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks);
@@ -141,13 +145,13 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
private:
SOCKS5BytestreamServerSession* createSession() {
- SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, &bytestreams);
+ SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, bytestreams);
return session;
}
private:
DummyEventLoop* eventLoop;
- SOCKS5BytestreamRegistry bytestreams;
+ SOCKS5BytestreamRegistry* bytestreams;
boost::shared_ptr<DummyConnection> connection;
std::vector<unsigned char> receivedData;
int receivedDataChunks;
diff --git a/Swiften/FileTransfer/WriteBytestream.h b/Swiften/FileTransfer/WriteBytestream.h
index c27aeff..fb6f2f1 100644
--- a/Swiften/FileTransfer/WriteBytestream.h
+++ b/Swiften/FileTransfer/WriteBytestream.h
@@ -9,6 +9,8 @@
#include <boost/shared_ptr.hpp>
#include <vector>
+#include <Swiften/Base/boost_bsignals.h>
+
namespace Swift {
class WriteBytestream {
public:
@@ -17,5 +19,7 @@ namespace Swift {
virtual ~WriteBytestream();
virtual void write(const std::vector<unsigned char>&) = 0;
+
+ boost::signal<void (const std::vector<unsigned char>&)> onWrite;
};
}