summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2012-12-25 14:39:48 (GMT)
committerRemko Tronçon <git@el-tramo.be>2013-05-11 10:22:56 (GMT)
commit927d62cc54c8a5087dba6b61afa9ad30dc528a23 (patch)
treee67dc911bd30c0519d31a542d8e085bbb209879d /Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
parent17b188343e7208b875af7af30d94f0bf948f6b93 (diff)
downloadswift-927d62cc54c8a5087dba6b61afa9ad30dc528a23.zip
swift-927d62cc54c8a5087dba6b61afa9ad30dc528a23.tar.bz2
File Transfer refactoring.
Allocate S5B server lazily. Forward forts lazily. Various state machine fixes. Temporarily disabling S5B proxy support. Change-Id: I3145e85a99b15a7e457306bbfbe9c0eb570191e4
Diffstat (limited to 'Swiften/FileTransfer/IncomingJingleFileTransfer.cpp')
-rw-r--r--Swiften/FileTransfer/IncomingJingleFileTransfer.cpp651
1 files changed, 261 insertions, 390 deletions
diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
index 6dc53fb..b64e333 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Remko Tronçon
+ * Copyright (c) 2011-2013 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -11,513 +11,384 @@
#include <Swiften/Base/Log.h>
#include <Swiften/Base/foreach.h>
+#include <Swiften/Jingle/JingleSession.h>
#include <Swiften/Elements/JingleIBBTransportPayload.h>
#include <Swiften/Elements/JingleS5BTransportPayload.h>
#include <Swiften/Elements/JingleFileTransferHash.h>
-#include <Swiften/Elements/S5BProxyRequest.h>
#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
-#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
-#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
-#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
-#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
-#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
-#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/FileTransfer/FileTransferTransporter.h>
+#include <Swiften/FileTransfer/FileTransferTransporterFactory.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/FileTransfer/TransportSession.h>
-namespace Swift {
+using namespace Swift;
+
+// TODO: ALlow terminate when already terminated.
IncomingJingleFileTransfer::IncomingJingleFileTransfer(
- const JID& ourJID,
+ const JID& toJID,
JingleSession::ref session,
JingleContentPayload::ref content,
- RemoteJingleTransportCandidateSelectorFactory* candidateSelectorFactory,
- LocalJingleTransportCandidateGeneratorFactory* candidateGeneratorFactory,
- IQRouter* router,
- SOCKS5BytestreamRegistry* registry,
- SOCKS5BytestreamProxy* proxy,
+ FileTransferTransporterFactory* transporterFactory,
TimerFactory* timerFactory,
CryptoProvider* crypto) :
- ourJID(ourJID),
- session(session),
- router(router),
+ JingleFileTransfer(session, toJID, transporterFactory),
initialContent(content),
crypto(crypto),
state(Initial),
receivedBytes(0),
- s5bRegistry(registry),
- s5bProxy(proxy),
- remoteTransportCandidateSelectFinished(false),
- localTransportCandidateSelectFinished(false),
- serverSession(0) {
-
- candidateSelector = candidateSelectorFactory->createCandidateSelector();
- candidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
-
- candidateGenerator = candidateGeneratorFactory->createCandidateGenerator();
- candidateGenerator->onLocalTransportCandidatesGenerated.connect(boost::bind(&IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1));
-
- session->onTransportInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
- session->onTransportReplaceReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2));
- session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this, _1));
- session->onSessionInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionInfoReceived, this, _1));
-
+ hashCalculator(NULL) {
description = initialContent->getDescription<JingleFileTransferDescription>();
assert(description);
assert(description->getOffers().size() == 1);
StreamInitiationFileInfo fileInfo = description->getOffers().front();
- fileSizeInBytes = fileInfo.getSize();
- filename = fileInfo.getName();
+ setFileInfo(fileInfo.getName(), fileInfo.getSize());
hash = fileInfo.getHash();
- algo = fileInfo.getAlgo();
+ hashAlgorithm = fileInfo.getAlgo();
waitOnHashTimer = timerFactory->createTimer(5000);
- waitOnHashTimer->onTick.connect(boost::bind(&IncomingJingleFileTransfer::finishOffTransfer, this));
+ waitOnHashTimerTickedConnection = waitOnHashTimer->onTick.connect(
+ boost::bind(&IncomingJingleFileTransfer::handleWaitOnHashTimerTicked, this));
}
IncomingJingleFileTransfer::~IncomingJingleFileTransfer() {
- stream->onWrite.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
- delete hashCalculator;
-
- session->onSessionTerminateReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this, _1));
- session->onTransportReplaceReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2));
- session->onTransportInfoReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
-
- candidateGenerator->onLocalTransportCandidatesGenerated.disconnect(boost::bind(&IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1));
- delete candidateGenerator;
-
- candidateSelector->onRemoteTransportCandidateSelectFinished.disconnect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
- delete candidateSelector;
}
-void IncomingJingleFileTransfer::accept(WriteBytestream::ref stream) {
+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;
- hashCalculator = new IncrementalBytestreamHashCalculator( algo == "md5" || hash.empty() , algo == "sha-1" || hash.empty(), crypto);
- stream->onWrite.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
- stream->onWrite.connect(boost::bind(&IncomingJingleFileTransfer::handleWriteStreamDataReceived, this, _1));
- onStateChange(FileTransfer::State(FileTransfer::State::Negotiating));
- if (JingleIBBTransportPayload::ref ibbTransport = initialContent->getTransport<JingleIBBTransportPayload>()) {
- SWIFT_LOG(debug) << "Got IBB transport payload!" << std::endl;
- setActiveTransport(createIBBTransport(ibbTransport));
- session->sendAccept(getContentID(), initialContent->getDescriptions()[0], ibbTransport);
- }
- else if (JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport<JingleS5BTransportPayload>()) {
+ assert(!hashCalculator);
+ hashCalculator = new IncrementalBytestreamHashCalculator(
+ hashAlgorithm == "md5" || hash.empty(), hashAlgorithm == "sha-1" || hash.empty(), crypto);
+
+ writeStreamDataReceivedConnection = stream->onWrite.connect(
+ boost::bind(&IncomingJingleFileTransfer::handleWriteStreamDataReceived, this, _1));
+
+ if (JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport<JingleS5BTransportPayload>()) {
SWIFT_LOG(debug) << "Got S5B transport payload!" << std::endl;
- state = CreatingInitialTransports;
- s5bSessionID = s5bTransport->getSessionID().empty() ? idGenerator.generateID() : s5bTransport->getSessionID();
- s5bDestination = SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator(), crypto);
- s5bRegistry->addWriteBytestream(s5bDestination, stream);
- fillCandidateMap(theirCandidates, s5bTransport);
- candidateSelector->addRemoteTransportCandidates(s5bTransport);
- candidateSelector->setRequesterTarget(session->getInitiator(), ourJID);
- s5bTransport->setSessionID(s5bSessionID);
- candidateGenerator->start(s5bTransport);
+ setTransporter(transporterFactory->createResponderTransporter(
+ getInitiator(), getResponder(), s5bTransport->getSessionID()));
+ transporter->addRemoteCandidates(s5bTransport->getCandidates());
+ setState(GeneratingInitialLocalCandidates);
+ transporter->startGeneratingLocalCandidates();
}
else {
+ // Can't happen, because the transfer would have been rejected automatically
assert(false);
}
}
-const JID& IncomingJingleFileTransfer::getSender() const {
- return session->getInitiator();
-}
-
-const JID& IncomingJingleFileTransfer::getRecipient() const {
- return ourJID;
-}
-
void IncomingJingleFileTransfer::cancel() {
- session->sendTerminate(JinglePayload::Reason::Cancel);
-
- if (activeTransport) activeTransport->stop();
- if (serverSession) serverSession->stop();
- if (clientSession) clientSession->stop();
- onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
-}
-
-void IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates) {
- if (state == CreatingInitialTransports) {
- if (JingleS5BTransportPayload::ref s5bCandidates = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(candidates)) {
- //localTransportCandidateSelectFinished = true;
- //JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared<JingleS5BTransportPayload>();
- //emptyCandidates->setSessionID(s5bCandidates->getSessionID());
- fillCandidateMap(ourCandidates, s5bCandidates);
- session->sendAccept(getContentID(), initialContent->getDescriptions()[0], s5bCandidates);
-
- state = NegotiatingTransport;
- candidateSelector->selectCandidate();
- }
- }
- else {
- SWIFT_LOG(debug) << "Unhandled state!" << std::endl;
- }
-}
-
-
-void IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref transport) {
SWIFT_LOG(debug) << std::endl;
- if (state == Terminated) {
- return;
- }
- if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
- //remoteTransportCandidateSelectFinished = true;
- //selectedRemoteTransportCandidate = transport;
- ourCandidate = s5bPayload;
- //checkCandidateSelected();
- decideOnUsedTransport();
- session->sendTransportInfo(getContentID(), s5bPayload);
- }
- else {
- SWIFT_LOG(debug) << "Expected something different here." << std::endl;
- }
+ terminate(state == Initial ? JinglePayload::Reason::Decline : JinglePayload::Reason::Cancel);
}
-// TODO: Why was assert(false) there? Is this method no longer used perhaps? Delete it if not
-void IncomingJingleFileTransfer::checkCandidateSelected() {
- //assert(false);
- if (localTransportCandidateSelectFinished && remoteTransportCandidateSelectFinished) {
- if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate) && candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) {
- if (candidateGenerator->getPriority(selectedLocalTransportCandidate) > candidateSelector->getPriority(selectedRemoteTransportCandidate)) {
- setActiveTransport(candidateGenerator->selectTransport(selectedLocalTransportCandidate));
- }
- else {
- setActiveTransport(candidateSelector->selectTransport(selectedRemoteTransportCandidate));
- }
- }
- else if (candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) {
- setActiveTransport(candidateSelector->selectTransport(selectedRemoteTransportCandidate));
- }
- else if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate)) {
- setActiveTransport(candidateGenerator->selectTransport(selectedLocalTransportCandidate));
- }
- else {
- state = WaitingForFallbackOrTerminate;
- }
- }
-}
+void IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated(
+ const std::string& s5bSessionID,
+ const std::vector<JingleS5BTransportPayload::Candidate>& candidates) {
+ SWIFT_LOG(debug) << std::endl;
+ if (state != GeneratingInitialLocalCandidates) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; }
-void IncomingJingleFileTransfer::setActiveTransport(JingleTransport::ref transport) {
- state = Transferring;
- onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
- activeTransport = transport;
- activeTransport->onDataReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1));
- activeTransport->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
- activeTransport->start();
-}
+ fillCandidateMap(localCandidates, candidates);
-bool IncomingJingleFileTransfer::verifyReceviedData() {
- if (algo.empty() || hash.empty()) {
- SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
- return true;
- } else {
- if (algo == "sha-1") {
- SWIFT_LOG(debug) << "verify data via SHA-1 hash: " << (hash == hashCalculator->getSHA1String()) << std::endl;
- return hash == hashCalculator->getSHA1String();
- }
- else if (algo == "md5") {
- SWIFT_LOG(debug) << "verify data via MD5 hash: " << (hash == hashCalculator->getMD5String()) << std::endl;
- return hash == hashCalculator->getMD5String();
- }
- else {
- SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
- return true;
- }
+ JingleS5BTransportPayload::ref transport = boost::make_shared<JingleS5BTransportPayload>();
+ transport->setSessionID(s5bSessionID);
+ transport->setMode(JingleS5BTransportPayload::TCPMode);
+ foreach(JingleS5BTransportPayload::Candidate candidate, candidates) {
+ transport->addCandidate(candidate);
}
-}
+ session->sendAccept(getContentID(), initialContent->getDescriptions()[0], transport);
-void IncomingJingleFileTransfer::finishOffTransfer() {
- if (verifyReceviedData()) {
- onStateChange(FileTransfer::State(FileTransfer::State::Finished));
- session->sendTerminate(JinglePayload::Reason::Success);
- } else {
- onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed."));
- session->sendTerminate(JinglePayload::Reason::MediaError);
- }
- state = Terminated;
- waitOnHashTimer->stop();
+ setState(TryingCandidates);
+ transporter->startTryingRemoteCandidates();
}
+
void IncomingJingleFileTransfer::handleSessionInfoReceived(JinglePayload::ref jinglePayload) {
- if (state == Terminated) {
- return;
- }
+ SWIFT_LOG(debug) << std::endl;
+
JingleFileTransferHash::ref transferHash = jinglePayload->getPayload<JingleFileTransferHash>();
if (transferHash) {
- SWIFT_LOG(debug) << "Recevied hash information." << std::endl;
+ SWIFT_LOG(debug) << "Received hash information." << std::endl;
+ waitOnHashTimer->stop();
if (transferHash->getHashes().find("sha-1") != transferHash->getHashes().end()) {
- algo = "sha-1";
+ hashAlgorithm = "sha-1";
hash = transferHash->getHashes().find("sha-1")->second;
}
else if (transferHash->getHashes().find("md5") != transferHash->getHashes().end()) {
- algo = "md5";
+ hashAlgorithm = "md5";
hash = transferHash->getHashes().find("md5")->second;
}
- checkIfAllDataReceived();
+ if (state == WaitingForHash) {
+ checkHashAndTerminate();
+ }
+ }
+ else {
+ SWIFT_LOG(debug) << "Ignoring unknown session info" << std::endl;
}
}
void IncomingJingleFileTransfer::handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason) {
- SWIFT_LOG(debug) << "session terminate received" << std::endl;
- if (activeTransport) activeTransport->stop();
- if (reason && reason.get().type == JinglePayload::Reason::Cancel) {
- onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Other user canceled the transfer."));
+ SWIFT_LOG(debug) << std::endl;
+ if (state == Finished) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; }
+
+ if (state == Finished) {
+ SWIFT_LOG(debug) << "Already terminated" << std::endl;
+ return;
+ }
+
+ stopAll();
+ if (reason && reason->type == JinglePayload::Reason::Cancel) {
+ setFinishedState(FileTransfer::State::Canceled, FileTransferError(FileTransferError::PeerError));
+ }
+ else if (reason && reason->type == JinglePayload::Reason::Success) {
+ setFinishedState(FileTransfer::State::Finished, boost::optional<FileTransferError>());
+ }
+ else {
+ setFinishedState(FileTransfer::State::Failed, FileTransferError(FileTransferError::PeerError));
+ }
+}
+
+void IncomingJingleFileTransfer::checkHashAndTerminate() {
+ if (verifyData()) {
+ terminate(JinglePayload::Reason::Success);
}
- else if (reason && reason.get().type == JinglePayload::Reason::Success) {
- /*if (verifyReceviedData()) {
- onStateChange(FileTransfer::State(FileTransfer::State::Finished));
- } else {
- onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed."));
- }*/
+ else {
+ SWIFT_LOG(warning) << "Hash verification failed" << std::endl;
+ terminate(JinglePayload::Reason::MediaError);
}
- state = Terminated;
}
void IncomingJingleFileTransfer::checkIfAllDataReceived() {
- if (receivedBytes == fileSizeInBytes) {
+ if (receivedBytes == getFileSizeInBytes()) {
SWIFT_LOG(debug) << "All data received." << std::endl;
if (hash.empty()) {
- SWIFT_LOG(debug) << "No hash information yet. Waiting 5 seconds on hash info." << std::endl;
+ SWIFT_LOG(debug) << "No hash information yet. Waiting a while on hash info." << std::endl;
+ setState(WaitingForHash);
waitOnHashTimer->start();
- } else {
- SWIFT_LOG(debug) << "We already have hash info using " << algo << " algorithm. Finishing off transfer." << std::endl;
- finishOffTransfer();
+ }
+ else {
+ checkHashAndTerminate();
}
}
- else if (receivedBytes > fileSizeInBytes) {
+ else if (receivedBytes > getFileSizeInBytes()) {
SWIFT_LOG(debug) << "We got more than we could handle!" << std::endl;
+ terminate(JinglePayload::Reason::MediaError);
}
}
-void IncomingJingleFileTransfer::handleTransportDataReceived(const std::vector<unsigned char>& data) {
- SWIFT_LOG(debug) << data.size() << " bytes received" << std::endl;
- onProcessedBytes(data.size());
- stream->write(data);
+void IncomingJingleFileTransfer::handleWriteStreamDataReceived(
+ const std::vector<unsigned char>& data) {
+ hashCalculator->feedData(data);
receivedBytes += data.size();
checkIfAllDataReceived();
}
-void IncomingJingleFileTransfer::handleWriteStreamDataReceived(const std::vector<unsigned char>& data) {
- receivedBytes += data.size();
- checkIfAllDataReceived();
-}
-
-void IncomingJingleFileTransfer::useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) {
+void IncomingJingleFileTransfer::handleTransportReplaceReceived(
+ const JingleContentID& content, JingleTransportPayload::ref transport) {
SWIFT_LOG(debug) << std::endl;
- if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
- // get proxy client session from remoteCandidateSelector
- clientSession = candidateSelector->getS5BSession();
-
- // wait on <activated/> transport-info
- } else {
- // ask s5b client
- clientSession = candidateSelector->getS5BSession();
- if (clientSession) {
- state = Transferring;
- SWIFT_LOG(debug) << clientSession->getAddressPort().toString() << std::endl;
- clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
- clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
- clientSession->startReceiving(stream);
- } else {
- SWIFT_LOG(debug) << "No S5B client session found!!!" << std::endl;
- }
+ if (state != WaitingForFallbackOrTerminate) {
+ SWIFT_LOG(warning) << "Incorrect state" << std::endl;
+ return;
}
-}
-void IncomingJingleFileTransfer::useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) {
- SWIFT_LOG(debug) << std::endl;
+ if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) {
+ SWIFT_LOG(debug) << "transport replaced with IBB" << std::endl;
- if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
- // get proxy client session from s5bRegistry
- clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator(), crypto));
- clientSession->onSessionReady.connect(boost::bind(&IncomingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1));
- clientSession->start();
-
- // on reply send activate
- } else {
- // ask s5b server
- serverSession = s5bRegistry->getConnectedSession(s5bDestination);
- if (serverSession) {
- state = Transferring;
- serverSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
- serverSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
- serverSession->startTransfer();
- } else {
- SWIFT_LOG(debug) << "No S5B server session found!!!" << std::endl;
- }
+ startTransferring(transporter->createIBBReceiveSession(
+ ibbTransport->getSessionID(),
+ description->getOffers()[0].getSize(),
+ stream));
+ session->sendTransportAccept(content, ibbTransport);
+ }
+ else {
+ SWIFT_LOG(debug) << "Unknown replace transport" << std::endl;
+ session->sendTransportReject(content, transport);
}
}
-void IncomingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) {
- map.clear();
- foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) {
- map[candidate.cid] = candidate;
- }
+JingleContentID IncomingJingleFileTransfer::getContentID() const {
+ return JingleContentID(initialContent->getName(), initialContent->getCreator());
}
-
-void IncomingJingleFileTransfer::decideOnUsedTransport() {
- if (ourCandidate && theirCandidate) {
- if (ourCandidate->hasCandidateError() && theirCandidate->hasCandidateError()) {
- state = WaitingForFallbackOrTerminate;
- return;
- }
- std::string our_cid = ourCandidate->getCandidateUsed();
- std::string their_cid = theirCandidate->getCandidateUsed();
- if (ourCandidate->hasCandidateError() && !their_cid.empty()) {
- useTheirCandidateChoiceForTransfer(ourCandidates[their_cid]);
- onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
- }
- else if (theirCandidate->hasCandidateError() && !our_cid.empty()) {
- useOurCandidateChoiceForTransfer(theirCandidates[our_cid]);
- onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
- }
- else if (!our_cid.empty() && !their_cid.empty()) {
- // compare priorites, if same they win
- if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) {
- SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl;
- session->sendTerminate(JinglePayload::Reason::FailedTransport);
- onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Failed to negotiate candidate."));
- onFinished(FileTransferError(FileTransferError::PeerError));
- return;
- }
-
- JingleS5BTransportPayload::Candidate our_candidate = theirCandidates[our_cid];
- JingleS5BTransportPayload::Candidate their_candidate = ourCandidates[their_cid];
- if (our_candidate.priority > their_candidate.priority) {
- useOurCandidateChoiceForTransfer(our_candidate);
- }
- else if (our_candidate.priority < their_candidate.priority) {
- useTheirCandidateChoiceForTransfer(their_candidate);
- }
- else {
- useTheirCandidateChoiceForTransfer(their_candidate);
- }
- onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
- }
- else {
- assert(false);
- }
- } else {
- SWIFT_LOG(debug) << "Can't make a transport decision yet." << std::endl;
+bool IncomingJingleFileTransfer::verifyData() {
+ if (hashAlgorithm.empty() || hash.empty()) {
+ SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
+ return true;
+ }
+ if (hashAlgorithm == "sha-1") {
+ SWIFT_LOG(debug) << "Verify SHA-1 hash: " << (hash == hashCalculator->getSHA1String()) << std::endl;
+ return hash == hashCalculator->getSHA1String();
+ }
+ else if (hashAlgorithm == "md5") {
+ SWIFT_LOG(debug) << "Verify MD5 hash: " << (hash == hashCalculator->getMD5String()) << std::endl;
+ return hash == hashCalculator->getMD5String();
+ }
+ else {
+ SWIFT_LOG(debug) << "Unknown hash, skipping" << std::endl;
+ return true;
}
}
-void IncomingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) {
- if (error) {
- // indicate proxy error
- } else {
- // activate proxy
- activateProxySession(proxy);
- }
+void IncomingJingleFileTransfer::handleWaitOnHashTimerTicked() {
+ SWIFT_LOG(debug) << std::endl;
+ waitOnHashTimer->stop();
+ terminate(JinglePayload::Reason::Success);
}
-void IncomingJingleFileTransfer::activateProxySession(const JID &proxy) {
- S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
- proxyRequest->setSID(s5bSessionID);
- proxyRequest->setActivate(session->getInitiator());
+const JID& IncomingJingleFileTransfer::getSender() const {
+ return getInitiator();
+}
+
+const JID& IncomingJingleFileTransfer::getRecipient() const {
+ return getResponder();
+}
- boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Set, proxy, proxyRequest, router);
- request->onResponse.connect(boost::bind(&IncomingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2));
- request->send();
+void IncomingJingleFileTransfer::setState(State state) {
+ SWIFT_LOG(debug) << state << std::endl;
+ this->state = state;
+ onStateChanged(FileTransfer::State(getExternalState(state)));
}
-void IncomingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> /*request*/, ErrorPayload::ref error) {
+void IncomingJingleFileTransfer::setFinishedState(
+ FileTransfer::State::Type type, const boost::optional<FileTransferError>& error) {
SWIFT_LOG(debug) << std::endl;
- if (error) {
- SWIFT_LOG(debug) << "ERROR" << std::endl;
- } else {
- // send activated to other jingle party
- JingleS5BTransportPayload::ref proxyActivate = boost::make_shared<JingleS5BTransportPayload>();
- proxyActivate->setActivated(theirCandidate->getCandidateUsed());
- session->sendTransportInfo(getContentID(), proxyActivate);
-
- // start transferring
- clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
- clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
- clientSession->startReceiving(stream);
- onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+ this->state = Finished;
+ onStateChanged(type);
+ onFinished(error);
+}
+
+void IncomingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) {
+ if (error && state != WaitingForHash) {
+ terminate(JinglePayload::Reason::MediaError);
}
}
-void IncomingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref transport) {
- SWIFT_LOG(debug) << "transport info received" << std::endl;
- if (state == Terminated) {
- return;
+FileTransfer::State::Type IncomingJingleFileTransfer::getExternalState(State state) {
+ switch (state) {
+ case Initial: return FileTransfer::State::Initial;
+ case GeneratingInitialLocalCandidates: return FileTransfer::State::WaitingForStart;
+ case TryingCandidates: return FileTransfer::State::Negotiating;
+ case WaitingForPeerProxyActivate: return FileTransfer::State::Negotiating;
+ case WaitingForLocalProxyActivate: return FileTransfer::State::Negotiating;
+ case WaitingForFallbackOrTerminate: return FileTransfer::State::Negotiating;
+ case Transferring: return FileTransfer::State::Transferring;
+ case WaitingForHash: return FileTransfer::State::Transferring;
+ case Finished: return FileTransfer::State::Finished;
}
- if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
- if (!s5bPayload->getActivated().empty()) {
- if (ourCandidate->getCandidateUsed() == s5bPayload->getActivated()) {
- clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
- clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
- clientSession->startReceiving(stream);
- onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
- } else {
- SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl;
- JingleS5BTransportPayload::ref proxyError = boost::make_shared<JingleS5BTransportPayload>();
- proxyError->setProxyError(true);
- proxyError->setSessionID(s5bSessionID);
- session->sendTransportInfo(getContentID(), proxyError);
- }
- } else {
- theirCandidate = s5bPayload;
- decideOnUsedTransport();
- }
+ assert(false);
+ return FileTransfer::State::Initial;
+}
+
+void IncomingJingleFileTransfer::stopAll() {
+ if (state != Initial) {
+ writeStreamDataReceivedConnection.disconnect();
+ delete hashCalculator;
}
- else {
- SWIFT_LOG(debug) << "Expected something different here." << std::endl;
+ switch (state) {
+ case Initial: break;
+ case GeneratingInitialLocalCandidates: transporter->stopGeneratingLocalCandidates(); break;
+ case TryingCandidates: transporter->stopTryingRemoteCandidates(); break;
+ case WaitingForFallbackOrTerminate: break;
+ case WaitingForPeerProxyActivate: break;
+ case WaitingForLocalProxyActivate: transporter->stopActivatingProxy(); break;
+ case WaitingForHash: // Fallthrough
+ case Transferring:
+ assert(transportSession);
+ transferFinishedConnection.disconnect();
+ transportSession->stop();
+ transportSession.reset();
+ break;
+ case Finished: SWIFT_LOG(warning) << "Already finished" << std::endl; break;
+ }
+ if (state != Initial) {
+ delete transporter;
}
- /*localTransportCandidateSelectFinished = true;
- selectedLocalTransportCandidate = transport;
- if (candidateGenerator->isActualCandidate(transport)) {
- candidateSelector->setMinimumPriority(candidateGenerator->getPriority(transport));
- }*/
- //checkCandidateSelected();
}
-void IncomingJingleFileTransfer::handleTransportReplaceReceived(const JingleContentID& content, JingleTransportPayload::ref transport) {
- if (state == Terminated) {
- return;
+bool IncomingJingleFileTransfer::hasPriorityOnCandidateTie() const {
+ return false;
+}
+
+void IncomingJingleFileTransfer::fallback() {
+ if (options.isInBandAllowed()) {
+ setState(WaitingForFallbackOrTerminate);
}
- if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) {
- SWIFT_LOG(debug) << "transport replaced with IBB" << std::endl;
- setActiveTransport(createIBBTransport(ibbTransport));
- session->sendTransportAccept(content, ibbTransport);
- } else {
- SWIFT_LOG(debug) << "transport replaced failed" << std::endl;
- session->sendTransportReject(content, transport);
+ else {
+ terminate(JinglePayload::Reason::ConnectivityError);
}
}
-void IncomingJingleFileTransfer::stopActiveTransport() {
- if (activeTransport) {
- activeTransport->stop();
- activeTransport->onDataReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1));
+void IncomingJingleFileTransfer::startTransferViaRemoteCandidate() {
+ SWIFT_LOG(debug) << std::endl;
+
+ if (ourCandidateChoice->type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ setState(WaitingForPeerProxyActivate);
+ }
+ else {
+ startTransferring(createRemoteCandidateSession());
}
}
-JingleIncomingIBBTransport::ref IncomingJingleFileTransfer::createIBBTransport(JingleIBBTransportPayload::ref ibbTransport) {
- // TODO: getOffer() -> getOffers correction
- return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), getRecipient(), ibbTransport->getSessionID(), description->getOffers()[0].getSize(), router);
+void IncomingJingleFileTransfer::startTransferViaLocalCandidate() {
+ SWIFT_LOG(debug) << std::endl;
+
+ if (theirCandidateChoice->type == JingleS5BTransportPayload::Candidate::ProxyType) {
+ setState(WaitingForLocalProxyActivate);
+ transporter->startActivatingProxy(theirCandidateChoice->jid);
+ }
+ else {
+ startTransferring(createLocalCandidateSession());
+ }
}
-JingleContentID IncomingJingleFileTransfer::getContentID() const {
- return JingleContentID(initialContent->getName(), initialContent->getCreator());
+
+void IncomingJingleFileTransfer::startTransferring(boost::shared_ptr<TransportSession> transportSession) {
+ SWIFT_LOG(debug) << std::endl;
+
+ this->transportSession = transportSession;
+ transferFinishedConnection = transportSession->onFinished.connect(
+ boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+ setState(Transferring);
+ transportSession->start();
}
-void IncomingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) {
- if (state == Terminated) {
- return;
- }
+bool IncomingJingleFileTransfer::isWaitingForPeerProxyActivate() const {
+ return state == WaitingForPeerProxyActivate;
+}
- if (error) {
- session->sendTerminate(JinglePayload::Reason::ConnectivityError);
- onStateChange(FileTransfer::State(FileTransfer::State::Failed));
- onFinished(error);
- }
- //
+bool IncomingJingleFileTransfer::isWaitingForLocalProxyActivate() const {
+ return state == WaitingForLocalProxyActivate;
}
+bool IncomingJingleFileTransfer::isTryingCandidates() const {
+ return state == TryingCandidates;
+}
+
+boost::shared_ptr<TransportSession> IncomingJingleFileTransfer::createLocalCandidateSession() {
+ return transporter->createLocalCandidateSession(stream);
+}
+
+boost::shared_ptr<TransportSession> IncomingJingleFileTransfer::createRemoteCandidateSession() {
+ return transporter->createRemoteCandidateSession(stream);
+}
+
+void IncomingJingleFileTransfer::terminate(JinglePayload::Reason::Type reason) {
+ SWIFT_LOG(debug) << reason << std::endl;
+
+ if (state != Finished) {
+ session->sendTerminate(reason);
+ }
+ stopAll();
+ setFinishedState(getExternalFinishedState(reason), getFileTransferError(reason));
}