summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swiften/FileTransfer/IncomingFileTransferManager.cpp4
-rw-r--r--Swiften/FileTransfer/IncomingJingleFileTransfer.cpp14
-rw-r--r--Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp40
-rw-r--r--Swiften/FileTransfer/SOCKS5BytestreamServerManager.cpp6
-rw-r--r--Swiften/FileTransfer/UnitTest/DummyFileTransferTransporterFactory.h16
-rw-r--r--Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp11
-rw-r--r--Swiften/QA/FileTransferTest/FileTransferTest.cpp45
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);