diff options
Diffstat (limited to 'Swiften/FileTransfer/IncomingJingleFileTransfer.cpp')
| -rw-r--r-- | Swiften/FileTransfer/IncomingJingleFileTransfer.cpp | 651 | 
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));  } | 
 Swift
 Swift