summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/FileTransfer')
-rw-r--r--Swiften/FileTransfer/ByteArrayReadBytestream.h34
-rw-r--r--Swiften/FileTransfer/ByteArrayWriteBytestream.h29
-rw-r--r--Swiften/FileTransfer/BytestreamsRequest.h11
-rw-r--r--Swiften/FileTransfer/ConnectivityManager.cpp104
-rw-r--r--Swiften/FileTransfer/ConnectivityManager.h45
-rw-r--r--Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp99
-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.cpp15
-rw-r--r--Swiften/FileTransfer/FileReadBytestream.h6
-rw-r--r--Swiften/FileTransfer/FileTransfer.h59
-rw-r--r--Swiften/FileTransfer/FileTransferManager.cpp14
-rw-r--r--Swiften/FileTransfer/FileTransferManager.h33
-rw-r--r--Swiften/FileTransfer/FileTransferManagerImpl.cpp133
-rw-r--r--Swiften/FileTransfer/FileTransferManagerImpl.h81
-rw-r--r--Swiften/FileTransfer/FileWriteBytestream.cpp14
-rw-r--r--Swiften/FileTransfer/FileWriteBytestream.h7
-rw-r--r--Swiften/FileTransfer/IBBReceiveSession.cpp122
-rw-r--r--Swiften/FileTransfer/IBBReceiveSession.h40
-rw-r--r--Swiften/FileTransfer/IBBRequest.h10
-rw-r--r--Swiften/FileTransfer/IBBSendSession.cpp56
-rw-r--r--Swiften/FileTransfer/IBBSendSession.h27
-rw-r--r--Swiften/FileTransfer/IncomingFileTransfer.cpp11
-rw-r--r--Swiften/FileTransfer/IncomingFileTransfer.h11
-rw-r--r--Swiften/FileTransfer/IncomingFileTransferManager.cpp30
-rw-r--r--Swiften/FileTransfer/IncomingFileTransferManager.h14
-rw-r--r--Swiften/FileTransfer/IncomingJingleFileTransfer.cpp504
-rw-r--r--Swiften/FileTransfer/IncomingJingleFileTransfer.h113
-rw-r--r--Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp61
-rw-r--r--Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h33
-rw-r--r--Swiften/FileTransfer/JingleIncomingIBBTransport.cpp24
-rw-r--r--Swiften/FileTransfer/JingleIncomingIBBTransport.h27
-rw-r--r--Swiften/FileTransfer/JingleTransport.cpp15
-rw-r--r--Swiften/FileTransfer/JingleTransport.h27
-rw-r--r--Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp14
-rw-r--r--Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h29
-rw-r--r--Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.cpp14
-rw-r--r--Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h18
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransfer.cpp68
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransfer.h43
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransferManager.cpp46
-rw-r--r--Swiften/FileTransfer/OutgoingFileTransferManager.h46
-rw-r--r--Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp402
-rw-r--r--Swiften/FileTransfer/OutgoingJingleFileTransfer.h117
-rw-r--r--Swiften/FileTransfer/OutgoingSIFileTransfer.cpp78
-rw-r--r--Swiften/FileTransfer/OutgoingSIFileTransfer.h53
-rw-r--r--Swiften/FileTransfer/ReadBytestream.cpp2
-rw-r--r--Swiften/FileTransfer/ReadBytestream.h17
-rw-r--r--Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.cpp14
-rw-r--r--Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h33
-rw-r--r--Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.cpp14
-rw-r--r--Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h18
-rw-r--r--Swiften/FileTransfer/SConscript33
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp236
-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/SOCKS5BytestreamProxyFinder.cpp62
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.h42
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp56
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamRegistry.h51
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServer.cpp24
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServer.h16
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp126
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerSession.h28
-rw-r--r--Swiften/FileTransfer/StreamInitiationRequest.h11
-rw-r--r--Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h36
-rw-r--r--Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp188
-rw-r--r--Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp102
-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.cpp88
-rw-r--r--Swiften/FileTransfer/WriteBytestream.cpp2
-rw-r--r--Swiften/FileTransfer/WriteBytestream.h7
80 files changed, 4970 insertions, 385 deletions
diff --git a/Swiften/FileTransfer/ByteArrayReadBytestream.h b/Swiften/FileTransfer/ByteArrayReadBytestream.h
index d459658..9311099 100644
--- a/Swiften/FileTransfer/ByteArrayReadBytestream.h
+++ b/Swiften/FileTransfer/ByteArrayReadBytestream.h
@@ -6,31 +6,47 @@
#pragma once
-#include "Swiften/FileTransfer/ReadBytestream.h"
-#include "Swiften/Base/ByteArray.h"
+#include <vector>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/Base/ByteArray.h>
namespace Swift {
class ByteArrayReadBytestream : public ReadBytestream {
public:
- ByteArrayReadBytestream(const ByteArray& data) : data(data), position(0) {
+ ByteArrayReadBytestream(const std::vector<unsigned char>& data) : data(data), position(0), dataComplete(true) {
}
- virtual ByteArray read(size_t size) {
+ virtual boost::shared_ptr<ByteArray> read(size_t size) {
size_t readSize = size;
- if (position + readSize > data.getSize()) {
- readSize = data.getSize() - position;
+ if (position + readSize > data.size()) {
+ readSize = data.size() - position;
}
- ByteArray result(data.getData() + position, readSize);
+ boost::shared_ptr<ByteArray> result = boost::make_shared<ByteArray>(data.begin() + position, data.begin() + position + readSize);
+
+ onRead(*result);
position += readSize;
return result;
}
virtual bool isFinished() const {
- return position >= data.getSize();
+ return position >= data.size() && dataComplete;
+ }
+
+ virtual void setDataComplete(bool b) {
+ dataComplete = b;
+ }
+
+ void addData(const std::vector<unsigned char>& moreData) {
+ append(data, moreData);
+ onDataAvailable();
}
private:
- ByteArray data;
+ std::vector<unsigned char> data;
size_t position;
+ bool dataComplete;
};
}
diff --git a/Swiften/FileTransfer/ByteArrayWriteBytestream.h b/Swiften/FileTransfer/ByteArrayWriteBytestream.h
new file mode 100644
index 0000000..ef97ed9
--- /dev/null
+++ b/Swiften/FileTransfer/ByteArrayWriteBytestream.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/WriteBytestream.h>
+
+namespace Swift {
+ class ByteArrayWriteBytestream : public WriteBytestream {
+ public:
+ ByteArrayWriteBytestream() {
+ }
+
+ 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 {
+ return data;
+ }
+
+ private:
+ std::vector<unsigned char> data;
+ };
+}
diff --git a/Swiften/FileTransfer/BytestreamsRequest.h b/Swiften/FileTransfer/BytestreamsRequest.h
index 9757bfa..fee09ee 100644
--- a/Swiften/FileTransfer/BytestreamsRequest.h
+++ b/Swiften/FileTransfer/BytestreamsRequest.h
@@ -8,8 +8,8 @@
#include <boost/shared_ptr.hpp>
-#include "Swiften/Queries/GenericRequest.h"
-#include "Swiften/Elements/Bytestreams.h"
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/Elements/Bytestreams.h>
namespace Swift {
class BytestreamsRequest : public GenericRequest<Bytestreams> {
@@ -20,8 +20,15 @@ namespace Swift {
return ref(new BytestreamsRequest(jid, payload, router));
}
+ static ref create(const JID& from, const JID& to, boost::shared_ptr<Bytestreams> payload, IQRouter* router) {
+ return ref(new BytestreamsRequest(from, to, payload, router));
+ }
+
private:
BytestreamsRequest(const JID& jid, boost::shared_ptr<Bytestreams> payload, IQRouter* router) : GenericRequest<Bytestreams>(IQ::Set, jid, payload, router) {
}
+
+ BytestreamsRequest(const JID& from, const JID& to, boost::shared_ptr<Bytestreams> payload, IQRouter* router) : GenericRequest<Bytestreams>(IQ::Set, from, to, payload, router) {
+ }
};
}
diff --git a/Swiften/FileTransfer/ConnectivityManager.cpp b/Swiften/FileTransfer/ConnectivityManager.cpp
new file mode 100644
index 0000000..7d25991
--- /dev/null
+++ b/Swiften/FileTransfer/ConnectivityManager.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "ConnectivityManager.h"
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/NATTraversalGetPublicIPRequest.h>
+#include <Swiften/Network/NATTraversalRemovePortForwardingRequest.h>
+#include <Swiften/Network/NATTraverser.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+ConnectivityManager::ConnectivityManager(NATTraverser* 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<NATTraversalGetPublicIPRequest> getIPRequest = natTraversalWorker->createGetPublicIPRequest();
+ if (getIPRequest) {
+ getIPRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalGetPublicIPResult, this, _1));
+ getIPRequest->run();
+ }
+
+ boost::shared_ptr<NATTraversalForwardPortRequest> 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<NATTraversalRemovePortForwardingRequest> 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;
+
+ std::vector<NetworkInterface> networkInterfaces;
+ foreach (const NetworkInterface& iface, networkInterfaces) {
+ foreach (const 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<NATPortMapping> 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..c094c02
--- /dev/null
+++ b/Swiften/FileTransfer/ConnectivityManager.h
@@ -0,0 +1,45 @@
+/*
+ * 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/NATTraverser.h>
+#include <Swiften/Network/NATTraversalForwardPortRequest.h>
+#include <Swiften/Network/NATPortMapping.h>
+
+namespace Swift {
+
+class NATTraverser;
+
+class ConnectivityManager {
+public:
+ ConnectivityManager(NATTraverser*);
+ ~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<NATPortMapping> mapping);
+
+private:
+ NATTraverser* 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..4b205cb
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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) {
+ if (proxy->getStreamHost()) { // FIXME: Added this test, because there were cases where this wasn't initialized. Investigate this. (Remko)
+ JingleS5BTransportPayload::Candidate candidate;
+ candidate.type = JingleS5BTransportPayload::Candidate::ProxyType;
+ candidate.jid = (*proxy->getStreamHost()).jid;
+ candidate.hostPort = (*proxy->getStreamHost()).addressPort;
+ candidate.priority = 65536 * 10 + 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 c08747b..a8946a0 100644
--- a/Swiften/FileTransfer/FileReadBytestream.cpp
+++ b/Swiften/FileTransfer/FileReadBytestream.cpp
@@ -6,8 +6,10 @@
#include <boost/filesystem/fstream.hpp>
#include <cassert>
+#include <boost/smart_ptr/make_shared.hpp>
-#include "Swiften/FileTransfer/FileReadBytestream.h"
+#include <Swiften/FileTransfer/FileReadBytestream.h>
+#include <Swiften/Base/ByteArray.h>
namespace Swift {
@@ -21,15 +23,16 @@ FileReadBytestream::~FileReadBytestream() {
}
}
-ByteArray FileReadBytestream::read(size_t size) {
+boost::shared_ptr<ByteArray> FileReadBytestream::read(size_t size) {
if (!stream) {
stream = new boost::filesystem::ifstream(file, std::ios_base::in|std::ios_base::binary);
}
- ByteArray result;
- result.resize(size);
+ boost::shared_ptr<ByteArray> result = boost::make_shared<ByteArray>();
+ result->resize(size);
assert(stream->good());
- stream->read(reinterpret_cast<char*>(result.getData()), size);
- result.resize(stream->gcount());
+ stream->read(reinterpret_cast<char*>(vecptr(*result)), size);
+ result->resize(stream->gcount());
+ onRead(*result);
return result;
}
diff --git a/Swiften/FileTransfer/FileReadBytestream.h b/Swiften/FileTransfer/FileReadBytestream.h
index 055e194..e9db2a4 100644
--- a/Swiften/FileTransfer/FileReadBytestream.h
+++ b/Swiften/FileTransfer/FileReadBytestream.h
@@ -6,10 +6,10 @@
#pragma once
-#include <boost/filesystem.hpp>
+#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
-#include "Swiften/FileTransfer/ReadBytestream.h"
+#include <Swiften/FileTransfer/ReadBytestream.h>
namespace Swift {
class FileReadBytestream : public ReadBytestream {
@@ -17,7 +17,7 @@ namespace Swift {
FileReadBytestream(const boost::filesystem::path& file);
~FileReadBytestream();
- virtual ByteArray read(size_t size) ;
+ virtual boost::shared_ptr< std::vector<unsigned char> > read(size_t size);
virtual bool isFinished() const;
private:
diff --git a/Swiften/FileTransfer/FileTransfer.h b/Swiften/FileTransfer/FileTransfer.h
new file mode 100644
index 0000000..336c51c
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransfer.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/cstdint.hpp>
+#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:
+ boost::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..d59f029
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManager.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 <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 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..83320b2
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 <boost/cstdint.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include "Swiften/Disco/EntityCapsProvider.h"
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.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/NATTraverser.h>
+
+namespace Swift {
+
+FileTransferManagerImpl::FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, NATTraverser* natTraverser) : ownJID(ownFullJID), jingleSM(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), presenceOracle(presOracle), timerFactory(timerFactory), connectionFactory(connectionFactory), connectionServerFactory(connectionServerFactory), natTraverser(natTraverser), bytestreamServer(NULL), s5bProxyFinder(NULL) {
+ assert(!ownFullJID.isBare());
+
+ connectivityManager = new ConnectivityManager(natTraverser);
+ bytestreamRegistry = new SOCKS5BytestreamRegistry();
+ bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+
+ localCandidateGeneratorFactory = new DefaultLocalJingleTransportCandidateGeneratorFactory(connectivityManager, bytestreamRegistry, bytestreamProxy, ownFullJID);
+ remoteCandidateSelectorFactory = new DefaultRemoteJingleTransportCandidateSelectorFactory(connectionFactory, timerFactory);
+ outgoingFTManager = new OutgoingFileTransferManager(jingleSM, iqRouter, capsProvider, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy);
+ incomingFTManager = new IncomingFileTransferManager(jingleSM, iqRouter, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy, timerFactory);
+ incomingFTManager->onIncomingFileTransfer.connect(onIncomingFileTransfer);
+}
+
+FileTransferManagerImpl::~FileTransferManagerImpl() {
+ if (s5bProxyFinder) {
+ s5bProxyFinder->stop();
+ delete s5bProxyFinder;
+ }
+ if (bytestreamServer) {
+ bytestreamServer->stop();
+ delete bytestreamServer;
+ }
+ 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);
+
+ s5bProxyFinder = new SOCKS5BytestreamProxyFinder(ownJID.getDomain(), iqRouter);
+ s5bProxyFinder->onProxyFound.connect(boost::bind(&FileTransferManagerImpl::addS5BProxy, this, _1));
+ s5bProxyFinder->start();
+}
+
+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) || info->hasFeature(DiscoInfo::JingleTransportsS5BFeature))) {
+
+ 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();
+ boost::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 boost::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(ownJID, receipient, bytestream, fileInfo);
+}
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.h b/Swiften/FileTransfer/FileTransferManagerImpl.h
new file mode 100644
index 0000000..248b437
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.h
@@ -0,0 +1,81 @@
+/*
+ * 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 NATTraverser;
+ class PresenceOracle;
+ class ReadBytestream;
+ class RemoteJingleTransportCandidateSelectorFactory;
+ class SOCKS5BytestreamRegistry;
+ class SOCKS5BytestreamServer;
+ class SOCKS5BytestreamProxy;
+ class TimerFactory;
+ class SOCKS5BytestreamProxyFinder;
+
+ class FileTransferManagerImpl : public FileTransferManager {
+ public:
+ FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, NATTraverser* natTraverser);
+ ~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;
+ NATTraverser* natTraverser;
+ SOCKS5BytestreamRegistry* bytestreamRegistry;
+ SOCKS5BytestreamServer* bytestreamServer;
+ SOCKS5BytestreamProxy* bytestreamProxy;
+ ConnectivityManager* connectivityManager;
+ SOCKS5BytestreamProxyFinder* s5bProxyFinder;
+ };
+
+}
diff --git a/Swiften/FileTransfer/FileWriteBytestream.cpp b/Swiften/FileTransfer/FileWriteBytestream.cpp
index 4d29bd1..6a22c6a 100644
--- a/Swiften/FileTransfer/FileWriteBytestream.cpp
+++ b/Swiften/FileTransfer/FileWriteBytestream.cpp
@@ -7,7 +7,7 @@
#include <boost/filesystem/fstream.hpp>
#include <cassert>
-#include "Swiften/FileTransfer/FileWriteBytestream.h"
+#include <Swiften/FileTransfer/FileWriteBytestream.h>
namespace Swift {
@@ -21,12 +21,20 @@ FileWriteBytestream::~FileWriteBytestream() {
}
}
-void FileWriteBytestream::write(const ByteArray& data) {
+void FileWriteBytestream::write(const std::vector<unsigned char>& data) {
if (!stream) {
stream = new boost::filesystem::ofstream(file, std::ios_base::out|std::ios_base::binary);
}
assert(stream->good());
- stream->write(reinterpret_cast<const char*>(data.getData()), data.getSize());
+ 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 c6f7b39..82c4a65 100644
--- a/Swiften/FileTransfer/FileWriteBytestream.h
+++ b/Swiften/FileTransfer/FileWriteBytestream.h
@@ -6,10 +6,10 @@
#pragma once
-#include <boost/filesystem.hpp>
+#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
-#include "Swiften/FileTransfer/WriteBytestream.h"
+#include <Swiften/FileTransfer/WriteBytestream.h>
namespace Swift {
class FileWriteBytestream : public WriteBytestream {
@@ -17,7 +17,8 @@ namespace Swift {
FileWriteBytestream(const boost::filesystem::path& file);
~FileWriteBytestream();
- virtual void write(const ByteArray&);
+ 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 5c90757..1a2bb3a 100644
--- a/Swiften/FileTransfer/IBBReceiveSession.cpp
+++ b/Swiften/FileTransfer/IBBReceiveSession.cpp
@@ -4,31 +4,107 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/IBBReceiveSession.h"
+#include <Swiften/FileTransfer/IBBReceiveSession.h>
#include <boost/bind.hpp>
-#include "Swiften/Queries/IQRouter.h"
-#include "Swiften/FileTransfer/IBBRequest.h"
-#include "Swiften/FileTransfer/BytestreamException.h"
+#include <Swiften/Base/Log.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/FileTransfer/IBBRequest.h>
+#include <Swiften/FileTransfer/BytestreamException.h>
+#include <Swiften/Queries/SetResponder.h>
+
+#include <cassert>
namespace Swift {
-IBBReceiveSession::IBBReceiveSession(const std::string& id, const JID& from, size_t size, WriteBytestream::ref bytestream, IQRouter* router) : SetResponder<IBB>(router), id(id), from(from), size(size), bytestream(bytestream), router(router), sequenceNumber(0), active(false), receivedSize(0) {
+class IBBReceiveSession::IBBResponder : public SetResponder<IBB> {
+ public:
+ IBBResponder(IBBReceiveSession* session, IQRouter* router) : SetResponder<IBB>(router), session(session), sequenceNumber(0), receivedSize(0) {
+ }
+
+ virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, IBB::ref ibb) {
+ if (from == session->from && ibb->getStreamID() == session->id) {
+ if (ibb->getAction() == IBB::Data) {
+ if (sequenceNumber == ibb->getSequenceNumber()) {
+ session->onDataReceived(ibb->getData());
+ receivedSize += ibb->getData().size();
+ sequenceNumber++;
+ sendResponse(from, id, IBB::ref());
+ if (receivedSize >= session->size) {
+ if (receivedSize > session->size) {
+ std::cerr << "Warning: Received more data than expected" << std::endl;
+ }
+ session->finish(boost::optional<FileTransferError>());
+ }
+ }
+ else {
+ SWIFT_LOG(warning) << "Received data out of order" << std::endl;
+ sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel);
+ session->finish(FileTransferError(FileTransferError::ClosedError));
+ }
+ }
+ 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;
+ }
+
+ private:
+ IBBReceiveSession* session;
+ int sequenceNumber;
+ size_t receivedSize;
+};
+
+
+IBBReceiveSession::IBBReceiveSession(
+ const std::string& id,
+ const JID& from,
+ const JID& to,
+ size_t size,
+ IQRouter* router) :
+ id(id),
+ from(from),
+ to(to),
+ size(size),
+ router(router),
+ active(false) {
+ assert(!id.empty());
+ assert(from.isValid());
+ responder = new IBBResponder(this, router);
}
IBBReceiveSession::~IBBReceiveSession() {
+ if (active) {
+ SWIFT_LOG(warning) << "Session still active" << std::endl;
+ }
+ delete responder;
}
void IBBReceiveSession::start() {
+ SWIFT_LOG(debug) << "receive session started" << std::endl;
active = true;
+ responder->start();
}
void IBBReceiveSession::stop() {
- if (active && router->isAvailable()) {
- IBBRequest::create(from, IBB::createIBBClose(id), router)->send();
+ SWIFT_LOG(debug) << "receive session stopped" << std::endl;
+ responder->stop();
+ if (active) {
+ if (router->isAvailable()) {
+ IBBRequest::create(to, from, IBB::createIBBClose(id), router)->send();
+ }
+ finish(boost::optional<FileTransferError>());
}
- finish(boost::optional<FileTransferError>());
}
void IBBReceiveSession::finish(boost::optional<FileTransferError> error) {
@@ -36,34 +112,4 @@ void IBBReceiveSession::finish(boost::optional<FileTransferError> error) {
onFinished(error);
}
-bool IBBReceiveSession::handleSetRequest(const JID& from, const JID&, const std::string& id, IBB::ref ibb) {
- if (from == this->from && ibb->getStreamID() == id) {
- if (ibb->getAction() == IBB::Data) {
- if (sequenceNumber == ibb->getSequenceNumber()) {
- bytestream->write(ibb->getData());
- receivedSize += ibb->getData().getSize();
- if (receivedSize >= size) {
- if (receivedSize > size) {
- std::cerr << "Warning: Received more data than expected" << std::endl;
- }
- finish(boost::optional<FileTransferError>());
- }
- }
- else {
- sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel);
- finish(FileTransferError(FileTransferError::ClosedError));
- }
- }
- else if (ibb->getAction() == IBB::Open) {
- sendResponse(from, id, IBB::ref());
- }
- else if (ibb->getAction() == IBB::Close) {
- sendResponse(from, id, IBB::ref());
- finish(FileTransferError(FileTransferError::ClosedError));
- }
- return true;
- }
- return false;
-}
-
}
diff --git a/Swiften/FileTransfer/IBBReceiveSession.h b/Swiften/FileTransfer/IBBReceiveSession.h
index 6d936de..d1c47bf 100644
--- a/Swiften/FileTransfer/IBBReceiveSession.h
+++ b/Swiften/FileTransfer/IBBReceiveSession.h
@@ -7,27 +7,39 @@
#pragma once
#include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
+#include <boost/optional/optional_fwd.hpp>
-#include "Swiften/Base/boost_bsignals.h"
-#include "Swiften/FileTransfer/WriteBytestream.h"
-#include "Swiften/JID/JID.h"
-#include "Swiften/Elements/IBB.h"
-#include "Swiften/Elements/ErrorPayload.h"
-#include "Swiften/FileTransfer/FileTransferError.h"
-#include "Swiften/Queries/SetResponder.h"
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/IBB.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
namespace Swift {
class IQRouter;
- class IBBReceiveSession : public SetResponder<IBB> {
+ class IBBReceiveSession {
public:
- IBBReceiveSession(const std::string& id, const JID& from, size_t size, WriteBytestream::ref bytestream, IQRouter* router);
+ IBBReceiveSession(
+ const std::string& id,
+ const JID& from,
+ const JID& to,
+ size_t size,
+ IQRouter* router);
~IBBReceiveSession();
void start();
void stop();
+ const JID& getSender() const {
+ return from;
+ }
+
+ const JID& getReceiver() const {
+ return to;
+ }
+
+ boost::signal<void (const std::vector<unsigned char>&)> onDataReceived;
boost::signal<void (boost::optional<FileTransferError>)> onFinished;
private:
@@ -35,13 +47,15 @@ namespace Swift {
void finish(boost::optional<FileTransferError>);
private:
+ class IBBResponder;
+ friend class IBBResponder;
+
std::string id;
JID from;
+ JID to;
size_t size;
- WriteBytestream::ref bytestream;
IQRouter* router;
- int sequenceNumber;
+ IBBResponder* responder;
bool active;
- size_t receivedSize;
};
}
diff --git a/Swiften/FileTransfer/IBBRequest.h b/Swiften/FileTransfer/IBBRequest.h
index f104277..58be173 100644
--- a/Swiften/FileTransfer/IBBRequest.h
+++ b/Swiften/FileTransfer/IBBRequest.h
@@ -6,8 +6,8 @@
#pragma once
-#include "Swiften/Queries/GenericRequest.h"
-#include "Swiften/Elements/IBB.h"
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/Elements/IBB.h>
namespace Swift {
@@ -15,12 +15,12 @@ namespace Swift {
public:
typedef boost::shared_ptr<IBBRequest> ref;
- static ref create(const JID& jid, boost::shared_ptr<IBB> payload, IQRouter* router) {
- return ref(new IBBRequest(jid, payload, router));
+ static ref create(const JID& from, const JID& to, boost::shared_ptr<IBB> payload, IQRouter* router) {
+ return ref(new IBBRequest(from, to, payload, router));
}
private:
- IBBRequest(const JID& jid, boost::shared_ptr<IBB> payload, IQRouter* router) : GenericRequest<IBB>(IQ::Set, jid, payload, router) {
+ IBBRequest(const JID& from, const JID& to, boost::shared_ptr<IBB> payload, IQRouter* router) : GenericRequest<IBB>(IQ::Set, from, to, payload, router) {
}
};
}
diff --git a/Swiften/FileTransfer/IBBSendSession.cpp b/Swiften/FileTransfer/IBBSendSession.cpp
index 0fb47d3..c24cc0a 100644
--- a/Swiften/FileTransfer/IBBSendSession.cpp
+++ b/Swiften/FileTransfer/IBBSendSession.cpp
@@ -4,24 +4,27 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/IBBSendSession.h"
+#include <Swiften/FileTransfer/IBBSendSession.h>
#include <boost/bind.hpp>
-#include "Swiften/Queries/IQRouter.h"
-#include "Swiften/FileTransfer/IBBRequest.h"
-#include "Swiften/FileTransfer/BytestreamException.h"
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/FileTransfer/IBBRequest.h>
+#include <Swiften/FileTransfer/BytestreamException.h>
namespace Swift {
-IBBSendSession::IBBSendSession(const std::string& id, const JID& to, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* router) : id(id), to(to), bytestream(bytestream), router(router), blockSize(4096), sequenceNumber(0), active(false) {
+IBBSendSession::IBBSendSession(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* router) : id(id), from(from), to(to), bytestream(bytestream), router(router), blockSize(4096), sequenceNumber(0), active(false), waitingForData(false) {
+ bytestream->onDataAvailable.connect(boost::bind(&IBBSendSession::handleDataAvailable, this));
}
IBBSendSession::~IBBSendSession() {
+ bytestream->onDataAvailable.disconnect(boost::bind(&IBBSendSession::handleDataAvailable, this));
}
void IBBSendSession::start() {
- IBBRequest::ref request = IBBRequest::create(to, IBB::createIBBOpen(id, blockSize), router);
+ IBBRequest::ref request = IBBRequest::create(from, to, IBB::createIBBOpen(id, blockSize), router);
request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
active = true;
request->send();
@@ -29,24 +32,15 @@ void IBBSendSession::start() {
void IBBSendSession::stop() {
if (active && router->isAvailable()) {
- IBBRequest::create(to, IBB::createIBBClose(id), router)->send();
+ IBBRequest::create(from, to, IBB::createIBBClose(id), router)->send();
}
finish(boost::optional<FileTransferError>());
}
void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
- if (!error) {
+ if (!error && active) {
if (!bytestream->isFinished()) {
- try {
- ByteArray data = bytestream->read(blockSize);
- IBBRequest::ref request = IBBRequest::create(to, IBB::createIBBData(id, sequenceNumber, data), router);
- sequenceNumber++;
- request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
- request->send();
- }
- catch (const BytestreamException& e) {
- finish(FileTransferError(FileTransferError::ReadError));
- }
+ sendMoreData();
}
else {
finish(boost::optional<FileTransferError>());
@@ -57,9 +51,35 @@ void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
}
}
+void IBBSendSession::sendMoreData() {
+ try {
+ boost::shared_ptr<ByteArray> data = bytestream->read(blockSize);
+ if (!data->empty()) {
+ waitingForData = false;
+ IBBRequest::ref request = IBBRequest::create(from, to, IBB::createIBBData(id, sequenceNumber, *data), router);
+ sequenceNumber++;
+ request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
+ request->send();
+ onBytesSent(data->size());
+ }
+ else {
+ waitingForData = true;
+ }
+ }
+ catch (const BytestreamException&) {
+ finish(FileTransferError(FileTransferError::ReadError));
+ }
+}
+
void IBBSendSession::finish(boost::optional<FileTransferError> error) {
active = false;
onFinished(error);
}
+void IBBSendSession::handleDataAvailable() {
+ if (waitingForData) {
+ sendMoreData();
+ }
+}
+
}
diff --git a/Swiften/FileTransfer/IBBSendSession.h b/Swiften/FileTransfer/IBBSendSession.h
index bef7bec..abd217b 100644
--- a/Swiften/FileTransfer/IBBSendSession.h
+++ b/Swiften/FileTransfer/IBBSendSession.h
@@ -9,41 +9,54 @@
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>
-#include "Swiften/Base/boost_bsignals.h"
-#include "Swiften/FileTransfer/ReadBytestream.h"
-#include "Swiften/JID/JID.h"
-#include "Swiften/Elements/IBB.h"
-#include "Swiften/Elements/ErrorPayload.h"
-#include "Swiften/FileTransfer/FileTransferError.h"
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/IBB.h>
+#include <Swiften/Elements/ErrorPayload.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
namespace Swift {
class IQRouter;
class IBBSendSession {
public:
- IBBSendSession(const std::string& id, const JID& to, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* router);
+ IBBSendSession(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* router);
~IBBSendSession();
void start();
void stop();
+ const JID& getSender() const {
+ return from;
+ }
+
+ const JID& getReceiver() const {
+ return to;
+ }
+
void setBlockSize(int blockSize) {
this->blockSize = blockSize;
}
boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+ boost::signal<void (int)> onBytesSent;
private:
void handleIBBResponse(IBB::ref, ErrorPayload::ref);
void finish(boost::optional<FileTransferError>);
+ void sendMoreData();
+ void handleDataAvailable();
private:
std::string id;
+ JID from;
JID to;
boost::shared_ptr<ReadBytestream> bytestream;
IQRouter* router;
int blockSize;
int sequenceNumber;
bool active;
+ bool waitingForData;
};
}
diff --git a/Swiften/FileTransfer/IncomingFileTransfer.cpp b/Swiften/FileTransfer/IncomingFileTransfer.cpp
index 238ccce..7c97e4d 100644
--- a/Swiften/FileTransfer/IncomingFileTransfer.cpp
+++ b/Swiften/FileTransfer/IncomingFileTransfer.cpp
@@ -4,20 +4,11 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/IncomingFileTransfer.h"
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
namespace Swift {
IncomingFileTransfer::~IncomingFileTransfer() {
-
-}
-
-/*void IncomingFileTransfer::accept(WriteBytestream::ref) {
-
}
-void IncomingFileTransfer::stop() {
-
-}*/
-
}
diff --git a/Swiften/FileTransfer/IncomingFileTransfer.h b/Swiften/FileTransfer/IncomingFileTransfer.h
index 4286ef0..5b53d54 100644
--- a/Swiften/FileTransfer/IncomingFileTransfer.h
+++ b/Swiften/FileTransfer/IncomingFileTransfer.h
@@ -8,16 +8,21 @@
#include <boost/shared_ptr.hpp>
-#include "Swiften/Base/boost_bsignals.h"
-#include "Swiften/FileTransfer/WriteBytestream.h"
+#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;
+ virtual const JID& getRecipient() const = 0;
};
}
diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.cpp b/Swiften/FileTransfer/IncomingFileTransferManager.cpp
index 5535840..22e8bf9 100644
--- a/Swiften/FileTransfer/IncomingFileTransferManager.cpp
+++ b/Swiften/FileTransfer/IncomingFileTransferManager.cpp
@@ -10,13 +10,17 @@
#include <Swiften/Elements/JingleDescription.h>
#include <Swiften/Elements/JingleFileTransferDescription.h>
-#include <Swiften/Elements/JingleIBBTransport.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/Jingle/Jingle.h>
#include <Swiften/FileTransfer/IncomingJingleFileTransfer.h>
namespace Swift {
-IncomingFileTransferManager::IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router) : jingleSessionManager(jingleSessionManager), router(router) {
+IncomingFileTransferManager::IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router,
+ RemoteJingleTransportCandidateSelectorFactory* remoteFactory,
+ LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory) : jingleSessionManager(jingleSessionManager), router(router), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), timerFactory(timerFactory) {
jingleSessionManager->addIncomingSessionHandler(this);
}
@@ -24,16 +28,22 @@ IncomingFileTransferManager::~IncomingFileTransferManager() {
jingleSessionManager->removeIncomingSessionHandler(this);
}
-bool IncomingFileTransferManager::handleIncomingJingleSession(IncomingJingleSession::ref session) {
- JingleContent::ref content = session->getContentWithDescription<JingleFileTransferDescription>();
- if (content) {
- // Check for supported transports
- if (content->getTransport<JingleIBBTransport>()) {
- IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(session);
- onIncomingFileTransfer(transfer);
+bool IncomingFileTransferManager::handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents, const JID& recipient) {
+ if (JingleContentPayload::ref content = Jingle::getContentWithDescription<JingleFileTransferDescription>(contents)) {
+ if (content->getTransport<JingleIBBTransportPayload>() || content->getTransport<JingleS5BTransportPayload>()) {
+
+ JingleFileTransferDescription::ref description = content->getDescription<JingleFileTransferDescription>();
+
+ if (description && description->getOffers().size() == 1) {
+ IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(recipient, 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 a54b5cd..2d1c07f 100644
--- a/Swiften/FileTransfer/IncomingFileTransferManager.h
+++ b/Swiften/FileTransfer/IncomingFileTransferManager.h
@@ -15,19 +15,29 @@
namespace Swift {
class IQRouter;
class JingleSessionManager;
+ class RemoteJingleTransportCandidateSelectorFactory;
+ class LocalJingleTransportCandidateGeneratorFactory;
+ class SOCKS5BytestreamRegistry;
+ class SOCKS5BytestreamProxy;
+ class TimerFactory;
class IncomingFileTransferManager : public IncomingJingleSessionHandler {
public:
- IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router);
+ IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory);
~IncomingFileTransferManager();
boost::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;
private:
- bool handleIncomingJingleSession(IncomingJingleSession::ref session);
+ bool handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents, const JID& recipient);
private:
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 cb2f65c..0871568 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
@@ -6,14 +6,516 @@
#include <Swiften/FileTransfer/IncomingJingleFileTransfer.h>
+#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.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(IncomingJingleSession::ref session) : session(session) {
+IncomingJingleFileTransfer::IncomingJingleFileTransfer(
+ const JID& ourJID,
+ JingleSession::ref session,
+ JingleContentPayload::ref content,
+ RemoteJingleTransportCandidateSelectorFactory* candidateSelectorFactory,
+ LocalJingleTransportCandidateGeneratorFactory* candidateGeneratorFactory,
+ IQRouter* router,
+ SOCKS5BytestreamRegistry* registry,
+ SOCKS5BytestreamProxy* proxy,
+ TimerFactory* timerFactory) :
+ ourJID(ourJID),
+ session(session),
+ router(router),
+ timerFactory(timerFactory),
+ initialContent(content),
+ state(Initial),
+ receivedBytes(0),
+ s5bRegistry(registry),
+ s5bProxy(proxy),
+ remoteTransportCandidateSelectFinished(false),
+ localTransportCandidateSelectFinished(false),
+ serverSession(0) {
+
+ candidateSelector = candidateSelectorFactory->createCandidateSelector();
+ candidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
+
+ candidateGenerator = candidateGeneratorFactory->createCandidateGenerator();
+ candidateGenerator->onLocalTransportCandidatesGenerated.connect(boost::bind(&IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1));
+
+ 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, _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));
+
+ candidateGenerator->onLocalTransportCandidatesGenerated.disconnect(boost::bind(&IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1));
+ delete candidateGenerator;
+ candidateSelector->onRemoteTransportCandidateSelectFinished.disconnect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
+ delete candidateSelector;
}
void IncomingJingleFileTransfer::accept(WriteBytestream::ref 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->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);
+ candidateSelector->setRequesterTargtet(session->getInitiator(), ourJID);
+ s5bTransport->setSessionID(s5bSessionID);
+ candidateGenerator->generateLocalTransportCandidates(s5bTransport);
+ }
+ else {
+ assert(false);
+ }
+}
+
+const JID& IncomingJingleFileTransfer::getSender() const {
+ return session->getInitiator();
+}
+
+const JID& IncomingJingleFileTransfer::getRecipient() const {
+ return ourJID;
+}
+
+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 (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();
+ }
+ }
+ else {
+ SWIFT_LOG(debug) << "Unhandled state!" << std::endl;
+ }
+}
+
+
+void IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref transport) {
+ 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)) {
+ setActiveTransport(candidateGenerator->selectTransport(selectedLocalTransportCandidate));
+ }
+ else {
+ setActiveTransport(candidateSelector->selectTransport(selectedRemoteTransportCandidate));
+ }
+ }
+ else if (candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) {
+ setActiveTransport(candidateSelector->selectTransport(selectedRemoteTransportCandidate));
+ }
+ else if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate)) {
+ setActiveTransport(candidateGenerator->selectTransport(selectedLocalTransportCandidate));
+ }
+ else {
+ state = WaitingForFallbackOrTerminate;
+ }
+ }
+}
+
+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();
+}
+
+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().find("sha-1")->second;
+ }
+ else if (transferHash->getHashes().find("md5") != transferHash->getHashes().end()) {
+ algo = "md5";
+ hash = transferHash->getHashes().find("md5")->second;
+ }
+ 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) {
+ 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();
+}
+
+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->sendTransportAccept(content, ibbTransport);
+ } else {
+ SWIFT_LOG(debug) << "transport replaced failed" << std::endl;
+ session->sendTransportReject(content, transport);
+ }
+}
+
+void IncomingJingleFileTransfer::stopActiveTransport() {
+ if (activeTransport) {
+ activeTransport->stop();
+ activeTransport->onDataReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1));
+ }
+}
+
+JingleIncomingIBBTransport::ref IncomingJingleFileTransfer::createIBBTransport(JingleIBBTransportPayload::ref ibbTransport) {
+ // TODO: getOffer() -> getOffers correction
+ return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), getRecipient(), 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 d69449e..4ae0bfb 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.h
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.h
@@ -7,21 +7,128 @@
#pragma once
#include <boost/shared_ptr.hpp>
+#include <boost/cstdint.hpp>
-#include <Swiften/Jingle/IncomingJingleSession.h>
+#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;
+ class RemoteJingleTransportCandidateSelectorFactory;
+ class LocalJingleTransportCandidateGeneratorFactory;
+ class RemoteJingleTransportCandidateSelector;
+ class LocalJingleTransportCandidateGenerator;
+ class SOCKS5BytestreamRegistry;
+ class SOCKS5BytestreamProxy;
+ class IncrementalBytestreamHashCalculator;
+
class IncomingJingleFileTransfer : public IncomingFileTransfer {
public:
typedef boost::shared_ptr<IncomingJingleFileTransfer> ref;
+ enum State {
+ Initial,
+ CreatingInitialTransports,
+ NegotiatingTransport,
+ Transferring,
+ WaitingForFallbackOrTerminate,
+ Terminated
+ };
- IncomingJingleFileTransfer(IncomingJingleSession::ref session);
+ IncomingJingleFileTransfer(
+ const JID& recipient,
+ JingleSession::ref,
+ JingleContentPayload::ref content,
+ RemoteJingleTransportCandidateSelectorFactory*,
+ LocalJingleTransportCandidateGeneratorFactory*,
+ IQRouter* router,
+ SOCKS5BytestreamRegistry* bytestreamRegistry,
+ SOCKS5BytestreamProxy* bytestreamProxy,
+ TimerFactory*);
+ ~IncomingJingleFileTransfer();
virtual void accept(WriteBytestream::ref);
+ virtual const JID& getSender() const;
+ virtual const JID& getRecipient() const;
+ void cancel();
+
+ private:
+ 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:
- IncomingJingleSession::ref session;
+ JID ourJID;
+ JingleSession::ref session;
+ IQRouter* router;
+ TimerFactory* timerFactory;
+ JingleContentPayload::ref initialContent;
+ State state;
+ JingleFileTransferDescription::ref description;
WriteBytestream::ref stream;
+ boost::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
new file mode 100644
index 0000000..ccca641
--- /dev/null
+++ b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
+
+namespace Swift {
+
+JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const JID& to, const std::string& id, size_t size, IQRouter* router) : ibbSession(id, from, to, size, router) {
+ ibbSession.onDataReceived.connect(boost::ref(onDataReceived));
+ ibbSession.onFinished.connect(boost::ref(onFinished));
+}
+
+void JingleIncomingIBBTransport::start() {
+ ibbSession.start();
+}
+
+void JingleIncomingIBBTransport::stop() {
+ ibbSession.stop();
+}
+
+}
diff --git a/Swiften/FileTransfer/JingleIncomingIBBTransport.h b/Swiften/FileTransfer/JingleIncomingIBBTransport.h
new file mode 100644
index 0000000..be18a2d
--- /dev/null
+++ b/Swiften/FileTransfer/JingleIncomingIBBTransport.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/FileTransfer/JingleTransport.h>
+#include <Swiften/FileTransfer/IBBReceiveSession.h>
+
+namespace Swift {
+ class JingleIncomingIBBTransport : public JingleTransport {
+ public:
+ typedef boost::shared_ptr<JingleIncomingIBBTransport> ref;
+
+ JingleIncomingIBBTransport(const JID& from, const JID& to, const std::string& id, size_t size, IQRouter* router);
+
+ virtual void start();
+ virtual void stop();
+
+ private:
+ IBBReceiveSession ibbSession;
+ };
+}
diff --git a/Swiften/FileTransfer/JingleTransport.cpp b/Swiften/FileTransfer/JingleTransport.cpp
new file mode 100644
index 0000000..c507922
--- /dev/null
+++ b/Swiften/FileTransfer/JingleTransport.cpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/JingleTransport.h>
+
+namespace Swift {
+
+JingleTransport::~JingleTransport() {
+
+}
+
+}
diff --git a/Swiften/FileTransfer/JingleTransport.h b/Swiften/FileTransfer/JingleTransport.h
new file mode 100644
index 0000000..fa296e8
--- /dev/null
+++ b/Swiften/FileTransfer/JingleTransport.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
+
+namespace Swift {
+ class JingleTransport {
+ public:
+ typedef boost::shared_ptr<JingleTransport> ref;
+
+ virtual ~JingleTransport();
+
+ virtual void start() = 0;
+ 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.cpp b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp
new file mode 100644
index 0000000..852902b
--- /dev/null
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+
+namespace Swift {
+
+LocalJingleTransportCandidateGenerator::~LocalJingleTransportCandidateGenerator() {
+}
+
+}
diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
new file mode 100644
index 0000000..041afe3
--- /dev/null
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <Swiften/Elements/JingleTransportPayload.h>
+#include <Swiften/FileTransfer/JingleTransport.h>
+
+namespace Swift {
+ class LocalJingleTransportCandidateGenerator {
+ public:
+ virtual ~LocalJingleTransportCandidateGenerator();
+ /**
+ * 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;
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) = 0;
+
+ boost::signal<void (JingleTransportPayload::ref)> onLocalTransportCandidatesGenerated;
+ };
+}
diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.cpp b/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.cpp
new file mode 100644
index 0000000..a1e3874
--- /dev/null
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+
+namespace Swift {
+
+LocalJingleTransportCandidateGeneratorFactory::~LocalJingleTransportCandidateGeneratorFactory() {
+}
+
+}
diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h b/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h
new file mode 100644
index 0000000..c969fc7
--- /dev/null
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+ class LocalJingleTransportCandidateGenerator;
+
+ class LocalJingleTransportCandidateGeneratorFactory {
+ public:
+ virtual ~LocalJingleTransportCandidateGeneratorFactory();
+
+ virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() = 0;
+ };
+}
diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.cpp b/Swiften/FileTransfer/OutgoingFileTransfer.cpp
index 32f7e17..94d4348 100644
--- a/Swiften/FileTransfer/OutgoingFileTransfer.cpp
+++ b/Swiften/FileTransfer/OutgoingFileTransfer.cpp
@@ -4,75 +4,11 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/OutgoingFileTransfer.h"
-
-#include <boost/bind.hpp>
-
-#include "Swiften/FileTransfer/StreamInitiationRequest.h"
-#include "Swiften/FileTransfer/BytestreamsRequest.h"
-#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h"
-#include "Swiften/FileTransfer/IBBSendSession.h"
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
namespace Swift {
-OutgoingFileTransfer::OutgoingFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer) {
-}
-
-void OutgoingFileTransfer::start() {
- StreamInitiation::ref streamInitiation(new StreamInitiation());
- streamInitiation->setID(id);
- streamInitiation->setFileInfo(StreamInitiationFileInfo(name, description, size));
- //streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams");
- streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb");
- StreamInitiationRequest::ref request = StreamInitiationRequest::create(to, streamInitiation, iqRouter);
- request->onResponse.connect(boost::bind(&OutgoingFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2));
- request->send();
-}
-
-void OutgoingFileTransfer::stop() {
-}
-
-void OutgoingFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) {
- if (error) {
- finish(FileTransferError());
- }
- else {
- if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
- socksServer->addBytestream(id, from, to, bytestream);
- Bytestreams::ref bytestreams(new Bytestreams());
- bytestreams->setStreamID(id);
- HostAddressPort addressPort = socksServer->getAddressPort();
- bytestreams->addStreamHost(Bytestreams::StreamHost(addressPort.getAddress().toString(), from, addressPort.getPort()));
- BytestreamsRequest::ref request = BytestreamsRequest::create(to, bytestreams, iqRouter);
- request->onResponse.connect(boost::bind(&OutgoingFileTransfer::handleBytestreamsRequestResponse, this, _1, _2));
- request->send();
- }
- else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") {
- ibbSession = boost::shared_ptr<IBBSendSession>(new IBBSendSession(id, to, bytestream, iqRouter));
- ibbSession->onFinished.connect(boost::bind(&OutgoingFileTransfer::handleIBBSessionFinished, this, _1));
- ibbSession->start();
- }
- }
-}
-
-void OutgoingFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) {
- if (error) {
- finish(FileTransferError());
- }
- //socksServer->onTransferFinished.connect();
-}
-
-void OutgoingFileTransfer::finish(boost::optional<FileTransferError> error) {
- if (ibbSession) {
- ibbSession->onFinished.disconnect(boost::bind(&OutgoingFileTransfer::handleIBBSessionFinished, this, _1));
- ibbSession.reset();
- }
- socksServer->removeBytestream(id, from, to);
- onFinished(error);
-}
-
-void OutgoingFileTransfer::handleIBBSessionFinished(boost::optional<FileTransferError> error) {
- finish(error);
+OutgoingFileTransfer::~OutgoingFileTransfer() {
}
}
diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.h b/Swiften/FileTransfer/OutgoingFileTransfer.h
index a694c13..1ec1ae3 100644
--- a/Swiften/FileTransfer/OutgoingFileTransfer.h
+++ b/Swiften/FileTransfer/OutgoingFileTransfer.h
@@ -8,45 +8,16 @@
#include <boost/shared_ptr.hpp>
-#include "Swiften/FileTransfer/ReadBytestream.h"
-#include "Swiften/Base/boost_bsignals.h"
-#include "Swiften/FileTransfer/FileTransferError.h"
-#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h"
-#include "Swiften/JID/JID.h"
-#include "Swiften/Elements/StreamInitiation.h"
-#include "Swiften/Elements/Bytestreams.h"
-#include "Swiften/Elements/ErrorPayload.h"
-#include "Swiften/FileTransfer/IBBSendSession.h"
+#include <Swiften/FileTransfer/FileTransfer.h>
namespace Swift {
- class IQRouter;
- class SOCKS5BytestreamServer;
-
- class OutgoingFileTransfer {
+ class OutgoingFileTransfer : public FileTransfer {
public:
- OutgoingFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer);
-
- void start();
- void stop();
-
- boost::signal<void (const boost::optional<FileTransferError>&)> onFinished;
-
- private:
- void handleStreamInitiationRequestResponse(StreamInitiation::ref, ErrorPayload::ref);
- void handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref);
- void finish(boost::optional<FileTransferError> error);
- void handleIBBSessionFinished(boost::optional<FileTransferError> error);
+ typedef boost::shared_ptr<OutgoingFileTransfer> ref;
+ public:
+ virtual ~OutgoingFileTransfer();
- private:
- std::string id;
- JID from;
- JID to;
- std::string name;
- int size;
- std::string description;
- boost::shared_ptr<ReadBytestream> bytestream;
- IQRouter* iqRouter;
- SOCKS5BytestreamServer* socksServer;
- boost::shared_ptr<IBBSendSession> ibbSession;
+ virtual void start() = 0;
+ virtual void stop() = 0;
};
}
diff --git a/Swiften/FileTransfer/OutgoingFileTransferManager.cpp b/Swiften/FileTransfer/OutgoingFileTransferManager.cpp
new file mode 100644
index 0000000..6f23bb7
--- /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(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy) : 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& from, const JID& receipient, boost::shared_ptr<ReadBytestream> readBytestream, const StreamInitiationFileInfo& fileInfo) {
+ // check if receipient support Jingle FT
+
+
+ JingleSessionImpl::ref jingleSession = boost::make_shared<JingleSessionImpl>(from, receipient, idGenerator->generateID(), iqRouter);
+
+ //jsManager->getSession(receipient, idGenerator->generateID());
+ assert(jingleSession);
+ jsManager->registerOutgoingSession(from, jingleSession);
+ boost::shared_ptr<OutgoingJingleFileTransfer> jingleFT = boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, from, 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..c686001
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransferManager.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 <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(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy);
+ ~OutgoingFileTransferManager();
+
+ boost::shared_ptr<OutgoingFileTransfer> createOutgoingFileTransfer(const JID& from, const JID& to, boost::shared_ptr<ReadBytestream>, const StreamInitiationFileInfo&);
+
+private:
+ 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..5e2a1c3
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp
@@ -0,0 +1,402 @@
+/*
+ * 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& fromJID,
+ 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), fromJID(fromJID), 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(), fromJID, 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(), fromJID, 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..ff7bfc7
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.h
@@ -0,0 +1,117 @@
+/*
+ * 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& from,
+ const JID& to,
+ 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 fromJID;
+ 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
new file mode 100644
index 0000000..8a8237a
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/OutgoingSIFileTransfer.h>
+
+#include <boost/bind.hpp>
+
+#include <Swiften/FileTransfer/StreamInitiationRequest.h>
+#include <Swiften/FileTransfer/BytestreamsRequest.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
+#include <Swiften/FileTransfer/IBBSendSession.h>
+
+namespace Swift {
+
+OutgoingSIFileTransfer::OutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer) {
+}
+
+void OutgoingSIFileTransfer::start() {
+ StreamInitiation::ref streamInitiation(new StreamInitiation());
+ streamInitiation->setID(id);
+ streamInitiation->setFileInfo(StreamInitiationFileInfo(name, description, size));
+ //streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams");
+ streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb");
+ StreamInitiationRequest::ref request = StreamInitiationRequest::create(to, streamInitiation, iqRouter);
+ request->onResponse.connect(boost::bind(&OutgoingSIFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2));
+ request->send();
+}
+
+void OutgoingSIFileTransfer::stop() {
+}
+
+void OutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) {
+ if (error) {
+ finish(FileTransferError());
+ }
+ else {
+ if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
+ socksServer->addReadBytestream(id, from, to, bytestream);
+ Bytestreams::ref bytestreams(new Bytestreams());
+ bytestreams->setStreamID(id);
+ HostAddressPort addressPort = socksServer->getAddressPort();
+ bytestreams->addStreamHost(Bytestreams::StreamHost(addressPort.getAddress().toString(), from, addressPort.getPort()));
+ BytestreamsRequest::ref request = BytestreamsRequest::create(to, bytestreams, iqRouter);
+ request->onResponse.connect(boost::bind(&OutgoingSIFileTransfer::handleBytestreamsRequestResponse, this, _1, _2));
+ request->send();
+ }
+ else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") {
+ ibbSession = boost::shared_ptr<IBBSendSession>(new IBBSendSession(id, from, to, bytestream, iqRouter));
+ ibbSession->onFinished.connect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
+ ibbSession->start();
+ }
+ }
+}
+
+void OutgoingSIFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) {
+ if (error) {
+ finish(FileTransferError());
+ }
+ //socksServer->onTransferFinished.connect();
+}
+
+void OutgoingSIFileTransfer::finish(boost::optional<FileTransferError> error) {
+ if (ibbSession) {
+ ibbSession->onFinished.disconnect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
+ ibbSession.reset();
+ }
+ socksServer->removeReadBytestream(id, from, to);
+ onFinished(error);
+}
+
+void OutgoingSIFileTransfer::handleIBBSessionFinished(boost::optional<FileTransferError> error) {
+ finish(error);
+}
+
+}
diff --git a/Swiften/FileTransfer/OutgoingSIFileTransfer.h b/Swiften/FileTransfer/OutgoingSIFileTransfer.h
new file mode 100644
index 0000000..584eb60
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingSIFileTransfer.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/StreamInitiation.h>
+#include <Swiften/Elements/Bytestreams.h>
+#include <Swiften/Elements/ErrorPayload.h>
+#include <Swiften/FileTransfer/IBBSendSession.h>
+
+namespace Swift {
+ class IQRouter;
+ class SOCKS5BytestreamServer;
+
+ class OutgoingSIFileTransfer : public OutgoingFileTransfer {
+ public:
+ OutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer);
+
+ virtual void start();
+ virtual void stop();
+
+ boost::signal<void (const boost::optional<FileTransferError>&)> onFinished;
+
+ private:
+ void handleStreamInitiationRequestResponse(StreamInitiation::ref, ErrorPayload::ref);
+ void handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref);
+ void finish(boost::optional<FileTransferError> error);
+ void handleIBBSessionFinished(boost::optional<FileTransferError> error);
+
+ private:
+ std::string id;
+ JID from;
+ JID to;
+ std::string name;
+ int size;
+ std::string description;
+ boost::shared_ptr<ReadBytestream> bytestream;
+ IQRouter* iqRouter;
+ SOCKS5BytestreamServer* socksServer;
+ boost::shared_ptr<IBBSendSession> ibbSession;
+ };
+}
diff --git a/Swiften/FileTransfer/ReadBytestream.cpp b/Swiften/FileTransfer/ReadBytestream.cpp
index 705906c..5fa10d8 100644
--- a/Swiften/FileTransfer/ReadBytestream.cpp
+++ b/Swiften/FileTransfer/ReadBytestream.cpp
@@ -4,7 +4,7 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/ReadBytestream.h"
+#include <Swiften/FileTransfer/ReadBytestream.h>
namespace Swift {
diff --git a/Swiften/FileTransfer/ReadBytestream.h b/Swiften/FileTransfer/ReadBytestream.h
index 4da2bc2..c94e4d3 100644
--- a/Swiften/FileTransfer/ReadBytestream.h
+++ b/Swiften/FileTransfer/ReadBytestream.h
@@ -6,13 +6,26 @@
#pragma once
-#include "Swiften/Base/ByteArray.h"
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+#include <Swiften/Base/boost_bsignals.h>
namespace Swift {
class ReadBytestream {
public:
virtual ~ReadBytestream();
- virtual ByteArray read(size_t size) = 0;
+
+ /**
+ * Return an empty vector if no more data is available.
+ * Use onDataAvailable signal for signaling there is data available again.
+ */
+ virtual boost::shared_ptr< std::vector<unsigned char> > read(size_t size) = 0;
+
virtual bool isFinished() const = 0;
+
+ public:
+ boost::signal<void ()> onDataAvailable;
+ boost::signal<void (const std::vector<unsigned char>&)> onRead;
};
}
diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.cpp b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.cpp
new file mode 100644
index 0000000..338f221
--- /dev/null
+++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+
+namespace Swift {
+
+RemoteJingleTransportCandidateSelector::~RemoteJingleTransportCandidateSelector() {
+}
+
+}
diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
new file mode 100644
index 0000000..f8df8f9
--- /dev/null
+++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#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 {
+ public:
+ virtual ~RemoteJingleTransportCandidateSelector();
+
+ 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;
+ virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) = 0;
+
+ boost::signal<void (JingleTransportPayload::ref)> onRemoteTransportCandidateSelectFinished;
+ };
+}
diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.cpp b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.cpp
new file mode 100644
index 0000000..36b7cba
--- /dev/null
+++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+
+namespace Swift {
+
+RemoteJingleTransportCandidateSelectorFactory::~RemoteJingleTransportCandidateSelectorFactory() {
+}
+
+}
diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h
new file mode 100644
index 0000000..caa3097
--- /dev/null
+++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+ class RemoteJingleTransportCandidateSelector;
+
+ class RemoteJingleTransportCandidateSelectorFactory {
+ public:
+ virtual ~RemoteJingleTransportCandidateSelectorFactory();
+
+ virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() = 0;
+ };
+}
diff --git a/Swiften/FileTransfer/SConscript b/Swiften/FileTransfer/SConscript
index ea9e7bb..4e79992 100644
--- a/Swiften/FileTransfer/SConscript
+++ b/Swiften/FileTransfer/SConscript
@@ -1,19 +1,48 @@
-Import("swiften_env")
+Import("swiften_env", "env")
sources = [
"OutgoingFileTransfer.cpp",
+ "OutgoingSIFileTransfer.cpp",
+ "OutgoingJingleFileTransfer.cpp",
+ "OutgoingFileTransferManager.cpp",
"IncomingFileTransfer.cpp",
"IncomingJingleFileTransfer.cpp",
- "IncomingFileTransferManager.cpp",
+ "IncomingFileTransferManager.cpp",
+ "RemoteJingleTransportCandidateSelector.cpp",
+ "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",
+ "SOCKS5BytestreamProxyFinder.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..cd555e5
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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>
+#include <Swiften/Base/ByteArray.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(&vecptr(unprocessedData)[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 {
+ boost::shared_ptr<ByteArray> dataToSend = readBytestream->read(chunkSize);
+ connection->write(createSafeByteArray(*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(boost::shared_ptr<SafeByteArray> data) {
+ SWIFT_LOG(debug) << "state: " << state << " data.size() = " << data->size() << std::endl;
+ if (state != Reading) {
+ append(unprocessedData, *data);
+ process();
+ }
+ else {
+ writeBytestream->write(createByteArray(vecptr(*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..ea45955
--- /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(boost::shared_ptr<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/SOCKS5BytestreamProxyFinder.cpp b/Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.cpp
new file mode 100644
index 0000000..9d7505b
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/Queries/IQRouter.h>
+
+namespace Swift {
+
+SOCKS5BytestreamProxyFinder::SOCKS5BytestreamProxyFinder(const JID& service, IQRouter *iqRouter) : service(service), iqRouter(iqRouter) {
+}
+
+SOCKS5BytestreamProxyFinder::~SOCKS5BytestreamProxyFinder() {
+}
+
+void SOCKS5BytestreamProxyFinder::start() {
+ serviceWalker = boost::make_shared<DiscoServiceWalker>(service, iqRouter);
+ serviceWalker->onServiceFound.connect(boost::bind(&SOCKS5BytestreamProxyFinder::handleServiceFound, this, _1, _2));
+ serviceWalker->beginWalk();
+}
+
+void SOCKS5BytestreamProxyFinder::stop() {
+ serviceWalker->endWalk();
+ serviceWalker->onServiceFound.disconnect(boost::bind(&SOCKS5BytestreamProxyFinder::handleServiceFound, this, _1, _2));
+ serviceWalker.reset();
+}
+
+void SOCKS5BytestreamProxyFinder::sendBytestreamQuery(const JID& jid) {
+ S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+ boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Get, jid, proxyRequest, iqRouter);
+ request->onResponse.connect(boost::bind(&SOCKS5BytestreamProxyFinder::handleProxyResponse, this, _1, _2));
+ request->send();
+}
+
+void SOCKS5BytestreamProxyFinder::handleServiceFound(const JID& jid, boost::shared_ptr<DiscoInfo> discoInfo) {
+ if (discoInfo->hasFeature(DiscoInfo::Bytestream)) {
+ sendBytestreamQuery(jid);
+ }
+}
+
+void SOCKS5BytestreamProxyFinder::handleProxyResponse(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error) {
+ if (error) {
+ SWIFT_LOG(debug) << "ERROR" << std::endl;
+ } else {
+ if (request) {
+ onProxyFound(request);
+ } else {
+ //assert(false);
+ }
+ }
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.h b/Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.h
new file mode 100644
index 0000000..071e03a
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.h
@@ -0,0 +1,42 @@
+/*
+ * 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/Disco/DiscoServiceWalker.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+
+namespace Swift {
+
+class JID;
+class IQRouter;
+
+class SOCKS5BytestreamProxyFinder {
+public:
+ SOCKS5BytestreamProxyFinder(const JID& service, IQRouter *iqRouter);
+ ~SOCKS5BytestreamProxyFinder();
+
+ void start();
+ void stop();
+
+ boost::signal<void(boost::shared_ptr<S5BProxyRequest>)> onProxyFound;
+
+private:
+ void sendBytestreamQuery(const JID&);
+
+ void handleServiceFound(const JID&, boost::shared_ptr<DiscoInfo>);
+ void handleProxyResponse(boost::shared_ptr<S5BProxyRequest>, ErrorPayload::ref);
+private:
+ JID service;
+ IQRouter* iqRouter;
+ boost::shared_ptr<DiscoServiceWalker> serviceWalker;
+ std::vector<boost::shared_ptr<GenericRequest<S5BProxyRequest> > > requests;
+};
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
index 7f889b1..ffc4298 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
@@ -4,29 +4,69 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h"
+#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 7cee256..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 "Swiften/FileTransfer/ReadBytestream.h"
+#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 9bc49ae..90fed7a 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
@@ -4,17 +4,19 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h"
+#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
#include <boost/bind.hpp>
-#include "Swiften/StringCodecs/Hexify.h"
-#include "Swiften/StringCodecs/SHA1.h"
-#include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h"
+#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,20 +27,20 @@ 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) {
- return Hexify::hexify(SHA1::getHash(ByteArray(id + from.toString() + to.toString())));
+ return Hexify::hexify(SHA1::getHash(createByteArray(id + from.toString() + to.toString())));
}
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 d5a62bb..6bb598e 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServer.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
@@ -9,26 +9,26 @@
#include <boost/shared_ptr.hpp>
#include <map>
-#include "Swiften/Network/ConnectionServer.h"
+#include <Swiften/Network/ConnectionServer.h>
#include <string>
-#include "Swiften/JID/JID.h"
-#include "Swiften/FileTransfer/ReadBytestream.h"
-#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h"
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
namespace Swift {
class SOCKS5BytestreamServerSession;
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 9951f7a..f660fda 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
@@ -4,17 +4,23 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h"
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
#include <boost/bind.hpp>
+#include <iostream>
-#include "Swiften/Base/ByteArray.h"
-#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h"
-#include "Swiften/FileTransfer/BytestreamException.h"
+#include <Swiften/Base/ByteArray.h>
+#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() {
@@ -25,68 +31,111 @@ 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;
+ }
}
-void SOCKS5BytestreamServerSession::handleDataRead(const ByteArray& data) {
- unprocessedData += data;
- process();
+HostAddressPort SOCKS5BytestreamServerSession::getAddressPort() const {
+ return connection->getLocalAddress();
+}
+
+void SOCKS5BytestreamServerSession::handleDataRead(boost::shared_ptr<SafeByteArray> data) {
+ if (state != ReadingData) {
+ append(unprocessedData, *data);
+ process();
+ } else {
+ writeBytestream->write(createByteArray(vecptr(*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() {
if (state == WaitingForAuthentication) {
- if (unprocessedData.getSize() >= 2) {
+ if (unprocessedData.size() >= 2) {
size_t authCount = unprocessedData[1];
size_t i = 2;
- while (i < 2 + authCount && i < unprocessedData.getSize()) {
+ while (i < 2 + authCount && i < unprocessedData.size()) {
// Skip authentication mechanism
++i;
}
if (i == 2 + authCount) {
// Authentication message is complete
- if (i != unprocessedData.getSize()) {
- std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+ if (i != unprocessedData.size()) {
+ SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl;
}
unprocessedData.clear();
- connection->write(ByteArray("\x05\x00", 2));
+ connection->write(createSafeByteArray("\x05\x00", 2));
state = WaitingForRequest;
}
}
}
else if (state == WaitingForRequest) {
- if (unprocessedData.getSize() >= 5) {
+ if (unprocessedData.size() >= 5) {
ByteArray requestID;
size_t i = 5;
size_t hostnameSize = unprocessedData[4];
- while (i < 5 + hostnameSize && i < unprocessedData.getSize()) {
- requestID += unprocessedData[i];
+ while (i < 5 + hostnameSize && i < unprocessedData.size()) {
+ 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.getSize()) {
- if (i != unprocessedData.getSize()) {
- std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+ if (i <= unprocessedData.size()) {
+ if (i != unprocessedData.size()) {
+ SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl;
}
- bytestream = bytestreams->getBytestream(requestID.toString());
- ByteArray result("\x05", 1);
- result += bytestream ? 0x0 : 0x4;
- result += ByteArray("\x00\x03", 2);
- result += static_cast<char>(requestID.getSize());
- result += requestID + ByteArray("\x00\x00", 2);
- if (!bytestream) {
+ unprocessedData.clear();
+ std::string streamID = byteArrayToString(requestID);
+ readBytestream = bytestreams->getReadBytestream(streamID);
+ writeBytestream = bytestreams->getWriteBytestream(streamID);
+ SafeByteArray result = createSafeByteArray("\x05", 1);
+ 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 (!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;
}
}
}
@@ -94,11 +143,13 @@ void SOCKS5BytestreamServerSession::process() {
}
void SOCKS5BytestreamServerSession::sendData() {
- if (!bytestream->isFinished()) {
+ if (!readBytestream->isFinished()) {
try {
- connection->write(bytestream->read(chunkSize));
+ SafeByteArray dataToSend = createSafeByteArray(*readBytestream->read(chunkSize));
+ connection->write(dataToSend);
+ onBytesSent(dataToSend.size());
}
- catch (const BytestreamException& e) {
+ catch (const BytestreamException&) {
finish(true);
}
}
@@ -110,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 f430f5d..4557a36 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
@@ -8,20 +8,27 @@
#include <boost/shared_ptr.hpp>
-#include "Swiften/Base/boost_bsignals.h"
-#include "Swiften/Network/Connection.h"
-#include "Swiften/FileTransfer/ReadBytestream.h"
+#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 ByteArray&);
+ void handleDataRead(boost::shared_ptr<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/StreamInitiationRequest.h b/Swiften/FileTransfer/StreamInitiationRequest.h
index f516d4b..658a8a9 100644
--- a/Swiften/FileTransfer/StreamInitiationRequest.h
+++ b/Swiften/FileTransfer/StreamInitiationRequest.h
@@ -6,8 +6,8 @@
#pragma once
-#include "Swiften/Queries/GenericRequest.h"
-#include "Swiften/Elements/StreamInitiation.h"
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/Elements/StreamInitiation.h>
namespace Swift {
@@ -19,8 +19,15 @@ namespace Swift {
return ref(new StreamInitiationRequest(jid, payload, router));
}
+ static ref create(const JID& from, const JID& to, boost::shared_ptr<StreamInitiation> payload, IQRouter* router) {
+ return ref(new StreamInitiationRequest(from, to, payload, router));
+ }
+
private:
StreamInitiationRequest(const JID& jid, boost::shared_ptr<StreamInitiation> payload, IQRouter* router) : GenericRequest<StreamInitiation>(IQ::Set, jid, payload, router) {
}
+
+ StreamInitiationRequest(const JID& from, const JID& to, boost::shared_ptr<StreamInitiation> payload, IQRouter* router) : GenericRequest<StreamInitiation>(IQ::Set, from, to, payload, router) {
+ }
};
}
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/IBBReceiveSessionTest.cpp b/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp
new file mode 100644
index 0000000..c62636d
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <vector>
+#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/FileTransfer/IBBReceiveSession.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+
+using namespace Swift;
+
+class IBBReceiveSessionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(IBBReceiveSessionTest);
+ CPPUNIT_TEST(testOpen);
+ CPPUNIT_TEST(testReceiveData);
+ CPPUNIT_TEST(testReceiveMultipleData);
+ CPPUNIT_TEST(testReceiveDataForOtherSession);
+ CPPUNIT_TEST(testReceiveDataOutOfOrder);
+ CPPUNIT_TEST(testReceiveLastData);
+ CPPUNIT_TEST(testReceiveClose);
+ CPPUNIT_TEST(testStopWhileActive);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ stanzaChannel = new DummyStanzaChannel();
+ iqRouter = new IQRouter(stanzaChannel);
+ finished = false;
+ }
+
+ void tearDown() {
+ delete iqRouter;
+ delete stanzaChannel;
+ }
+
+ void testOpen() {
+ boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession"));
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+
+ CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(0, "id-open"));
+ CPPUNIT_ASSERT(!finished);
+
+ testling->stop();
+ }
+
+ void testReceiveData() {
+ boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession"));
+ testling->start();
+ 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(stanzaChannel->isResultAtIndex(1, "id-a"));
+ CPPUNIT_ASSERT(createByteArray("abc") == receivedData);
+ CPPUNIT_ASSERT(!finished);
+
+ testling->stop();
+ }
+
+ void testReceiveMultipleData() {
+ boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession"));
+ testling->start();
+ 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"));
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 1, createByteArray("def")), "foo@bar.com/baz", "id-b"));
+
+ CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(2, "id-b"));
+ CPPUNIT_ASSERT(createByteArray("abcdef") == receivedData);
+ CPPUNIT_ASSERT(!finished);
+
+ testling->stop();
+ }
+
+ void testReceiveDataForOtherSession() {
+ boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession"));
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("othersession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a"));
+
+ CPPUNIT_ASSERT(stanzaChannel->isErrorAtIndex(1, "id-a"));
+
+ testling->stop();
+ }
+
+ void testReceiveDataOutOfOrder() {
+ boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession"));
+ testling->start();
+ 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"));
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("def")), "foo@bar.com/baz", "id-b"));
+
+ CPPUNIT_ASSERT(stanzaChannel->isErrorAtIndex(2, "id-b"));
+ CPPUNIT_ASSERT(finished);
+ CPPUNIT_ASSERT(error);
+
+ testling->stop();
+ }
+
+ void testReceiveLastData() {
+ boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession", 6));
+ testling->start();
+ 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"));
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 1, createByteArray("def")), "foo@bar.com/baz", "id-b"));
+
+ CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(2, "id-b"));
+ CPPUNIT_ASSERT(createByteArray("abcdef") == receivedData);
+ CPPUNIT_ASSERT(finished);
+ CPPUNIT_ASSERT(!error);
+
+ testling->stop();
+ }
+
+ void testReceiveClose() {
+ boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession"));
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBClose("mysession"), "foo@bar.com/baz", "id-close"));
+
+ CPPUNIT_ASSERT(finished);
+ CPPUNIT_ASSERT(error);
+
+ testling->stop();
+ }
+
+ void testStopWhileActive() {
+ boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession"));
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+
+ testling->stop();
+
+ CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(1, JID("foo@bar.com/baz"), IQ::Set));
+ IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload<IBB>();
+ CPPUNIT_ASSERT_EQUAL(IBB::Close, ibb->getAction());
+ CPPUNIT_ASSERT_EQUAL(std::string("mysession"), ibb->getStreamID());
+ CPPUNIT_ASSERT(finished);
+ CPPUNIT_ASSERT(!error);
+ }
+
+ private:
+ IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) {
+ IQ::ref request = IQ::createRequest(IQ::Set, JID("baz@fum.com/dum"), id, ibb);
+ request->setFrom(from);
+ return request;
+ }
+
+ IBBReceiveSession* createSession(const std::string& from, const std::string& id, size_t size = 0x1000) {
+ IBBReceiveSession* session = new IBBReceiveSession(id, JID(from), JID(), size, iqRouter);
+ session->onDataReceived.connect(boost::bind(&IBBReceiveSessionTest::handleDataReceived, this, _1));
+ session->onFinished.connect(boost::bind(&IBBReceiveSessionTest::handleFinished, this, _1));
+ return session;
+ }
+
+
+ void handleFinished(boost::optional<FileTransferError> error) {
+ finished = true;
+ this->error = error;
+ }
+
+ void handleDataReceived(const std::vector<unsigned char>& data) {
+ receivedData.insert(receivedData.end(), data.begin(), data.end());
+ }
+
+ private:
+ DummyStanzaChannel* stanzaChannel;
+ IQRouter* iqRouter;
+ bool finished;
+ boost::optional<FileTransferError> error;
+ std::vector<unsigned char> receivedData;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IBBReceiveSessionTest);
diff --git a/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp b/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp
index e89ef93..d12f99e 100644
--- a/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp
+++ b/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp
@@ -4,17 +4,16 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/Base/ByteArray.h"
-
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <vector>
#include <boost/bind.hpp>
-#include "Swiften/FileTransfer/IBBSendSession.h"
-#include "Swiften/FileTransfer/ByteArrayReadBytestream.h"
-#include "Swiften/Queries/IQRouter.h"
-#include "Swiften/Client/DummyStanzaChannel.h"
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/FileTransfer/IBBSendSession.h>
+#include <Swiften/FileTransfer/ByteArrayReadBytestream.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
using namespace Swift;
@@ -27,13 +26,19 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testErrorResponseFinishesWithError);
CPPUNIT_TEST(testStopDuringSessionCloses);
CPPUNIT_TEST(testStopAfterFinishedDoesNotClose);
+ CPPUNIT_TEST(testDataStreamPauseStopsSendingData);
+ CPPUNIT_TEST(testDataStreamResumeAfterPauseSendsData);
+ CPPUNIT_TEST(testDataStreamResumeBeforePauseDoesNotSendData);
+ CPPUNIT_TEST(testDataStreamResumeAfterResumeDoesNotSendData);
+
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
stanzaChannel = new DummyStanzaChannel();
iqRouter = new IQRouter(stanzaChannel);
- bytestream = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray("abcdefg")));
+ bytestream = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg")));
+ finished = false;
}
void tearDown() {
@@ -42,7 +47,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
}
void testStart() {
- std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
testling->setBlockSize(1234);
testling->start();
@@ -56,7 +61,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
}
void testStart_ResponseStartsSending() {
- std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
testling->setBlockSize(3);
testling->start();
@@ -66,13 +71,13 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(1, JID("foo@bar.com/baz"), IQ::Set));
IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload<IBB>();
CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction());
- CPPUNIT_ASSERT_EQUAL(ByteArray("abc"), ibb->getData());
+ CPPUNIT_ASSERT(createByteArray("abc") == ibb->getData());
CPPUNIT_ASSERT_EQUAL(0, ibb->getSequenceNumber());
CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID());
}
void testResponseContinuesSending() {
- std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
testling->setBlockSize(3);
testling->start();
stanzaChannel->onIQReceived(createIBBResult());
@@ -82,13 +87,13 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(2, JID("foo@bar.com/baz"), IQ::Set));
IBB::ref ibb = stanzaChannel->sentStanzas[2]->getPayload<IBB>();
CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction());
- CPPUNIT_ASSERT_EQUAL(ByteArray("def"), ibb->getData());
+ CPPUNIT_ASSERT(createByteArray("def") == ibb->getData());
CPPUNIT_ASSERT_EQUAL(1, ibb->getSequenceNumber());
CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID());
}
void testRespondToAllFinishes() {
- std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
testling->setBlockSize(3);
testling->start();
stanzaChannel->onIQReceived(createIBBResult());
@@ -101,7 +106,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
}
void testErrorResponseFinishesWithError() {
- std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
testling->setBlockSize(3);
testling->start();
stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID()));
@@ -111,7 +116,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
}
void testStopDuringSessionCloses() {
- std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
testling->setBlockSize(3);
testling->start();
testling->stop();
@@ -126,7 +131,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
}
void testStopAfterFinishedDoesNotClose() {
- std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
testling->setBlockSize(16);
testling->start();
stanzaChannel->onIQReceived(createIBBResult());
@@ -137,15 +142,74 @@ class IBBSendSessionTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(stanzaChannel->sentStanzas.size()));
}
-
+
+ void testDataStreamPauseStopsSendingData() {
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ bytestream->setDataComplete(false);
+ testling->setBlockSize(3);
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+
+ CPPUNIT_ASSERT(!finished);
+ CPPUNIT_ASSERT(!error);
+
+ CPPUNIT_ASSERT_EQUAL(4, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ }
+
+ void testDataStreamResumeAfterPauseSendsData() {
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ bytestream->setDataComplete(false);
+ testling->setBlockSize(3);
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+
+ bytestream->addData(createByteArray("xyz"));
+
+ CPPUNIT_ASSERT_EQUAL(5, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ }
+
+ void testDataStreamResumeBeforePauseDoesNotSendData() {
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ bytestream->setDataComplete(false);
+ testling->setBlockSize(3);
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBResult());
+
+ bytestream->addData(createByteArray("xyz"));
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ }
+
+ void testDataStreamResumeAfterResumeDoesNotSendData() {
+ boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz");
+ bytestream->setDataComplete(false);
+ testling->setBlockSize(3);
+ testling->start();
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+ stanzaChannel->onIQReceived(createIBBResult());
+
+ bytestream->addData(createByteArray("xyz"));
+ bytestream->addData(createByteArray("xuv"));
+
+ CPPUNIT_ASSERT_EQUAL(5, static_cast<int>(stanzaChannel->sentStanzas.size()));
+ }
+
private:
IQ::ref createIBBResult() {
return IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getTo(), stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getID(), boost::shared_ptr<IBB>());
}
private:
- std::auto_ptr<IBBSendSession> createSession(const std::string& to) {
- std::auto_ptr<IBBSendSession> session(new IBBSendSession("myid", JID(to), bytestream, iqRouter));
+ boost::shared_ptr<IBBSendSession> createSession(const std::string& to) {
+ boost::shared_ptr<IBBSendSession> session(new IBBSendSession("myid", JID(), JID(to), bytestream, iqRouter));
session->onFinished.connect(boost::bind(&IBBSendSessionTest::handleFinished, this, _1));
return session;
}
diff --git a/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp
new file mode 100644
index 0000000..4c6ae72
--- /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("foo.txt", "", 10));
+ 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("file.txt", "", 10));
+ 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("file.txt", "", 10));
+ 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..0c324bf
--- /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, JID(), 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..527e0ca
--- /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(createSafeByteArrayRef(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);
+ 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(createSafeByteArrayRef("\x05\00", 2));
+ }
+
+ void serverRespondHelloAuthFail() {
+ connection->onDataRead(createSafeByteArrayRef("\x05\xFF", 2));
+ }
+
+ void serverRespondRequestOK() {
+ boost::shared_ptr<SafeByteArray> dataToSend = createSafeByteArrayRef("\x05\x00\x00\x03", 4);
+ append(*dataToSend, createSafeByteArray(destination.size()));
+ append(*dataToSend, createSafeByteArray(destination));
+ append(*dataToSend, createSafeByteArray("\x00", 1));
+ connection->onDataRead(dataToSend);
+ }
+
+ void serverRespondRequestFail() {
+ boost::shared_ptr<SafeByteArray> correctData = createSafeByteArrayRef("\x05\x00\x00\x03", 4);
+ append(*correctData, createSafeByteArray(destination.size()));
+ append(*correctData, createSafeByteArray(destination));
+ append(*correctData, createSafeByteArray("\x00", 1));
+
+ boost::shared_ptr<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 = createSafeByteArrayRef(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 c6d246d..4fe72c0 100644
--- a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
+++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
@@ -4,18 +4,19 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/Base/ByteArray.h"
+#include <Swiften/Base/ByteArray.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <boost/bind.hpp>
-#include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h"
-#include "Swiften/FileTransfer/ByteArrayReadBytestream.h"
-#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h"
-#include "Swiften/Network/DummyConnection.h"
-#include "Swiften/EventLoop/DummyEventLoop.h"
-#include "Swiften/Base/StartStopper.h"
+#include <Swiften/Base/Concat.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/FileTransfer/ByteArrayReadBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/Network/DummyConnection.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Base/StartStopper.h>
using namespace Swift;
@@ -33,122 +34,127 @@ 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(ByteArray("abcdefg")));
+ stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg")));
}
void tearDown() {
connection.reset();
+ delete bytestreams;
delete eventLoop;
}
void testAuthenticate() {
- std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- receive(ByteArray("\x05\x02\x01\x02"));
+ receive(createSafeByteArray("\x05\x02\x01\x02"));
- CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00", 2), receivedData);
+ CPPUNIT_ASSERT(createByteArray("\x05\x00", 2) == receivedData);
}
void testAuthenticate_Chunked() {
- std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- receive(ByteArray("\x05\x02\x01"));
+ receive(createSafeByteArray("\x05\x02\x01"));
- CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedData.getSize()));
- receive(ByteArray("\x01"));
- CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00", 2), receivedData);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedData.size()));
+ receive(createSafeByteArray("\x01"));
+ CPPUNIT_ASSERT(createByteArray("\x05\x00", 2) == receivedData);
}
void testRequest() {
- std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
- bytestreams.addBytestream("abcdef", stream1);
+ bytestreams->addReadBytestream("abcdef", stream1);
authenticate();
- ByteArray hostname("abcdef");
- receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getSize() + hostname + ByteArray("\x00\x00", 2));
- CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13), ByteArray(receivedData.getData(), 13));
+ ByteArray hostname(createByteArray("abcdef"));
+ receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2)));
+ CPPUNIT_ASSERT(createByteArray("\x05\x00\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13) == createByteArray(&receivedData[0], 13));
}
void testRequest_UnknownBytestream() {
- std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
authenticate();
- ByteArray hostname("abcdef");
- receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getSize() + hostname + ByteArray("\x00\x00", 2));
- CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x04\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13), receivedData);
+ ByteArray hostname(createByteArray("abcdef"));
+ receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2)));
+ CPPUNIT_ASSERT(createByteArray("\x05\x04\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13) == receivedData);
}
void testReceiveData() {
- std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ 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");
+ eventLoop->processEvents();
- CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefg"), receivedData);
+ CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData);
CPPUNIT_ASSERT_EQUAL(2, receivedDataChunks);
}
void testReceiveData_Chunked() {
- std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());
+ 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_EQUAL(ByteArray("abcdefg"), receivedData);
+ CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData);
CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks);
}
private:
- void receive(const ByteArray& data) {
+ void receive(const SafeByteArray& data) {
connection->receive(data);
eventLoop->processEvents();
}
void authenticate() {
- receive(ByteArray("\x05\x02\x01\x02"));
+ receive(createSafeByteArray("\x05\x02\x01\x02"));
receivedData.clear();
receivedDataChunks = 0;
}
void request(const std::string& hostname) {
- receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.size() + hostname + ByteArray("\x00\x00", 2));
+ receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2)));
}
void skipHeader(const std::string& hostname) {
int headerSize = 7 + hostname.size();
- receivedData = ByteArray(receivedData.getData() + headerSize, receivedData.getSize() - headerSize);
+ receivedData = createByteArray(&receivedData[headerSize], receivedData.size() - headerSize);
}
- void handleDataWritten(const ByteArray& data) {
- receivedData += data;
+ void handleDataWritten(const SafeByteArray& data) {
+ receivedData.insert(receivedData.end(), data.begin(), data.end());
receivedDataChunks++;
}
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;
- ByteArray receivedData;
+ std::vector<unsigned char> receivedData;
int receivedDataChunks;
boost::shared_ptr<ByteArrayReadBytestream> stream1;
};
diff --git a/Swiften/FileTransfer/WriteBytestream.cpp b/Swiften/FileTransfer/WriteBytestream.cpp
index f1a5afc..8d10193 100644
--- a/Swiften/FileTransfer/WriteBytestream.cpp
+++ b/Swiften/FileTransfer/WriteBytestream.cpp
@@ -4,7 +4,7 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swiften/FileTransfer/WriteBytestream.h"
+#include <Swiften/FileTransfer/WriteBytestream.h>
namespace Swift {
diff --git a/Swiften/FileTransfer/WriteBytestream.h b/Swiften/FileTransfer/WriteBytestream.h
index 1dc791c..fb6f2f1 100644
--- a/Swiften/FileTransfer/WriteBytestream.h
+++ b/Swiften/FileTransfer/WriteBytestream.h
@@ -7,8 +7,9 @@
#pragma once
#include <boost/shared_ptr.hpp>
+#include <vector>
-#include "Swiften/Base/ByteArray.h"
+#include <Swiften/Base/boost_bsignals.h>
namespace Swift {
class WriteBytestream {
@@ -17,6 +18,8 @@ namespace Swift {
virtual ~WriteBytestream();
- virtual void write(const ByteArray&) = 0;
+ virtual void write(const std::vector<unsigned char>&) = 0;
+
+ boost::signal<void (const std::vector<unsigned char>&)> onWrite;
};
}