diff options
author | Tobias Markmann <tm@ayena.de> | 2016-02-11 14:50:37 (GMT) |
---|---|---|
committer | Kevin Smith <kevin.smith@isode.com> | 2016-02-15 13:05:18 (GMT) |
commit | 75703db2de5bbfb6622286600362016edb42dfb0 (patch) | |
tree | 2520ed777286c6b732e756387ca88d49b9c1814e | |
parent | ca226e7bb019308db4bfc818d7e04422d9d28106 (diff) | |
download | swift-75703db2de5bbfb6622286600362016edb42dfb0.zip swift-75703db2de5bbfb6622286600362016edb42dfb0.tar.bz2 |
Support early IBB use in Jingle File Transfer
Previously Jingle File Transfer in Swiften only used IBB
transport as fallback mechanism. With this patch Swiften will
use IBB transport candidates directly in the first
session-initate/session-accept message if the other party
only supports IBB.
Fixed a ASAN reported heap-use-after-free in
SOCKS5BytestreamServerManager.cpp while testing.
Test-Information:
./scons test=system passed without error.
Testing all sender/receiver file-transfer option configurations
with FileTransferTest resulting in expected behavior.
Successfully transferring a file between two Swift instances.
Change-Id: Ia0ffeaa1fd54fc0da23db75344c9e94f9d03a774
7 files changed, 98 insertions, 38 deletions
diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.cpp b/Swiften/FileTransfer/IncomingFileTransferManager.cpp index c1cc757..f5b95ec 100644 --- a/Swiften/FileTransfer/IncomingFileTransferManager.cpp +++ b/Swiften/FileTransfer/IncomingFileTransferManager.cpp @@ -1,66 +1,66 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/FileTransfer/IncomingFileTransferManager.h> #include <boost/smart_ptr/make_shared.hpp> #include <Swiften/Base/Log.h> #include <Swiften/Elements/JingleDescription.h> #include <Swiften/Elements/JingleFileTransferDescription.h> #include <Swiften/Elements/JingleIBBTransportPayload.h> #include <Swiften/Elements/JingleS5BTransportPayload.h> #include <Swiften/FileTransfer/IncomingJingleFileTransfer.h> #include <Swiften/Jingle/Jingle.h> #include <Swiften/Jingle/JingleSessionManager.h> namespace Swift { IncomingFileTransferManager::IncomingFileTransferManager( JingleSessionManager* jingleSessionManager, FileTransferTransporterFactory* transporterFactory, TimerFactory* timerFactory, CryptoProvider* crypto) : jingleSessionManager(jingleSessionManager), transporterFactory(transporterFactory), timerFactory(timerFactory), crypto(crypto) { jingleSessionManager->addIncomingSessionHandler(this); } IncomingFileTransferManager::~IncomingFileTransferManager() { jingleSessionManager->removeIncomingSessionHandler(this); } 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<JingleS5BTransportPayload>()) { + if (content->getTransport<JingleS5BTransportPayload>() || content->getTransport<JingleIBBTransportPayload>()) { JingleFileTransferDescription::ref description = content->getDescription<JingleFileTransferDescription>(); if (description) { IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>( recipient, session, content, transporterFactory, timerFactory, crypto); onIncomingFileTransfer(transfer); } else { SWIFT_LOG(warning) << "Received a file-transfer request with no file description."; session->sendTerminate(JinglePayload::Reason::FailedApplication); } } else { session->sendTerminate(JinglePayload::Reason::UnsupportedTransports); } return true; } else { return false; } } } diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp index 8cb1cab..db17620 100644 --- a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp +++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp @@ -1,49 +1,50 @@ /* * Copyright (c) 2011-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/FileTransfer/IncomingJingleFileTransfer.h> #include <set> #include <boost/bind.hpp> #include <boost/smart_ptr/make_shared.hpp> #include <Swiften/Base/Log.h> #include <Swiften/Base/foreach.h> #include <Swiften/Elements/JingleFileTransferDescription.h> #include <Swiften/Elements/JingleFileTransferHash.h> #include <Swiften/Elements/JingleIBBTransportPayload.h> #include <Swiften/Elements/JingleS5BTransportPayload.h> +#include <Swiften/FileTransfer/FileTransferOptions.h> #include <Swiften/FileTransfer/FileTransferTransporter.h> #include <Swiften/FileTransfer/FileTransferTransporterFactory.h> #include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h> #include <Swiften/FileTransfer/TransportSession.h> #include <Swiften/FileTransfer/WriteBytestream.h> #include <Swiften/Jingle/JingleSession.h> #include <Swiften/Network/TimerFactory.h> #include <Swiften/Queries/GenericRequest.h> #include <Swiften/StringCodecs/Base64.h> using namespace Swift; // TODO: ALlow terminate when already terminated. IncomingJingleFileTransfer::IncomingJingleFileTransfer( const JID& toJID, JingleSession::ref session, JingleContentPayload::ref content, FileTransferTransporterFactory* transporterFactory, TimerFactory* timerFactory, CryptoProvider* crypto) : JingleFileTransfer(session, toJID, transporterFactory), initialContent(content), crypto(crypto), state(Initial), receivedBytes(0), hashCalculator(NULL) { description = initialContent->getDescription<JingleFileTransferDescription>(); assert(description); JingleFileTransferFileInfo fileInfo = description->getFileInfo(); @@ -55,83 +56,86 @@ IncomingJingleFileTransfer::IncomingJingleFileTransfer( boost::bind(&IncomingJingleFileTransfer::handleWaitOnHashTimerTicked, this)); } IncomingJingleFileTransfer::~IncomingJingleFileTransfer() { if (waitOnHashTimer) { waitOnHashTimer->stop(); } delete hashCalculator; hashCalculator = NULL; } void IncomingJingleFileTransfer::accept( boost::shared_ptr<WriteBytestream> stream, const FileTransferOptions& options) { SWIFT_LOG(debug) << std::endl; if (state != Initial) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; } assert(!this->stream); this->stream = stream; this->options = options; assert(!hashCalculator); hashCalculator = new IncrementalBytestreamHashCalculator( hashes.find("md5") != hashes.end(), hashes.find("sha-1") != hashes.end(), crypto); writeStreamDataReceivedConnection = stream->onWrite.connect( boost::bind(&IncomingJingleFileTransfer::handleWriteStreamDataReceived, this, _1)); - if (JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport<JingleS5BTransportPayload>()) { + JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport<JingleS5BTransportPayload>(); + JingleIBBTransportPayload::ref ibbTransport = initialContent->getTransport<JingleIBBTransportPayload>(); + if (s5bTransport) { SWIFT_LOG(debug) << "Got S5B transport as initial payload." << std::endl; setTransporter(transporterFactory->createResponderTransporter( getInitiator(), getResponder(), s5bTransport->getSessionID(), options)); transporter->addRemoteCandidates(s5bTransport->getCandidates(), s5bTransport->getDstAddr()); setState(GeneratingInitialLocalCandidates); transporter->startGeneratingLocalCandidates(); } - else if (JingleIBBTransportPayload::ref ibbTransport = initialContent->getTransport<JingleIBBTransportPayload>()) { + else if (ibbTransport && options.isInBandAllowed()) { SWIFT_LOG(debug) << "Got IBB transport as initial payload." << std::endl; setTransporter(transporterFactory->createResponderTransporter( getInitiator(), getResponder(), ibbTransport->getSessionID(), options)); startTransferring(transporter->createIBBReceiveSession( ibbTransport->getSessionID(), description->getFileInfo().getSize(), stream)); session->sendAccept(getContentID(), initialContent->getDescriptions()[0], ibbTransport); } else { - // Can't happen, because the transfer would have been rejected automatically - assert(false); + // This might happen on incoming transfer which only list transport methods we are not allowed to use due to file-transfer options. + session->sendTerminate(JinglePayload::Reason::UnsupportedTransports); + setFinishedState(FileTransfer::State::Failed, FileTransferError(FileTransferError::PeerError)); } } void IncomingJingleFileTransfer::cancel() { SWIFT_LOG(debug) << std::endl; terminate(state == Initial ? JinglePayload::Reason::Decline : JinglePayload::Reason::Cancel); } void IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated( const std::string& s5bSessionID, const std::vector<JingleS5BTransportPayload::Candidate>& candidates, const std::string& dstAddr) { SWIFT_LOG(debug) << std::endl; if (state != GeneratingInitialLocalCandidates) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; } fillCandidateMap(localCandidates, candidates); JingleS5BTransportPayload::ref transport = boost::make_shared<JingleS5BTransportPayload>(); transport->setSessionID(s5bSessionID); transport->setMode(JingleS5BTransportPayload::TCPMode); transport->setDstAddr(dstAddr); foreach(JingleS5BTransportPayload::Candidate candidate, candidates) { transport->addCandidate(candidate); } session->sendAccept(getContentID(), initialContent->getDescriptions()[0], transport); setState(TryingCandidates); transporter->startTryingRemoteCandidates(); } @@ -196,61 +200,61 @@ void IncomingJingleFileTransfer::checkIfAllDataReceived() { foreach(const JingleFileTransferFileInfo::HashElementMap::value_type& hashElement, hashes) { hashInfoAvailable |= !hashElement.second.empty(); } if (!hashInfoAvailable) { SWIFT_LOG(debug) << "No hash information yet. Waiting a while on hash info." << std::endl; setState(WaitingForHash); waitOnHashTimer->start(); } else { checkHashAndTerminate(); } } else if (receivedBytes > getFileSizeInBytes()) { SWIFT_LOG(debug) << "We got more than we could handle!" << std::endl; terminate(JinglePayload::Reason::MediaError); } } void IncomingJingleFileTransfer::handleWriteStreamDataReceived( const std::vector<unsigned char>& data) { hashCalculator->feedData(data); receivedBytes += data.size(); onProcessedBytes(data.size()); checkIfAllDataReceived(); } void IncomingJingleFileTransfer::handleTransportReplaceReceived( const JingleContentID& content, JingleTransportPayload::ref transport) { SWIFT_LOG(debug) << std::endl; - if (state != WaitingForFallbackOrTerminate) { + if (state != WaitingForFallbackOrTerminate) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; } JingleIBBTransportPayload::ref ibbTransport; if (options.isInBandAllowed() && (ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport))) { SWIFT_LOG(debug) << "transport replaced with IBB" << std::endl; startTransferring(transporter->createIBBReceiveSession( ibbTransport->getSessionID(), description->getFileInfo().getSize(), stream)); session->sendTransportAccept(content, ibbTransport); } else { SWIFT_LOG(debug) << "Unknown replace transport" << std::endl; session->sendTransportReject(content, transport); } } JingleContentID IncomingJingleFileTransfer::getContentID() const { return JingleContentID(initialContent->getName(), initialContent->getCreator()); } bool IncomingJingleFileTransfer::verifyData() { if (hashes.empty()) { SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl; return true; } if (hashes.find("sha-1") != hashes.end()) { diff --git a/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp index a72d5ef..2c43766 100644 --- a/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp +++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp @@ -66,91 +66,100 @@ OutgoingJingleFileTransfer::OutgoingJingleFileTransfer( // calculate both, MD5 and SHA-1 since we don't know which one the other side supports hashCalculator = new IncrementalBytestreamHashCalculator(true, true, crypto); stream->onRead.connect( boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1)); waitForRemoteTermination = timerFactory->createTimer(5000); waitForRemoteTermination->onTick.connect(boost::bind(&OutgoingJingleFileTransfer::handleWaitForRemoteTerminationTimeout, this)); } OutgoingJingleFileTransfer::~OutgoingJingleFileTransfer() { if (waitForRemoteTermination) { waitForRemoteTermination->onTick.disconnect(boost::bind(&OutgoingJingleFileTransfer::handleWaitForRemoteTerminationTimeout, this)); waitForRemoteTermination->stop(); } stream->onRead.disconnect( boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1)); delete hashCalculator; hashCalculator = NULL; removeTransporter(); } void OutgoingJingleFileTransfer::start() { SWIFT_LOG(debug) << std::endl; if (state != Initial) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; } - setTransporter(transporterFactory->createInitiatorTransporter(getInitiator(), getResponder(), options)); - setInternalState(GeneratingInitialLocalCandidates); - transporter->startGeneratingLocalCandidates(); + if (!options.isInBandAllowed() && !options.isDirectAllowed() && !options.isAssistedAllowed() && !options.isProxiedAllowed()) { + // Started outgoing file transfer while not supporting transport methods. + setFinishedState(FileTransfer::State::Failed, FileTransferError(FileTransferError::UnknownError)); + } + else { + setTransporter(transporterFactory->createInitiatorTransporter(getInitiator(), getResponder(), options)); + setInternalState(GeneratingInitialLocalCandidates); + transporter->startGeneratingLocalCandidates(); + } } void OutgoingJingleFileTransfer::cancel() { terminate(JinglePayload::Reason::Cancel); } void OutgoingJingleFileTransfer::terminate(JinglePayload::Reason::Type reason) { SWIFT_LOG(debug) << reason << std::endl; if (state != Initial && state != GeneratingInitialLocalCandidates && state != Finished) { session->sendTerminate(reason); } stopAll(); setFinishedState(getExternalFinishedState(reason), getFileTransferError(reason)); } void OutgoingJingleFileTransfer::handleSessionAcceptReceived( const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref transportPayload) { SWIFT_LOG(debug) << std::endl; if (state != WaitingForAccept) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; } if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload)) { transporter->addRemoteCandidates(s5bPayload->getCandidates(), s5bPayload->getDstAddr()); setInternalState(TryingCandidates); transporter->startTryingRemoteCandidates(); } + else if (JingleIBBTransportPayload::ref ibbPayload = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transportPayload)) { + startTransferring(transporter->createIBBSendSession(ibbPayload->getSessionID(), ibbPayload->getBlockSize().get_value_or(DEFAULT_BLOCK_SIZE), stream)); + } else { SWIFT_LOG(debug) << "Unknown transport payload. Falling back." << std::endl; fallback(); } } void OutgoingJingleFileTransfer::handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason) { SWIFT_LOG(debug) << std::endl; if (state == Finished) { SWIFT_LOG(warning) << "Incorrect state: " << state << std::endl; return; } stopAll(); if (state == WaitForTermination) { waitForRemoteTermination->stop(); } if (reason && reason->type == JinglePayload::Reason::Cancel) { setFinishedState(FileTransfer::State::Canceled, FileTransferError(FileTransferError::PeerError)); } else if (reason && reason->type == JinglePayload::Reason::Decline) { setFinishedState(FileTransfer::State::Canceled, boost::optional<FileTransferError>()); } else if (reason && reason->type == JinglePayload::Reason::Success) { setFinishedState(FileTransfer::State::Finished, boost::optional<FileTransferError>()); } else { setFinishedState(FileTransfer::State::Failed, FileTransferError(FileTransferError::PeerError)); } } void OutgoingJingleFileTransfer::handleTransportAcceptReceived(const JingleContentID&, JingleTransportPayload::ref transport) { SWIFT_LOG(debug) << std::endl; @@ -165,67 +174,78 @@ void OutgoingJingleFileTransfer::handleTransportAcceptReceived(const JingleConte } } void OutgoingJingleFileTransfer::handleTransportRejectReceived(const JingleContentID &, boost::shared_ptr<JingleTransportPayload>) { SWIFT_LOG(debug) << std::endl; terminate(JinglePayload::Reason::UnsupportedTransports); } void OutgoingJingleFileTransfer::sendSessionInfoHash() { SWIFT_LOG(debug) << std::endl; JingleFileTransferHash::ref hashElement = boost::make_shared<JingleFileTransferHash>(); hashElement->getFileInfo().addHash(HashElement("sha-1", hashCalculator->getSHA1Hash())); hashElement->getFileInfo().addHash(HashElement("md5", hashCalculator->getMD5Hash())); session->sendInfo(hashElement); } void OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated( const std::string& s5bSessionID, const std::vector<JingleS5BTransportPayload::Candidate>& candidates, const std::string& dstAddr) { SWIFT_LOG(debug) << std::endl; if (state != GeneratingInitialLocalCandidates) { SWIFT_LOG(warning) << "Incorrect state: " << state << std::endl; return; } fillCandidateMap(localCandidates, candidates); JingleFileTransferDescription::ref description = boost::make_shared<JingleFileTransferDescription>(); fileInfo.addHash(HashElement("sha-1", ByteArray())); fileInfo.addHash(HashElement("md5", ByteArray())); description->setFileInfo(fileInfo); - JingleS5BTransportPayload::ref transport = boost::make_shared<JingleS5BTransportPayload>(); - transport->setSessionID(s5bSessionID); - transport->setMode(JingleS5BTransportPayload::TCPMode); - transport->setDstAddr(dstAddr); - foreach(JingleS5BTransportPayload::Candidate candidate, candidates) { - transport->addCandidate(candidate); - SWIFT_LOG(debug) << "\t" << "S5B candidate: " << candidate.hostPort.toString() << std::endl; + JingleTransportPayload::ref transport; + if (candidates.empty()) { + SWIFT_LOG(debug) << "no S5B candidates generated. Send IBB transport candidate." << std::endl; + JingleIBBTransportPayload::ref ibbTransport = boost::make_shared<JingleIBBTransportPayload>(); + ibbTransport->setBlockSize(DEFAULT_BLOCK_SIZE); + ibbTransport->setSessionID(idGenerator->generateID()); + transport = ibbTransport; + } + else { + JingleS5BTransportPayload::ref s5bTransport = boost::make_shared<JingleS5BTransportPayload>(); + s5bTransport->setSessionID(s5bSessionID); + s5bTransport->setMode(JingleS5BTransportPayload::TCPMode); + s5bTransport->setDstAddr(dstAddr); + foreach(JingleS5BTransportPayload::Candidate candidate, candidates) { + s5bTransport->addCandidate(candidate); + SWIFT_LOG(debug) << "\t" << "S5B candidate: " << candidate.hostPort.toString() << std::endl; + } + transport = s5bTransport; } setInternalState(WaitingForAccept); session->sendInitiate(contentID, description, transport); } void OutgoingJingleFileTransfer::fallback() { if (options.isInBandAllowed()) { SWIFT_LOG(debug) << "Trying to fallback to IBB transport." << std::endl; JingleIBBTransportPayload::ref ibbTransport = boost::make_shared<JingleIBBTransportPayload>(); ibbTransport->setBlockSize(DEFAULT_BLOCK_SIZE); ibbTransport->setSessionID(idGenerator->generateID()); setInternalState(FallbackRequested); session->sendTransportReplace(contentID, ibbTransport); } else { SWIFT_LOG(debug) << "Fallback to IBB transport not allowed." << std::endl; terminate(JinglePayload::Reason::ConnectivityError); } } void OutgoingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) { SWIFT_LOG(debug) << std::endl; if (state != Transferring) { SWIFT_LOG(warning) << "Incorrect state: " << state << std::endl; return; } if (error) { terminate(JinglePayload::Reason::ConnectivityError); } else { sendSessionInfoHash(); diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp index 7b97698..3b1be89 100644 --- a/Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp +++ b/Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp @@ -1,32 +1,32 @@ /* - * Copyright (c) 2012-2015 Isode Limited. + * Copyright (c) 2012-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ /* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h> #include <boost/bind.hpp> #include <boost/smart_ptr/make_shared.hpp> #include <Swiften/Base/Log.h> #include <Swiften/Base/foreach.h> #include <Swiften/FileTransfer/SOCKS5BytestreamServer.h> #include <Swiften/FileTransfer/SOCKS5BytestreamServerPortForwardingUser.h> #include <Swiften/FileTransfer/SOCKS5BytestreamServerResourceUser.h> #include <Swiften/Network/ConnectionServer.h> #include <Swiften/Network/ConnectionServerFactory.h> #include <Swiften/Network/NATTraversalForwardPortRequest.h> #include <Swiften/Network/NATTraversalGetPublicIPRequest.h> #include <Swiften/Network/NATTraversalRemovePortForwardingRequest.h> #include <Swiften/Network/NATTraverser.h> #include <Swiften/Network/NetworkEnvironment.h> using namespace Swift; @@ -165,60 +165,64 @@ void SOCKS5BytestreamServerManager::setupPortForwarding() { // Forward ports int port = server->getAddressPort().getPort(); assert(!forwardPortRequest); portMapping = boost::optional<NATPortMapping>(); if ((forwardPortRequest = natTraverser->createForwardPortRequest(port, port))) { forwardPortRequest->onResult.connect( boost::bind(&SOCKS5BytestreamServerManager::handleForwardPortResult, this, _1)); forwardPortRequest->start(); } } void SOCKS5BytestreamServerManager::removePortForwarding() { // remove port forwards if (portMapping) { unforwardPortRequest = natTraverser->createRemovePortForwardingRequest(portMapping.get().getLocalPort(), portMapping.get().getPublicPort()); unforwardPortRequest->onResult.connect(boost::bind(&SOCKS5BytestreamServerManager::handleUnforwardPortResult, this, _1)); unforwardPortRequest->start(); } } void SOCKS5BytestreamServerManager::stop() { if (getPublicIPRequest) { getPublicIPRequest->stop(); getPublicIPRequest.reset(); } if (forwardPortRequest) { forwardPortRequest->stop(); forwardPortRequest.reset(); } + if (unforwardPortRequest) { + unforwardPortRequest->stop(); + unforwardPortRequest.reset(); + } if (server) { server->stop(); delete server; server = NULL; } if (connectionServer) { connectionServer->stop(); connectionServer.reset(); } state = Start; } void SOCKS5BytestreamServerManager::handleGetPublicIPResult(boost::optional<HostAddress> address) { if (address) { SWIFT_LOG(debug) << "Public IP discovered as " << address.get().toString() << "." << std::endl; } else { SWIFT_LOG(debug) << "No public IP discoverable." << std::endl; } publicAddress = address; getPublicIPRequest->stop(); getPublicIPRequest.reset(); } void SOCKS5BytestreamServerManager::handleForwardPortResult(boost::optional<NATPortMapping> mapping) { if (mapping) { SWIFT_LOG(debug) << "Mapping port was successful." << std::endl; diff --git a/Swiften/FileTransfer/UnitTest/DummyFileTransferTransporterFactory.h b/Swiften/FileTransfer/UnitTest/DummyFileTransferTransporterFactory.h index 324404d..00a931f 100644 --- a/Swiften/FileTransfer/UnitTest/DummyFileTransferTransporterFactory.h +++ b/Swiften/FileTransfer/UnitTest/DummyFileTransferTransporterFactory.h @@ -1,87 +1,94 @@ /* - * Copyright (c) 2015 Isode Limited. + * Copyright (c) 2015-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <string> #include <Swiften/Base/API.h> -#include <Swiften/StringCodecs/Hexify.h> #include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/FileTransfer/FileTransferOptions.h> #include <Swiften/FileTransfer/FileTransferTransporter.h> #include <Swiften/FileTransfer/FileTransferTransporterFactory.h> #include <Swiften/FileTransfer/IBBReceiveSession.h> #include <Swiften/FileTransfer/IBBReceiveTransportSession.h> -#include <Swiften/FileTransfer/IBBSendTransportSession.h> #include <Swiften/FileTransfer/IBBSendSession.h> +#include <Swiften/FileTransfer/IBBSendTransportSession.h> #include <Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h> #include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> #include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h> #include <Swiften/FileTransfer/TransportSession.h> #include <Swiften/JID/JID.h> +#include <Swiften/StringCodecs/Hexify.h> namespace Swift { class DummyFileTransferTransporter : public FileTransferTransporter { public: enum Role { Initiator, Responder }; public: DummyFileTransferTransporter( const JID& initiator, const JID& responder, Role role, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamServerManager* /* s5bServerManager */, SOCKS5BytestreamProxiesManager* /* s5bProxy */, IDGenerator* /* idGenerator */, ConnectionFactory*, TimerFactory*, CryptoProvider* cryptoProvider, IQRouter* iqRouter, - const FileTransferOptions&) : initiator_(initiator), responder_(responder), role_(role), s5bRegistry_(s5bRegistry), crypto_(cryptoProvider), iqRouter_(iqRouter) { + const FileTransferOptions& ftOptions) : initiator_(initiator), responder_(responder), role_(role), s5bRegistry_(s5bRegistry), crypto_(cryptoProvider), iqRouter_(iqRouter), ftOptions_(ftOptions) { } void initialize() { s5bSessionID_ = s5bRegistry_->generateSessionID(); } virtual void startGeneratingLocalCandidates() { std::vector<JingleS5BTransportPayload::Candidate> candidates; + if (ftOptions_.isDirectAllowed()) { + JingleS5BTransportPayload::Candidate candidate; + candidate.cid = "123"; + candidate.priority = 1235; + candidates.push_back(candidate); + } onLocalCandidatesGenerated(s5bSessionID_, candidates, getSOCKS5DstAddr()); } virtual void stopGeneratingLocalCandidates() { } virtual void addRemoteCandidates(const std::vector<JingleS5BTransportPayload::Candidate>&, const std::string&) { } virtual void startTryingRemoteCandidates() { onRemoteCandidateSelectFinished(s5bSessionID_, boost::optional<JingleS5BTransportPayload::Candidate>()); } virtual void stopTryingRemoteCandidates() { } virtual void startActivatingProxy(const JID& /* proxy */) { } virtual void stopActivatingProxy() { } virtual boost::shared_ptr<TransportSession> createIBBSendSession(const std::string& sessionID, unsigned int blockSize, boost::shared_ptr<ReadBytestream> stream) { boost::shared_ptr<IBBSendSession> ibbSession = boost::make_shared<IBBSendSession>( sessionID, initiator_, responder_, stream, iqRouter_); ibbSession->setBlockSize(blockSize); return boost::make_shared<IBBSendTransportSession>(ibbSession); } virtual boost::shared_ptr<TransportSession> createIBBReceiveSession(const std::string& sessionID, unsigned long long size, boost::shared_ptr<WriteBytestream> stream) { @@ -112,60 +119,61 @@ public: private: std::string getSOCKS5DstAddr() const { std::string result; if (role_ == Initiator) { result = getInitiatorCandidateSOCKS5DstAddr(); } else { result = getResponderCandidateSOCKS5DstAddr(); } return result; } std::string getInitiatorCandidateSOCKS5DstAddr() const { return Hexify::hexify(crypto_->getSHA1Hash(createSafeByteArray(s5bSessionID_ + initiator_.toString() + responder_.toString()))); } std::string getResponderCandidateSOCKS5DstAddr() const { return Hexify::hexify(crypto_->getSHA1Hash(createSafeByteArray(s5bSessionID_ + responder_.toString() + initiator_.toString()))); } private: JID initiator_; JID responder_; Role role_; SOCKS5BytestreamRegistry* s5bRegistry_; CryptoProvider* crypto_; std::string s5bSessionID_; IQRouter* iqRouter_; + FileTransferOptions ftOptions_; }; class DummyFileTransferTransporterFactory : public FileTransferTransporterFactory { public: DummyFileTransferTransporterFactory( SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamServerManager* s5bServerManager, SOCKS5BytestreamProxiesManager* s5bProxy, IDGenerator* idGenerator, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, CryptoProvider* cryptoProvider, IQRouter* iqRouter) : s5bRegistry_(s5bRegistry), s5bServerManager_(s5bServerManager), s5bProxy_(s5bProxy), idGenerator_(idGenerator), connectionFactory_(connectionFactory), timerFactory_(timerFactory), cryptoProvider_(cryptoProvider), iqRouter_(iqRouter) { } virtual ~DummyFileTransferTransporterFactory() { } virtual FileTransferTransporter* createInitiatorTransporter(const JID& initiator, const JID& responder, const FileTransferOptions& options) { DummyFileTransferTransporter* transporter = new DummyFileTransferTransporter( initiator, responder, DummyFileTransferTransporter::Initiator, s5bRegistry_, s5bServerManager_, s5bProxy_, idGenerator_, connectionFactory_, timerFactory_, diff --git a/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp index f3fe42e..fee26d5 100644 --- a/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp +++ b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp @@ -1,38 +1,38 @@ /* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* - * Copyright (c) 2013-2015 Isode Limited. + * Copyright (c) 2013-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <iostream> #include <boost/bind.hpp> #include <boost/optional.hpp> #include <boost/smart_ptr/make_shared.hpp> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include <Swiften/Base/ByteArray.h> #include <Swiften/Base/IDGenerator.h> #include <Swiften/Base/Override.h> #include <Swiften/Client/DummyStanzaChannel.h> #include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/Crypto/PlatformCryptoProvider.h> #include <Swiften/Elements/IBB.h> #include <Swiften/Elements/JingleFileTransferDescription.h> #include <Swiften/Elements/JingleIBBTransportPayload.h> #include <Swiften/Elements/JingleS5BTransportPayload.h> #include <Swiften/EventLoop/DummyEventLoop.h> #include <Swiften/FileTransfer/ByteArrayReadBytestream.h> #include <Swiften/FileTransfer/DefaultFileTransferTransporterFactory.h> #include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h> #include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h> #include <Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h> #include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> @@ -54,76 +54,76 @@ using namespace Swift; class OutgoingJingleFileTransferTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(OutgoingJingleFileTransferTest); CPPUNIT_TEST(test_SendSessionInitiateOnStart); CPPUNIT_TEST(test_FallbackToIBBAfterFailingS5B); CPPUNIT_TEST(test_ReceiveSessionTerminateAfterSessionInitiate); CPPUNIT_TEST(test_DeclineEmitsFinishedStateCanceled); CPPUNIT_TEST_SUITE_END(); class FTStatusHelper { public: FTStatusHelper() : finishedCalled(false), error(FileTransferError::UnknownError) { } void handleFileTransferFinished(boost::optional<FileTransferError> error) { finishedCalled = true; if (error.is_initialized()) this->error = error.get().getType(); } void handleFileTransferStatusChanged(FileTransfer::State fileTransferSTate) { state = fileTransferSTate; } public: bool finishedCalled; FileTransferError::Type error; boost::optional<FileTransfer::State> state; }; public: - boost::shared_ptr<OutgoingJingleFileTransfer> createTestling() { + boost::shared_ptr<OutgoingJingleFileTransfer> createTestling(const FileTransferOptions& options = FileTransferOptions().withAssistedAllowed(false).withDirectAllowed(false).withProxiedAllowed(false)) { JID to("test@foo.com/bla"); JingleFileTransferFileInfo fileInfo; fileInfo.setDescription("some file"); fileInfo.setName("test.bin"); fileInfo.addHash(HashElement("sha-1", ByteArray())); fileInfo.setSize(1024 * 1024); return boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer( to, boost::shared_ptr<JingleSession>(fakeJingleSession), stream, ftTransportFactory, timerFactory, idGen, fileInfo, - FileTransferOptions().withAssistedAllowed(false).withDirectAllowed(false).withProxiedAllowed(false), + options, crypto.get())); } 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() { crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession"); jingleContentPayload = boost::make_shared<JingleContentPayload>(); stanzaChannel = new DummyStanzaChannel(); iqRouter = new IQRouter(stanzaChannel); eventLoop = new DummyEventLoop(); timerFactory = new DummyTimerFactory(); connectionFactory = new DummyConnectionFactory(eventLoop); serverConnectionFactory = new DummyConnectionServerFactory(eventLoop); s5bRegistry = new SOCKS5BytestreamRegistry(); networkEnvironment = new PlatformNetworkEnvironment(); natTraverser = new PlatformNATTraversalWorker(eventLoop); bytestreamServerManager = new SOCKS5BytestreamServerManager(s5bRegistry, serverConnectionFactory, networkEnvironment, natTraverser); data.clear(); for (int n=0; n < 1024 * 1024; ++n) { data.push_back(34); } stream = boost::make_shared<ByteArrayReadBytestream>(data); @@ -132,70 +132,71 @@ public: s5bProxy = new SOCKS5BytestreamProxiesManager(connectionFactory, timerFactory, resolver, iqRouter, "bar.com"); ftTransportFactory = new DummyFileTransferTransporterFactory(s5bRegistry, bytestreamServerManager, s5bProxy, idGen, connectionFactory, timerFactory, crypto.get(), iqRouter); } void tearDown() { delete ftTransportFactory; delete s5bProxy; delete idGen; delete bytestreamServerManager; delete natTraverser; delete networkEnvironment; delete s5bRegistry; delete serverConnectionFactory; 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(static_cast<size_t>(1048576) == description->getFileInfo().getSize()); - JingleS5BTransportPayload::ref transport = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(call.payload); + JingleIBBTransportPayload::ref transport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(call.payload); CPPUNIT_ASSERT(transport); } void test_FallbackToIBBAfterFailingS5B() { - boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling(); + boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling(FileTransferOptions().withAssistedAllowed(true).withDirectAllowed(true).withProxiedAllowed(true)); transfer->start(); FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0); + CPPUNIT_ASSERT(boost::dynamic_pointer_cast<JingleS5BTransportPayload>(call.payload)); fakeJingleSession->handleSessionAcceptReceived(call.id, call.description, call.payload); // send candidate failure JingleS5BTransportPayload::ref candidateFailurePayload = boost::make_shared<JingleS5BTransportPayload>(); candidateFailurePayload->setCandidateError(true); candidateFailurePayload->setSessionID(call.payload->getSessionID()); fakeJingleSession->handleTransportInfoReceived(call.id, candidateFailurePayload); // no S5B candidates -> fallback to IBB // call at position 1 is the candidate our candidate error FakeJingleSession::ReplaceTransportCall replaceCall = getCall<FakeJingleSession::ReplaceTransportCall>(2); // accept transport replace fakeJingleSession->handleTransportAcceptReceived(replaceCall.id, replaceCall.payload); IQ::ref iqOpenStanza = stanzaChannel->getStanzaAtIndex<IQ>(0); CPPUNIT_ASSERT(iqOpenStanza); IBB::ref ibbOpen = iqOpenStanza->getPayload<IBB>(); CPPUNIT_ASSERT(ibbOpen); CPPUNIT_ASSERT_EQUAL(IBB::Open, ibbOpen->getAction()); } void test_ReceiveSessionTerminateAfterSessionInitiate() { boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling(); transfer->start(); getCall<FakeJingleSession::InitiateCall>(0); FTStatusHelper helper; helper.finishedCalled = false; diff --git a/Swiften/QA/FileTransferTest/FileTransferTest.cpp b/Swiften/QA/FileTransferTest/FileTransferTest.cpp index 8597033..7a50e9f 100644 --- a/Swiften/QA/FileTransferTest/FileTransferTest.cpp +++ b/Swiften/QA/FileTransferTest/FileTransferTest.cpp @@ -22,234 +22,256 @@ #include <Swiften/Elements/Presence.h> #include <Swiften/EventLoop/SimpleEventLoop.h> #include <Swiften/FileTransfer/FileReadBytestream.h> #include <Swiften/FileTransfer/FileTransferManager.h> #include <Swiften/FileTransfer/FileWriteBytestream.h> #include <Swiften/FileTransfer/OutgoingFileTransfer.h> #include <Swiften/FileTransfer/ReadBytestream.h> #include <Swiften/Network/BoostNetworkFactories.h> #include <Swiften/Network/Timer.h> #include <Swiften/Network/TimerFactory.h> using namespace Swift; static const std::string CLIENT_NAME = "Swiften FT Test"; static const std::string CLIENT_NODE = "http://swift.im"; static boost::shared_ptr<SimpleEventLoop> eventLoop; static boost::shared_ptr<BoostNetworkFactories> networkFactories; BoostRandomGenerator randGen; enum Candidate { InBandBytestream = 1, S5B_Direct = 2, S5B_Proxied = 4, S5B_Assisted = 8, }; class FileTransferTest { public: - FileTransferTest(int senderCandidates, int receiverCandidates) : senderCandidates_(senderCandidates), senderIsDone_(false), receiverCandidates_(receiverCandidates), receiverIsDone_(false) { + FileTransferTest(int senderCandidates, int receiverCandidates) : senderCandidates_(senderCandidates), senderError_(FileTransferError::UnknownError), senderIsDone_(false), receiverCandidates_(receiverCandidates), receiverError_(FileTransferError::UnknownError), receiverIsDone_(false) { sender_ = boost::make_shared<Client>(JID(getenv("SWIFT_FILETRANSFERTEST_JID")), getenv("SWIFT_FILETRANSFERTEST_PASS"), networkFactories.get()); sender_->onDisconnected.connect(boost::bind(&FileTransferTest::handleSenderDisconnected, this, _1)); sender_->onConnected.connect(boost::bind(&FileTransferTest::handleSenderConnected, this)); sender_->getEntityCapsProvider()->onCapsChanged.connect(boost::bind(&FileTransferTest::handleSenderCapsChanged, this, _1)); receiver_ = boost::make_shared<Client>(JID(getenv("SWIFT_FILETRANSFERTEST2_JID")), getenv("SWIFT_FILETRANSFERTEST2_PASS"), networkFactories.get()); receiver_->onConnected.connect(boost::bind(&FileTransferTest::handleReceiverConnected, this)); receiver_->onDisconnected.connect(boost::bind(&FileTransferTest::handleReceiverDisconnected, this, _1)); senderTracer_ = new ClientXMLTracer(sender_.get()); receiverTracer_ = new ClientXMLTracer(receiver_.get()); ClientOptions options; options.useTLS = ClientOptions::NeverUseTLS; options.useStreamCompression = false; options.useStreamResumption = false; options.useAcks = false; sender_->connect(options); receiver_->connect(options); timeOut_ = networkFactories->getTimerFactory()->createTimer(60000); timeOut_->onTick.connect(boost::bind(&FileTransferTest::handleTimeOut, this)); // Create randomly sized data to exchange. sendFilePath_ = boost::filesystem::unique_path("ft_send_%%%%%%%%%%%%%%%%.bin"); receiveFilePath_ = boost::filesystem::unique_path("ft_receive_%%%%%%%%%%%%%%%%.bin"); size_t size = 1024 + boost::numeric_cast<size_t>(randGen.generateRandomInteger(1024 * 10)); sendData_.resize(size); for (size_t n = 0; n < sendData_.size(); n++) { sendData_[n] = boost::numeric_cast<unsigned char>(randGen.generateRandomInteger(255)); } std::ofstream outfile(sendFilePath_.native().c_str(), std::ios::out | std::ios::binary); outfile.write(reinterpret_cast<char *>(&sendData_[0]), boost::numeric_cast<ptrdiff_t>(sendData_.size())); outfile.close(); } ~FileTransferTest() { timeOut_->stop(); delete senderTracer_; delete receiverTracer_; // Free file-transfer objects so file handles are closed and files can be removed afterwards. assert(!outgoingFileTransfer_ && incomingFileTransfers_.empty()); if(boost::filesystem::exists(sendFilePath_)) { boost::filesystem::remove(sendFilePath_); } if(boost::filesystem::exists(receiveFilePath_)) { boost::filesystem::remove(receiveFilePath_); } } void handleSenderConnected() { + DiscoInfo discoInfo; + discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc")); + discoInfo.addFeature(DiscoInfo::JingleFeature); + discoInfo.addFeature(DiscoInfo::JingleFTFeature); + discoInfo.addFeature(DiscoInfo::Bytestream); + if (senderCandidates_ & InBandBytestream) { + discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature); + } + if (senderCandidates_ & (S5B_Direct | S5B_Assisted | S5B_Proxied)) { + discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature); + } + sender_->getDiscoManager()->setCapsNode(CLIENT_NODE); + sender_->getDiscoManager()->setDiscoInfo(discoInfo); sender_->sendPresence(Presence::create()); } void handleReceiverConnected() { receiver_->getFileTransferManager()->onIncomingFileTransfer.connect(boost::bind(&FileTransferTest::handleReceiverIncomingFileTransfer, this, _1)); DiscoInfo discoInfo; discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc")); discoInfo.addFeature(DiscoInfo::JingleFeature); discoInfo.addFeature(DiscoInfo::JingleFTFeature); discoInfo.addFeature(DiscoInfo::Bytestream); - discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature); - discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature); + if (receiverCandidates_ & InBandBytestream) { + discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature); + } + if (receiverCandidates_ & (S5B_Direct | S5B_Assisted | S5B_Proxied)) { + discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature); + } receiver_->getDiscoManager()->setCapsNode(CLIENT_NODE); receiver_->getDiscoManager()->setDiscoInfo(discoInfo); receiver_->getPresenceSender()->sendPresence(Presence::create()); } void handleReceiverIncomingFileTransfer(IncomingFileTransfer::ref transfer) { incomingFileTransfers_.push_back(transfer); boost::shared_ptr<FileWriteBytestream> out = boost::make_shared<FileWriteBytestream>(receiveFilePath_.native()); transfer->onFinished.connect(boost::bind(&FileTransferTest::handleReceiverFileTransferFinished, this, _1, out)); FileTransferOptions options; options = options.withInBandAllowed(receiverCandidates_ & InBandBytestream); options = options.withDirectAllowed(receiverCandidates_ & S5B_Direct); options = options.withAssistedAllowed(receiverCandidates_ & S5B_Assisted); options = options.withProxiedAllowed(receiverCandidates_ & S5B_Proxied); std::cout << "Incoming transfer options: " << "IBB (" << options.isInBandAllowed() << ")" << ", "; std::cout << "S5B Direct (" << options.isDirectAllowed() << ")" << ", "; std::cout << "S5B Assisted (" << options.isAssistedAllowed() << ")" << ", "; std::cout << "S5B Proxied (" << options.isProxiedAllowed() << ")" << std::endl; transfer->accept(out, options); } void handleSenderCapsChanged(const JID &jid) { if (receiver_ && (receiver_->getJID().toBare() == jid.toBare())) { boost::shared_ptr<FileReadBytestream> fileStream = boost::make_shared<FileReadBytestream>(sendFilePath_); FileTransferOptions options; options = options.withInBandAllowed(senderCandidates_ & InBandBytestream); options = options.withDirectAllowed(senderCandidates_ & S5B_Direct); options = options.withAssistedAllowed(senderCandidates_ & S5B_Assisted); options = options.withProxiedAllowed(senderCandidates_ & S5B_Proxied); std::cout << "Outgoing transfer options: " << "IBB (" << options.isInBandAllowed() << ")" << ", "; std::cout << "S5B Direct (" << options.isDirectAllowed() << ")" << ", "; std::cout << "S5B Assisted (" << options.isAssistedAllowed() << ")" << ", "; std::cout << "S5B Proxied (" << options.isProxiedAllowed() << ")" << std::endl; outgoingFileTransfer_ = sender_->getFileTransferManager()->createOutgoingFileTransfer(jid.toBare(), sendFilePath_, "Some File!", fileStream, options); if (outgoingFileTransfer_) { outgoingFileTransfer_->onFinished.connect(boost::bind(&FileTransferTest::handleSenderFileTransferFinished, this, _1)); outgoingFileTransfer_->start(); } else { std::cout << "ERROR: No outgoing file transfer returned." << std::endl; + receiverIsDone_ = true; + senderIsDone_ = true; endTest(); } } } void handleReceiverFileTransferFinished(const boost::optional<FileTransferError>& error, boost::shared_ptr<FileWriteBytestream> out) { out->close(); receiverError_ = error; receiverIsDone_ = true; if (senderIsDone_) { timeOut_->stop(); timeOut_ = networkFactories->getTimerFactory()->createTimer(1000); timeOut_->onTick.connect(boost::bind(&FileTransferTest::endTest, this)); timeOut_->start(); } } void handleSenderDisconnected(const boost::optional<ClientError>& error) { if (error) { std::cout << this << " " << "handleSenderDisconnected: error: " << error.get() << std::endl; } // All file-transfers related to a Client instance need to be freed // *before* freeing the Client instance. outgoingFileTransfer_.reset(); sender_.reset(); if (!sender_ && !receiver_) { eventLoop->stop(); } } void handleReceiverDisconnected(const boost::optional<ClientError>& error) { if (error) { std::cout << this << " " << "handleReceiverDisconnected: error: " << error.get() << std::endl; } // All file-transfers related to a Client instance need to be freed // *before* freeing the Client instance. incomingFileTransfers_.clear(); receiver_.reset(); if (!sender_ && !receiver_) { eventLoop->stop(); } } void handleSenderFileTransferFinished(const boost::optional<FileTransferError>& error) { senderError_ = error; senderIsDone_ = true; if (receiverIsDone_) { timeOut_->stop(); timeOut_ = networkFactories->getTimerFactory()->createTimer(1000); timeOut_->onTick.connect(boost::bind(&FileTransferTest::endTest, this)); timeOut_->start(); } + else if (error) { + endTest(); + } } void run() { timeOut_->start(); eventLoop->run(); } void endTest() { if (sender_) { sender_->disconnect(); } if (receiver_) { receiver_->disconnect(); } } void handleTimeOut() { std::cout << "Test timed out!!!" << std::endl; endTest(); } bool isDone() const { return senderIsDone_ && receiverIsDone_; } bool wasSuccessful() const { return !senderError_ && !receiverError_; } private: @@ -260,70 +282,71 @@ class FileTransferTest { OutgoingFileTransfer::ref outgoingFileTransfer_; boost::filesystem::path sendFilePath_; boost::optional<FileTransferError> senderError_; bool senderIsDone_; int receiverCandidates_; boost::shared_ptr<Client> receiver_; ClientXMLTracer* receiverTracer_; ByteArray receiveData_; std::vector<IncomingFileTransfer::ref> incomingFileTransfers_; boost::filesystem::path receiveFilePath_; boost::optional<FileTransferError> receiverError_; bool receiverIsDone_; Timer::ref timeOut_; }; static bool runTest(int senderCandidates, int receiverCandidates) { bool success = false; std::cout << "senderCandidates: " << senderCandidates << ", receiverCandidates: " << receiverCandidates << std::endl; bool expectSuccess = (senderCandidates & receiverCandidates) > 0; eventLoop = boost::make_shared<SimpleEventLoop>(); networkFactories = boost::make_shared<BoostNetworkFactories>(eventLoop.get()); boost::shared_ptr<FileTransferTest> testRun = boost::make_shared<FileTransferTest>(senderCandidates, receiverCandidates); testRun->run(); - if (testRun->isDone()) { - bool wasSuccessful = testRun->wasSuccessful(); - if (expectSuccess == wasSuccessful) { - success = true; - } else { - std::cout << "expected success: " << expectSuccess << ", wasSuccessful: " << wasSuccessful << std::endl; + bool wasSuccessful = testRun->wasSuccessful(); + if (expectSuccess == wasSuccessful) { + success = true; + } + else { + if (!testRun->isDone()) { + std::cout << "Test did not finish transfer. Sender candidates = " << senderCandidates << ", receiver candidates = " << receiverCandidates << "." << std::endl; } - } else { - std::cout << "Failed to run test! Sender candidates = " << senderCandidates << ", receiver candidates = " << receiverCandidates << "." << std::endl; } + std::cout << "expected success: " << expectSuccess << ", wasSuccessful: " << wasSuccessful << std::endl; + testRun.reset(); networkFactories.reset(); eventLoop->runUntilEvents(); eventLoop->stop(); eventLoop.reset(); return success; } /** * This program test file-transfer interop between Swift and itself with various connection candidates. * The all combinations of the candidates, IBB, S5B (direct) and S5B (proxied), on sender and receiver side are tested. */ int main(int argc, char** argv) { int failedTests = 0; std::vector<std::pair<int, int> > failedTestPairs; std::cout << "Swiften File-Transfer Connectivity Test Suite" << std::endl; if (argc == 1) { if (getenv("SWIFT_FILETRANSFERTEST_CONFIG")) { // test configuration described in SWIFT_FILETRANSFERTEST_CONFIG environment variable, e.g. "1:1|2:2" std::vector<std::string> configurations; std::string configs_env = std::string(getenv("SWIFT_FILETRANSFERTEST_CONFIG")); boost::split(configurations, configs_env, boost::is_any_of("|")); foreach(const std::string& config, configurations) { std::vector<std::string> split_config; boost::split(split_config, config, boost::is_any_of(":")); assert(split_config.size() == 2); |