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