diff options
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); |