1 // rTorrent - BitTorrent client
2 // Copyright (C) 2005-2011, Jari Sundell
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 //
18 // In addition, as a special exception, the copyright holders give
19 // permission to link the code of portions of this program with the
20 // OpenSSL library under certain conditions as described in each
21 // individual source file, and distribute linked combinations
22 // including the two.
23 //
24 // You must obey the GNU General Public License in all respects for
25 // all of the code used other than OpenSSL. If you modify file(s)
26 // with this exception, you may extend this exception to your version
27 // of the file(s), but you are not obligated to do so. If you do not
28 // wish to do so, delete this exception statement from your version.
29 // If you delete this exception statement from all source files in the
30 // program, then also delete it here.
31 //
32 // Contact: Jari Sundell <jaris@ifi.uio.no>
33 //
34 // Skomakerveien 33
35 // 3185 Skoppum, NORWAY
36
37 #include "config.h"
38
39 #include <cstdio>
40 #include <cstring>
41 #include <fstream>
42 #include <sstream>
43 #include <unistd.h>
44 #include <sys/select.h>
45 #include <rak/address_info.h>
46 #include <rak/error_number.h>
47 #include <rak/regex.h>
48 #include <rak/path.h>
49 #include <rak/string_manip.h>
50 #include <torrent/utils/resume.h>
51 #include <torrent/object.h>
52 #include <torrent/connection_manager.h>
53 #include <torrent/error.h>
54 #include <torrent/exceptions.h>
55 #include <torrent/object_stream.h>
56 #include <torrent/tracker_list.h>
57 #include <torrent/throttle.h>
58 #include <torrent/utils/log.h>
59
60 #include "rpc/parse_commands.h"
61 #include "utils/directory.h"
62 #include "utils/file_status_cache.h"
63
64 #include "globals.h"
65 #include "curl_get.h"
66 #include "control.h"
67 #include "download.h"
68 #include "download_factory.h"
69 #include "download_store.h"
70 #include "http_queue.h"
71 #include "manager.h"
72 #include "poll_manager.h"
73 #include "view.h"
74
75 namespace core {
76
77 const int Manager::create_start;
78 const int Manager::create_tied;
79 const int Manager::create_quiet;
80 const int Manager::create_raw_data;
81
82 void
push_log(const char * msg)83 Manager::push_log(const char* msg) {
84 m_log_important->lock_and_push_log(msg, strlen(msg), 0);
85 m_log_complete->lock_and_push_log(msg, strlen(msg), 0);
86 }
87
Manager()88 Manager::Manager() :
89 m_hashingView(NULL),
90 m_log_important(torrent::log_open_log_buffer("important")),
91 m_log_complete(torrent::log_open_log_buffer("complete"))
92 {
93 m_downloadStore = new DownloadStore();
94 m_downloadList = new DownloadList();
95 m_fileStatusCache = new FileStatusCache();
96 m_httpQueue = new HttpQueue();
97 m_httpStack = new CurlStack();
98
99 torrent::Throttle* unthrottled = torrent::Throttle::create_throttle();
100 unthrottled->set_max_rate(0);
101 m_throttles["NULL"] = std::make_pair(unthrottled, unthrottled);
102 }
103
~Manager()104 Manager::~Manager() {
105 torrent::Throttle::destroy_throttle(m_throttles["NULL"].first);
106 delete m_downloadList;
107
108 // TODO: Clean up logs objects.
109
110 delete m_downloadStore;
111 delete m_httpQueue;
112 delete m_fileStatusCache;
113 }
114
115 void
set_hashing_view(View * v)116 Manager::set_hashing_view(View* v) {
117 if (v == NULL || m_hashingView != NULL)
118 throw torrent::internal_error("Manager::set_hashing_view(...) received NULL or is already set.");
119
120 m_hashingView = v;
121 m_hashingView->signal_changed().push_back(std::bind(&Manager::receive_hashing_changed, this));
122 }
123
124 torrent::ThrottlePair
get_throttle(const std::string & name)125 Manager::get_throttle(const std::string& name) {
126 ThrottleMap::const_iterator itr = m_throttles.find(name);
127 torrent::ThrottlePair throttles = (itr == m_throttles.end() ? torrent::ThrottlePair(NULL, NULL) : itr->second);
128
129 if (throttles.first == NULL)
130 throttles.first = torrent::up_throttle_global();
131
132 if (throttles.second == NULL)
133 throttles.second = torrent::down_throttle_global();
134
135 return throttles;
136 }
137
138 void
set_address_throttle(uint32_t begin,uint32_t end,torrent::ThrottlePair throttles)139 Manager::set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles) {
140 m_addressThrottles.set_merge(begin, end, throttles);
141 torrent::connection_manager()->address_throttle() = std::bind(&core::Manager::get_address_throttle, control->core(), std::placeholders::_1);
142 }
143
144 torrent::ThrottlePair
get_address_throttle(const sockaddr * addr)145 Manager::get_address_throttle(const sockaddr* addr) {
146 return m_addressThrottles.get(rak::socket_address::cast_from(addr)->sa_inet()->address_h(), torrent::ThrottlePair(NULL, NULL));
147 }
148
149 // Most of this should be possible to move out.
150 void
initialize_second()151 Manager::initialize_second() {
152 torrent::Http::slot_factory() = std::bind(&CurlStack::new_object, m_httpStack);
153 m_httpQueue->set_slot_factory(std::bind(&CurlStack::new_object, m_httpStack));
154
155 CurlStack::global_init();
156 }
157
158 void
cleanup()159 Manager::cleanup() {
160 // Need to disconnect log signals? Not really since we won't receive
161 // any more.
162
163 m_downloadList->clear();
164
165 // When we implement asynchronous DNS lookups, we need to cancel them
166 // here before the torrent::* objects are deleted.
167
168 torrent::cleanup();
169
170 delete m_httpStack;
171 CurlStack::global_cleanup();
172
173 }
174
175 void
shutdown(bool force)176 Manager::shutdown(bool force) {
177 if (!force)
178 std::for_each(m_downloadList->begin(), m_downloadList->end(), std::bind1st(std::mem_fun(&DownloadList::pause_default), m_downloadList));
179 else
180 std::for_each(m_downloadList->begin(), m_downloadList->end(), std::bind1st(std::mem_fun(&DownloadList::close_quick), m_downloadList));
181 }
182
183 void
listen_open()184 Manager::listen_open() {
185 // This stuff really should be moved outside of manager, make it
186 // part of the init script.
187 if (!rpc::call_command_value("network.port_open"))
188 return;
189
190 int portFirst, portLast;
191 torrent::Object portRange = rpc::call_command("network.port_range");
192
193 if (portRange.is_string()) {
194 if (std::sscanf(portRange.as_string().c_str(), "%i-%i", &portFirst, &portLast) != 2)
195 throw torrent::input_error("Invalid port_range argument.");
196
197 // } else if (portRange.is_list()) {
198
199 } else {
200 throw torrent::input_error("Invalid port_range argument.");
201 }
202
203 if (portFirst > portLast || portLast >= (1 << 16))
204 throw torrent::input_error("Invalid port range.");
205
206 if (rpc::call_command_value("network.port_random")) {
207 int boundary = portFirst + random() % (portLast - portFirst + 1);
208
209 if (torrent::connection_manager()->listen_open(boundary, portLast) ||
210 torrent::connection_manager()->listen_open(portFirst, boundary))
211 return;
212
213 } else {
214 if (torrent::connection_manager()->listen_open(portFirst, portLast))
215 return;
216 }
217
218 throw torrent::input_error("Could not open/bind port for listening: " + std::string(rak::error_number::current().c_str()));
219 }
220
221 std::string
bind_address() const222 Manager::bind_address() const {
223 return rak::socket_address::cast_from(torrent::connection_manager()->bind_address())->address_str();
224 }
225
226 void
set_bind_address(const std::string & addr)227 Manager::set_bind_address(const std::string& addr) {
228 int err;
229 rak::address_info* ai;
230
231 if ((err = rak::address_info::get_address_info(addr.c_str(), PF_INET, SOCK_STREAM, &ai)) != 0 &&
232 (err = rak::address_info::get_address_info(addr.c_str(), PF_INET6, SOCK_STREAM, &ai)) != 0)
233 throw torrent::input_error("Could not set bind address: " + std::string(rak::address_info::strerror(err)) + ".");
234
235 try {
236
237 if (torrent::connection_manager()->listen_port() != 0) {
238 torrent::connection_manager()->listen_close();
239 torrent::connection_manager()->set_bind_address(ai->address()->c_sockaddr());
240 listen_open();
241
242 } else {
243 torrent::connection_manager()->set_bind_address(ai->address()->c_sockaddr());
244 }
245
246 m_httpStack->set_bind_address(!ai->address()->is_address_any() ? ai->address()->address_str() : std::string());
247
248 rak::address_info::free_address_info(ai);
249
250 } catch (torrent::input_error& e) {
251 rak::address_info::free_address_info(ai);
252 throw e;
253 }
254 }
255
256 std::string
local_address() const257 Manager::local_address() const {
258 return rak::socket_address::cast_from(torrent::connection_manager()->local_address())->address_str();
259 }
260
261 void
set_local_address(const std::string & addr)262 Manager::set_local_address(const std::string& addr) {
263 int err;
264 rak::address_info* ai;
265
266 if ((err = rak::address_info::get_address_info(addr.c_str(), PF_INET, SOCK_STREAM, &ai)) != 0 &&
267 (err = rak::address_info::get_address_info(addr.c_str(), PF_INET6, SOCK_STREAM, &ai)) != 0)
268 throw torrent::input_error("Could not set local address: " + std::string(rak::address_info::strerror(err)) + ".");
269
270 try {
271
272 torrent::connection_manager()->set_local_address(ai->address()->c_sockaddr());
273 rak::address_info::free_address_info(ai);
274
275 } catch (torrent::input_error& e) {
276 rak::address_info::free_address_info(ai);
277 throw e;
278 }
279 }
280
281 std::string
proxy_address() const282 Manager::proxy_address() const {
283 return rak::socket_address::cast_from(torrent::connection_manager()->proxy_address())->address_str();
284 }
285
286 void
set_proxy_address(const std::string & addr)287 Manager::set_proxy_address(const std::string& addr) {
288 int port;
289 rak::address_info* ai;
290
291 char buf[addr.length() + 1];
292
293 int err = std::sscanf(addr.c_str(), "%[^:]:%i", buf, &port);
294
295 if (err <= 0)
296 throw torrent::input_error("Could not parse proxy address.");
297
298 if (err == 1)
299 port = 80;
300
301 if ((err = rak::address_info::get_address_info(buf, PF_INET, SOCK_STREAM, &ai)) != 0)
302 throw torrent::input_error("Could not set proxy address: " + std::string(rak::address_info::strerror(err)) + ".");
303
304 try {
305
306 ai->address()->set_port(port);
307 torrent::connection_manager()->set_proxy_address(ai->address()->c_sockaddr());
308
309 rak::address_info::free_address_info(ai);
310
311 } catch (torrent::input_error& e) {
312 rak::address_info::free_address_info(ai);
313 throw e;
314 }
315 }
316
317 void
receive_http_failed(std::string msg)318 Manager::receive_http_failed(std::string msg) {
319 push_log_std("Http download error: \"" + msg + "\"");
320 }
321
322 void
try_create_download(const std::string & uri,int flags,const command_list_type & commands)323 Manager::try_create_download(const std::string& uri, int flags, const command_list_type& commands) {
324 // If the path was attempted loaded before, skip it.
325 if ((flags & create_tied) &&
326 !(flags & create_raw_data) &&
327 !is_network_uri(uri) &&
328 !is_magnet_uri(uri) &&
329 !file_status_cache()->insert(uri, 0))
330 return;
331
332 // Adding download.
333 DownloadFactory* f = new DownloadFactory(this);
334
335 f->variables()["tied_to_file"] = (int64_t)(bool)(flags & create_tied);
336 f->commands().insert(f->commands().end(), commands.begin(), commands.end());
337
338 f->set_start(flags & create_start);
339 f->set_print_log(!(flags & create_quiet));
340 f->slot_finished(std::bind(&rak::call_delete_func<core::DownloadFactory>, f));
341
342 if (flags & create_raw_data)
343 f->load_raw_data(uri);
344 else
345 f->load(uri);
346
347 f->commit();
348 }
349
350 void
try_create_download_from_meta_download(torrent::Object * bencode,const std::string & metafile)351 Manager::try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile) {
352 DownloadFactory* f = new DownloadFactory(this);
353
354 f->variables()["tied_to_file"] = (int64_t)true;
355 f->variables()["tied_file"] = metafile;
356
357 torrent::Object& meta = bencode->get_key("rtorrent_meta_download");
358 torrent::Object::list_type& commands = meta.get_key_list("commands");
359 for (torrent::Object::list_type::const_iterator itr = commands.begin(); itr != commands.end(); ++itr)
360 f->commands().insert(f->commands().end(), itr->as_string());
361
362 f->set_start(meta.get_key_value("start"));
363 f->set_print_log(meta.get_key_value("print_log"));
364 f->slot_finished(std::bind(&rak::call_delete_func<core::DownloadFactory>, f));
365
366 // Bit of a waste to create the bencode repesentation here
367 // only to have the DownloadFactory decode it.
368 std::stringstream s;
369 s.imbue(std::locale::classic());
370 s << *bencode;
371 f->load_raw_data(s.str());
372 f->commit();
373 }
374
375 utils::Directory
path_expand_transform(std::string path,const utils::directory_entry & entry)376 path_expand_transform(std::string path, const utils::directory_entry& entry) {
377 return path + entry.d_name;
378 }
379
380 // Move this somewhere better.
381 void
path_expand(std::vector<std::string> * paths,const std::string & pattern)382 path_expand(std::vector<std::string>* paths, const std::string& pattern) {
383 std::vector<utils::Directory> currentCache;
384 std::vector<utils::Directory> nextCache;
385
386 rak::split_iterator_t<std::string> first = rak::split_iterator(pattern, '/');
387 rak::split_iterator_t<std::string> last = rak::split_iterator(pattern);
388
389 if (first == last)
390 return;
391
392 // Check for initial '/' that indicates the root.
393 if ((*first).empty()) {
394 currentCache.push_back(utils::Directory("/"));
395 ++first;
396 } else if (rak::trim(*first) == "~") {
397 currentCache.push_back(utils::Directory("~"));
398 ++first;
399 } else {
400 currentCache.push_back(utils::Directory("."));
401 }
402
403 // Might be an idea to use depth-first search instead.
404
405 for (; first != last; ++first) {
406 rak::regex r(*first);
407
408 if (r.pattern().empty())
409 continue;
410
411 // Special case for ".."?
412
413 for (std::vector<utils::Directory>::iterator itr = currentCache.begin(); itr != currentCache.end(); ++itr) {
414 // Only include filenames starting with '.' if the pattern
415 // starts with the same.
416 itr->update((r.pattern()[0] != '.') ? utils::Directory::update_hide_dot : 0);
417 itr->erase(std::remove_if(itr->begin(), itr->end(), rak::on(rak::mem_ref(&utils::directory_entry::d_name), std::not1(r))), itr->end());
418
419 std::transform(itr->begin(), itr->end(), std::back_inserter(nextCache), rak::bind1st(std::ptr_fun(&path_expand_transform), itr->path() + (itr->path() == "/" ? "" : "/")));
420 }
421
422 currentCache.clear();
423 currentCache.swap(nextCache);
424 }
425
426 std::transform(currentCache.begin(), currentCache.end(), std::back_inserter(*paths), std::mem_fun_ref(&utils::Directory::path));
427 }
428
429 bool
manager_equal_tied(const std::string & path,Download * download)430 manager_equal_tied(const std::string& path, Download* download) {
431 return path == rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
432 }
433
434 void
try_create_download_expand(const std::string & uri,int flags,command_list_type commands)435 Manager::try_create_download_expand(const std::string& uri, int flags, command_list_type commands) {
436 if (flags & create_raw_data) {
437 try_create_download(uri, flags, commands);
438 return;
439 }
440
441 std::vector<std::string> paths;
442 paths.reserve(256);
443
444 path_expand(&paths, uri);
445
446 if (!paths.empty())
447 for (std::vector<std::string>::iterator itr = paths.begin(); itr != paths.end(); ++itr)
448 try_create_download(*itr, flags, commands);
449
450 else
451 try_create_download(uri, flags, commands);
452 }
453
454 // DownloadList's hashing related functions don't actually start the
455 // hashing, it only reacts to events. This functions checks the
456 // hashing view and starts hashing if nessesary.
457 void
receive_hashing_changed()458 Manager::receive_hashing_changed() {
459 bool foundHashing = std::find_if(m_hashingView->begin_visible(), m_hashingView->end_visible(),
460 std::mem_fun(&Download::is_hash_checking)) != m_hashingView->end_visible();
461
462 // Try quick hashing all those with hashing == initial, set them to
463 // something else when failed.
464 for (View::iterator itr = m_hashingView->begin_visible(), last = m_hashingView->end_visible(); itr != last; ++itr) {
465 if ((*itr)->is_hash_checked())
466 throw torrent::internal_error("core::Manager::receive_hashing_changed() (*itr)->is_hash_checked().");
467
468 if ((*itr)->is_hash_checking() || (*itr)->is_hash_failed())
469 continue;
470
471 bool tryQuick =
472 rpc::call_command_value("d.hashing", rpc::make_target(*itr)) == Download::variable_hashing_initial &&
473 (*itr)->download()->file_list()->bitfield()->empty();
474
475 if (!tryQuick && foundHashing)
476 continue;
477
478 try {
479 m_downloadList->open_throw(*itr);
480
481 // Since the bitfield is allocated on loading of resume load or
482 // hash start, and unallocated on close, we know that if it it
483 // not empty then we have already loaded any existing resume
484 // data.
485 if ((*itr)->download()->file_list()->bitfield()->empty())
486 torrent::resume_load_progress(*(*itr)->download(), (*itr)->download()->bencode()->get_key("libtorrent_resume"));
487
488 if (tryQuick) {
489 if ((*itr)->download()->hash_check(true))
490 continue;
491
492 (*itr)->download()->hash_stop();
493
494 if (foundHashing) {
495 rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_rehash, rpc::make_target(*itr));
496 continue;
497 }
498 }
499
500 (*itr)->download()->hash_check(false);
501 foundHashing = true;
502
503 } catch (torrent::local_error& e) {
504 if (tryQuick) {
505 // Make sure we don't repeat the quick hashing.
506 rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_rehash, rpc::make_target(*itr));
507
508 } else {
509 (*itr)->set_hash_failed(true);
510 lt_log_print(torrent::LOG_TORRENT_ERROR, "Hashing failed: %s", e.what());
511 }
512 }
513 }
514 }
515
516 }
517