diff options
Diffstat (limited to 'Swiften/FileTransfer/JingleFileTransfer.cpp')
-rw-r--r-- | Swiften/FileTransfer/JingleFileTransfer.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/Swiften/FileTransfer/JingleFileTransfer.cpp b/Swiften/FileTransfer/JingleFileTransfer.cpp new file mode 100644 index 0000000..6eecaa2 --- /dev/null +++ b/Swiften/FileTransfer/JingleFileTransfer.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Swiften/FileTransfer/JingleFileTransfer.h> + +#include <boost/typeof/typeof.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/StringCodecs/Hexify.h> +#include <Swiften/Jingle/JingleSession.h> +#include <Swiften/FileTransfer/FileTransferTransporter.h> +#include <Swiften/Base/Log.h> + +using namespace Swift; + +JingleFileTransfer::JingleFileTransfer( + boost::shared_ptr<JingleSession> session, + const JID& target, + FileTransferTransporterFactory* transporterFactory) : + session(session), + target(target), + transporterFactory(transporterFactory), + transporter(NULL), + ourCandidateSelectFinished(false), + theirCandidateSelectFinished(false) { + + session->addListener(this); + +} + +JingleFileTransfer::~JingleFileTransfer() { + session->removeListener(this); +} + +void JingleFileTransfer::fillCandidateMap(CandidateMap& map, const std::vector<JingleS5BTransportPayload::Candidate>& candidates) { + map.clear(); + foreach (JingleS5BTransportPayload::Candidate candidate, candidates) { + map[candidate.cid] = candidate; + } +} + +/* +std::string JingleFileTransfer::getS5BDstAddr(const JID& requester, const JID& target) const { + return Hexify::hexify(crypto->getSHA1Hash( + createSafeByteArray(s5bSessionID + requester.toString() + target.toString()))); +} +*/ + +const JID& JingleFileTransfer::getInitiator() const { + return session->getInitiator(); +} + +const JID& JingleFileTransfer::getResponder() const { + return target; +} + +FileTransfer::State::Type JingleFileTransfer::getExternalFinishedState(JinglePayload::Reason::Type reason) { + if (reason == JinglePayload::Reason::Cancel || reason == JinglePayload::Reason::Decline) { + return FileTransfer::State::Canceled; + } + else if (reason == JinglePayload::Reason::Success) { + return FileTransfer::State::Finished; + } + else { + return FileTransfer::State::Failed; + } +} + +boost::optional<FileTransferError> JingleFileTransfer::getFileTransferError(JinglePayload::Reason::Type reason) { + if (reason == JinglePayload::Reason::Success) { + return boost::optional<FileTransferError>(); + } + else { + return boost::optional<FileTransferError>(FileTransferError::UnknownError); + } +} + +void JingleFileTransfer::handleRemoteTransportCandidateSelectFinished( + const std::string& s5bSessionID, const boost::optional<JingleS5BTransportPayload::Candidate>& candidate) { + SWIFT_LOG(debug) << std::endl; + + ourCandidateChoice = candidate; + ourCandidateSelectFinished = true; + + JingleS5BTransportPayload::ref s5bPayload = boost::make_shared<JingleS5BTransportPayload>(); + s5bPayload->setSessionID(s5bSessionID); + if (candidate) { + s5bPayload->setCandidateUsed(candidate->cid); + } + else { + s5bPayload->setCandidateError(true); + } + candidateSelectRequestID = session->sendTransportInfo(getContentID(), s5bPayload); + + decideOnCandidates(); +} + +// decide on candidates according to http://xmpp.org/extensions/xep-0260.html#complete +void JingleFileTransfer::decideOnCandidates() { + SWIFT_LOG(debug) << std::endl; + if (!ourCandidateSelectFinished || !theirCandidateSelectFinished) { + SWIFT_LOG(debug) << "Can't make a decision yet!" << std::endl; + return; + } + if (!ourCandidateChoice && !theirCandidateChoice) { + SWIFT_LOG(debug) << "No candidates succeeded." << std::endl; + fallback(); + } + else if (ourCandidateChoice && !theirCandidateChoice) { + startTransferViaRemoteCandidate(); + } + else if (theirCandidateChoice && !ourCandidateChoice) { + startTransferViaLocalCandidate(); + } + else { + SWIFT_LOG(debug) << "Choosing between candidates " + << ourCandidateChoice->cid << "(" << ourCandidateChoice->priority << ")" << " and " + << theirCandidateChoice->cid << "(" << theirCandidateChoice->priority << ")" << std::endl; + if (ourCandidateChoice->priority > theirCandidateChoice->priority) { + startTransferViaRemoteCandidate(); + } + else if (ourCandidateChoice->priority < theirCandidateChoice->priority) { + startTransferViaLocalCandidate(); + } + else { + if (hasPriorityOnCandidateTie()) { + startTransferViaRemoteCandidate(); + } + else { + startTransferViaLocalCandidate(); + } + } + } +} + +void JingleFileTransfer::handleProxyActivateFinished( + const std::string& s5bSessionID, ErrorPayload::ref error) { + SWIFT_LOG(debug) << std::endl; + if (!isWaitingForLocalProxyActivate()) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; } + + if (error) { + SWIFT_LOG(debug) << "Error activating proxy" << std::endl; + JingleS5BTransportPayload::ref proxyError = boost::make_shared<JingleS5BTransportPayload>(); + proxyError->setSessionID(s5bSessionID); + proxyError->setProxyError(true); + session->sendTransportInfo(getContentID(), proxyError); + fallback(); + } + else { + JingleS5BTransportPayload::ref proxyActivate = boost::make_shared<JingleS5BTransportPayload>(); + proxyActivate->setSessionID(s5bSessionID); + proxyActivate->setActivated(theirCandidateChoice->cid); + session->sendTransportInfo(getContentID(), proxyActivate); + startTransferring(createRemoteCandidateSession()); + } +} + +void JingleFileTransfer::handleTransportInfoReceived( + const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) { + SWIFT_LOG(debug) << std::endl; + + if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) { + if (s5bPayload->hasCandidateError() || !s5bPayload->getCandidateUsed().empty()) { + SWIFT_LOG(debug) << "Received candidate decision from peer" << std::endl; + if (!isTryingCandidates()) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; } + + theirCandidateSelectFinished = true; + if (!s5bPayload->hasCandidateError()) { + BOOST_AUTO(theirCandidate, localCandidates.find(s5bPayload->getCandidateUsed())); + if (theirCandidate == localCandidates.end()) { + SWIFT_LOG(warning) << "Got invalid candidate" << std::endl; + terminate(JinglePayload::Reason::GeneralError); + return; + } + theirCandidateChoice = theirCandidate->second; + } + decideOnCandidates(); + } + else if (!s5bPayload->getActivated().empty()) { + SWIFT_LOG(debug) << "Received peer activate from peer" << std::endl; + if (!isWaitingForPeerProxyActivate()) { SWIFT_LOG(warning) << "Incorrect state" << std::endl; return; } + + if (ourCandidateChoice->cid == s5bPayload->getActivated()) { + startTransferring(createRemoteCandidateSession()); + } + else { + SWIFT_LOG(warning) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl; + terminate(JinglePayload::Reason::GeneralError); + } + } + else { + SWIFT_LOG(debug) << "Ignoring unknown info" << std::endl; + } + } + else { + SWIFT_LOG(debug) << "Ignoring unknown info" << std::endl; + } +} + +void JingleFileTransfer::setTransporter(FileTransferTransporter* transporter) { + this->transporter = transporter; + localTransportCandidatesGeneratedConnection = transporter->onLocalCandidatesGenerated.connect( + boost::bind(&JingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1, _2)); + remoteTransportCandidateSelectFinishedConnection = transporter->onRemoteCandidateSelectFinished.connect( + boost::bind(&JingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1, _2)); + proxyActivatedConnection = transporter->onProxyActivated.connect( + boost::bind(&JingleFileTransfer::handleProxyActivateFinished, this, _1, _2)); +} + |