diff options
Diffstat (limited to 'Swiften/FileTransfer/IncomingJingleFileTransfer.cpp')
-rw-r--r-- | Swiften/FileTransfer/IncomingJingleFileTransfer.cpp | 654 |
1 files changed, 264 insertions, 390 deletions
diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp index 808ff58..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,510 +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, - TimerFactory* timerFactory) : - ourJID(ourJID), - session(session), - router(router), + FileTransferTransporterFactory* transporterFactory, + TimerFactory* timerFactory, + CryptoProvider* crypto) : + 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() ); - 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()); - s5bRegistry->addWriteBytestream(s5bDestination, stream); - fillCandidateMap(theirCandidates, s5bTransport); - candidateSelector->addRemoteTransportCandidates(s5bTransport); - candidateSelector->setRequesterTargtet(session->getInitiator(), ourJID); - s5bTransport->setSessionID(s5bSessionID); - candidateGenerator->generateLocalTransportCandidates(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); } -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())); - 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::startTransferViaRemoteCandidate() { + SWIFT_LOG(debug) << std::endl; + + if (ourCandidateChoice->type == JingleS5BTransportPayload::Candidate::ProxyType) { + setState(WaitingForPeerProxyActivate); + } + else { + startTransferring(createRemoteCandidateSession()); } } -void IncomingJingleFileTransfer::stopActiveTransport() { - if (activeTransport) { - activeTransport->stop(); - activeTransport->onDataReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1)); +void IncomingJingleFileTransfer::startTransferViaLocalCandidate() { + SWIFT_LOG(debug) << std::endl; + + if (theirCandidateChoice->type == JingleS5BTransportPayload::Candidate::ProxyType) { + setState(WaitingForLocalProxyActivate); + transporter->startActivatingProxy(theirCandidateChoice->jid); + } + else { + startTransferring(createLocalCandidateSession()); } } -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::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(); } -JingleContentID IncomingJingleFileTransfer::getContentID() const { - return JingleContentID(initialContent->getName(), initialContent->getCreator()); +bool IncomingJingleFileTransfer::isWaitingForPeerProxyActivate() const { + return state == WaitingForPeerProxyActivate; } -void IncomingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) { - if (state == Terminated) { - return; - } +bool IncomingJingleFileTransfer::isWaitingForLocalProxyActivate() const { + return state == WaitingForLocalProxyActivate; +} - if (error) { - session->sendTerminate(JinglePayload::Reason::ConnectivityError); - onStateChange(FileTransfer::State(FileTransfer::State::Failed)); - onFinished(error); - } - // +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)); } |