summaryrefslogtreecommitdiffstats
blob: d5d4aaf693f346a251c220fa41fcca0834130e61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * Copyright (c) 2011 Tobias Markmann
 * Licensed under the simplified BSD license.
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 */

#include <Swiften/FileTransfer/FileTransferManagerImpl.h>

#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
#include <boost/cstdint.hpp>

#include <Swiften/Base/foreach.h>
#include <Swiften/Base/Log.h>
#include "Swiften/Disco/EntityCapsProvider.h"
#include <Swiften/JID/JID.h>
#include <Swiften/Elements/StreamInitiationFileInfo.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.h>
#include <Swiften/FileTransfer/ConnectivityManager.h>
#include <Swiften/FileTransfer/OutgoingFileTransferManager.h>
#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Network/ConnectionServerFactory.h>
#include <Swiften/Network/HostAddress.h>
#include <Swiften/Network/NATTraverser.h>

#include <Swiften/Base/BoostFilesystemVersion.h>

namespace Swift {

FileTransferManagerImpl::FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, NATTraverser* natTraverser) : ownJID(ownFullJID), jingleSM(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), presenceOracle(presOracle), connectionServerFactory(connectionServerFactory), bytestreamServer(NULL), s5bProxyFinder(NULL) {
	assert(!ownFullJID.isBare());

	connectivityManager = new ConnectivityManager(natTraverser);
	bytestreamRegistry = new SOCKS5BytestreamRegistry();
	bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);

	localCandidateGeneratorFactory = new DefaultLocalJingleTransportCandidateGeneratorFactory(connectivityManager, bytestreamRegistry, bytestreamProxy, ownFullJID);
	remoteCandidateSelectorFactory = new DefaultRemoteJingleTransportCandidateSelectorFactory(connectionFactory, timerFactory);
	outgoingFTManager = new OutgoingFileTransferManager(jingleSM, iqRouter, capsProvider, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy);
	incomingFTManager = new IncomingFileTransferManager(jingleSM, iqRouter, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy, timerFactory);
	incomingFTManager->onIncomingFileTransfer.connect(onIncomingFileTransfer);
}

FileTransferManagerImpl::~FileTransferManagerImpl() {
	if (s5bProxyFinder) {
		s5bProxyFinder->stop();
		delete s5bProxyFinder;
	}
	if (bytestreamServer) {
		bytestreamServer->stop();
		delete bytestreamServer;
	}
	delete incomingFTManager;
	delete outgoingFTManager;
	delete remoteCandidateSelectorFactory;
	delete localCandidateGeneratorFactory;
	delete connectivityManager;
}

void FileTransferManagerImpl::startListeningOnPort(int port) {
	// TODO: create a server for each interface we're on
	SWIFT_LOG(debug) << "Start listening on port " << port << " and hope it's not in use." << std::endl;
	boost::shared_ptr<ConnectionServer> server = connectionServerFactory->createConnectionServer(HostAddress("0.0.0.0"), port);
	server->start();
	bytestreamServer = new SOCKS5BytestreamServer(server, bytestreamRegistry);
	bytestreamServer->start();
	connectivityManager->addListeningPort(port);

	s5bProxyFinder = new SOCKS5BytestreamProxyFinder(ownJID.getDomain(), iqRouter);
	s5bProxyFinder->onProxyFound.connect(boost::bind(&FileTransferManagerImpl::addS5BProxy, this, _1));
	s5bProxyFinder->start();
}

void FileTransferManagerImpl::addS5BProxy(S5BProxyRequest::ref proxy) {
	bytestreamProxy->addS5BProxy(proxy);
}

boost::optional<JID> FileTransferManagerImpl::highestPriorityJIDSupportingFileTransfer(const JID& bareJID) {
	JID fullReceipientJID;
	int priority = INT_MIN;
	
	//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
	std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);

	//iterate over them
	foreach(Presence::ref pres, presences) {
		if (pres->getPriority() > priority) {
			// look up caps from the jid
			DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
			if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && (info->hasFeature(DiscoInfo::JingleTransportsIBBFeature) || info->hasFeature(DiscoInfo::JingleTransportsS5BFeature))) {
			
				priority = pres->getPriority();
				fullReceipientJID = pres->getFrom();
			}
		}
	}
	
	return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
}

OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream) {
#if BOOST_FILESYSTEM_VERSION == 2 // TODO: Delete this when boost 1.44 becomes a minimum requirement, and we no longer need v2
	std::string filename = filepath.filename();
#else
	std::string filename = filepath.filename().string();
#endif

	boost::uintmax_t sizeInBytes = boost::filesystem::file_size(filepath);
	boost::posix_time::ptime lastModified = boost::posix_time::from_time_t(boost::filesystem::last_write_time(filepath));
	return createOutgoingFileTransfer(to, filename, description, sizeInBytes, lastModified, bytestream);
}

OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream) {
	StreamInitiationFileInfo fileInfo;
	fileInfo.setDate(lastModified);
	fileInfo.setSize(sizeInBytes);
	fileInfo.setName(filename);
	fileInfo.setDescription(description);
	
	JID receipient = to;
	
	if(receipient.isBare()) {
		boost::optional<JID> fullJID = highestPriorityJIDSupportingFileTransfer(receipient);
		if (fullJID.is_initialized()) {
			receipient = fullJID.get();
		} else {
			return OutgoingFileTransfer::ref();
		}
	}
	
	return outgoingFTManager->createOutgoingFileTransfer(ownJID, receipient, bytestream, fileInfo);
}

}