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 <algorithm>
40 #include <fstream>
41 #include <iostream>
42 #include <rak/functional.h>
43 #include <rak/string_manip.h>
44 #include <torrent/data/file.h>
45 #include <torrent/utils/resume.h>
46 #include <torrent/exceptions.h>
47 #include <torrent/download.h>
48 #include <torrent/hash_string.h>
49 #include <torrent/object.h>
50 #include <torrent/object_stream.h>
51 #include <torrent/torrent.h>
52 #include <torrent/utils/log.h>
53
54 #include "rpc/parse_commands.h"
55
56 #include "control.h"
57 #include "globals.h"
58 #include "manager.h"
59 #include "view.h"
60 #include "view_manager.h"
61
62 #include "dht_manager.h"
63 #include "download.h"
64 #include "download_list.h"
65 #include "download_store.h"
66 #include "ui/root.h"
67
68 #define DL_TRIGGER_EVENT(download, event_name) \
69 rpc::commands.call_catch(event_name, rpc::make_target(download), torrent::Object(), "Event '" event_name "' failed: ");
70
71 namespace core {
72
73 inline void
check_contains(Download * d)74 DownloadList::check_contains(Download* d) {
75 #ifdef USE_EXTRA_DEBUG
76 if (std::find(begin(), end(), d) == end())
77 throw torrent::internal_error("DownloadList::check_contains(...) failed.");
78 #endif
79 }
80
81 void
clear()82 DownloadList::clear() {
83 std::for_each(begin(), end(), std::bind1st(std::mem_fun(&DownloadList::close), this));
84 std::for_each(begin(), end(), rak::call_delete<Download>());
85
86 base_type::clear();
87 }
88
89 void
session_save()90 DownloadList::session_save() {
91 unsigned int c = std::count_if(begin(), end(), std::bind1st(std::mem_fun(&DownloadStore::save_resume), control->core()->download_store()));
92
93 if (c != size())
94 lt_log_print(torrent::LOG_ERROR, "Failed to save session torrents.");
95
96 control->dht_manager()->save_dht_cache();
97 control->ui()->save_input_history();
98 }
99
100 DownloadList::iterator
find(const torrent::HashString & hash)101 DownloadList::find(const torrent::HashString& hash) {
102 return std::find_if(begin(), end(), rak::equal(hash, rak::on(std::mem_fun(&Download::info), std::mem_fun(&torrent::DownloadInfo::hash))));
103 }
104
105 DownloadList::iterator
find_hex(const char * hash)106 DownloadList::find_hex(const char* hash) {
107 torrent::HashString key;
108
109 for (torrent::HashString::iterator itr = key.begin(), last = key.end(); itr != last; itr++, hash += 2)
110 *itr = (rak::hexchar_to_value(*hash) << 4) + rak::hexchar_to_value(*(hash + 1));
111
112 return std::find_if(begin(), end(), rak::equal(key, rak::on(std::mem_fun(&Download::info), std::mem_fun(&torrent::DownloadInfo::hash))));
113 }
114
115 Download*
find_hex_ptr(const char * hash)116 DownloadList::find_hex_ptr(const char* hash) {
117 iterator itr = find_hex(hash);
118
119 return itr != end() ? *itr : NULL;
120 }
121
122 Download*
create(torrent::Object * obj,bool printLog)123 DownloadList::create(torrent::Object* obj, bool printLog) {
124 torrent::Download download;
125
126 try {
127 download = torrent::download_add(obj);
128
129 } catch (torrent::local_error& e) {
130 delete obj;
131
132 if (printLog)
133 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download: %s", e.what());
134
135 return NULL;
136 }
137
138 // There's no non-critical exceptions that should be throwable by
139 // the ctor, so don't catch.
140 return new Download(download);
141 }
142
143 Download*
create(std::istream * str,bool printLog)144 DownloadList::create(std::istream* str, bool printLog) {
145 torrent::Object* object = new torrent::Object;
146 torrent::Download download;
147
148 try {
149 *str >> *object;
150
151 // Don't throw input_error from here as gcc-3.3.5 produces bad
152 // code.
153 if (str->fail()) {
154 delete object;
155
156 if (printLog)
157 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
158
159 return NULL;
160 }
161
162 download = torrent::download_add(object);
163
164 } catch (torrent::local_error& e) {
165 delete object;
166
167 if (printLog)
168 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download: %s", e.what());
169
170 return NULL;
171 }
172
173 // There's no non-critical exceptions that should be throwable by
174 // the ctor, so don't catch.
175 return new Download(download);
176 }
177
178 DownloadList::iterator
insert(Download * download)179 DownloadList::insert(Download* download) {
180 iterator itr = base_type::insert(end(), download);
181
182 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Inserting download.");
183
184 try {
185 (*itr)->data()->slot_initial_hash() = std::bind(&DownloadList::hash_done, this, download);
186 (*itr)->data()->slot_download_done() = std::bind(&DownloadList::received_finished, this, download);
187
188 // This needs to be separated into two different calls to ensure
189 // the download remains in the view.
190 std::for_each(control->view_manager()->begin(), control->view_manager()->end(), std::bind2nd(std::mem_fun(&View::insert), download));
191 std::for_each(control->view_manager()->begin(), control->view_manager()->end(), std::bind2nd(std::mem_fun(&View::filter_download), download));
192
193 DL_TRIGGER_EVENT(*itr, "event.download.inserted");
194
195 } catch (torrent::local_error& e) {
196 // Should perhaps relax this, just print an error and remove the
197 // downloads?
198 throw torrent::internal_error("Caught during DownloadList::insert(...): " + std::string(e.what()));
199 }
200
201 return itr;
202 }
203
204 void
erase_ptr(Download * download)205 DownloadList::erase_ptr(Download* download) {
206 erase(std::find(begin(), end(), download));
207 }
208
209 DownloadList::iterator
erase(iterator itr)210 DownloadList::erase(iterator itr) {
211 if (itr == end())
212 throw torrent::internal_error("DownloadList::erase(...) could not find download.");
213
214 lt_log_print_info(torrent::LOG_TORRENT_INFO, (*itr)->info(), "download_list", "Erasing download.");
215
216 // Makes sure close doesn't restart hashing of this download.
217 (*itr)->set_hash_failed(true);
218
219 close(*itr);
220
221 control->core()->download_store()->remove(*itr);
222
223 DL_TRIGGER_EVENT(*itr, "event.download.erased");
224 std::for_each(control->view_manager()->begin(), control->view_manager()->end(), std::bind2nd(std::mem_fun(&View::erase), *itr));
225
226 torrent::download_remove(*(*itr)->download());
227 delete *itr;
228
229 return base_type::erase(itr);
230 }
231
232 bool
open(Download * download)233 DownloadList::open(Download* download) {
234 try {
235
236 open_throw(download);
237
238 return true;
239
240 } catch (torrent::local_error& e) {
241 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not open download: %s", e.what());
242 return false;
243 }
244 }
245
246 void
open_throw(Download * download)247 DownloadList::open_throw(Download* download) {
248 check_contains(download);
249
250 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Opening download.");
251
252 if (download->download()->info()->is_open())
253 return;
254
255 int openFlags = download->resume_flags();
256
257 if (rpc::call_command_value("system.file.allocate"))
258 openFlags |= torrent::Download::open_enable_fallocate;
259
260 download->download()->open(openFlags);
261 DL_TRIGGER_EVENT(download, "event.download.opened");
262 }
263
264 void
close(Download * download)265 DownloadList::close(Download* download) {
266 try {
267 close_throw(download);
268
269 } catch (torrent::local_error& e) {
270 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not close download: %s", e.what());
271 }
272 }
273
274 void
close_directly(Download * download)275 DownloadList::close_directly(Download* download) {
276 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
277
278 if (download->download()->info()->is_active()) {
279 download->download()->stop(torrent::Download::stop_skip_tracker);
280
281 if (torrent::resume_check_target_files(*download->download(), download->download()->bencode()->get_key("libtorrent_resume")))
282 torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
283 }
284
285 if (download->download()->info()->is_open())
286 download->download()->close();
287 }
288
289 void
close_quick(Download * download)290 DownloadList::close_quick(Download* download) {
291 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download quickly.");
292 close(download);
293
294 // Make sure we cancel any tracker requests. This should rather be
295 // handled by some parameter to the close function, or some other
296 // way of giving the client more control of when STOPPED requests
297 // are sent.
298 download->download()->manual_cancel();
299 }
300
301 void
close_throw(Download * download)302 DownloadList::close_throw(Download* download) {
303 check_contains(download);
304
305 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download with throw.");
306
307 // When pause gets called it will clear the initial hash check state
308 // and set hash failed. This should ensure hashing doesn't restart
309 // until resume gets called.
310 pause(download);
311
312 // Check for is_open after pause due to hashing.
313 if (!download->is_open())
314 return;
315
316 // Save the torrent on close, this covers shutdown and if a torrent
317 // is manually closed which would clear the progress data. For
318 // better crash protection, save regulary in addition to this.
319 //
320 // Used to be in pause, but this was wrong for rehashing etc.
321 //
322 // Reconsider this save. Should be done explicitly when shutting down.
323 //control->core()->download_store()->save(download);
324
325 download->download()->close();
326
327 if (!download->is_hash_failed() && rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped)
328 throw torrent::internal_error("DownloadList::close_throw(...) called but we're going into a hashing loop.");
329
330 DL_TRIGGER_EVENT(download, "event.download.hash_removed");
331 DL_TRIGGER_EVENT(download, "event.download.closed");
332 }
333
334 void
resume(Download * download,int flags)335 DownloadList::resume(Download* download, int flags) {
336 check_contains(download);
337
338 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Resuming download: flags:%0x.", flags);
339
340 try {
341
342 if (download->download()->info()->is_active())
343 return;
344
345 rpc::parse_command_single(rpc::make_target(download), "view.set_visible=active");
346
347 // We need to make sure the flags aren't reset if someone decideds
348 // to call resume() while it is hashing, etc.
349 if (download->resume_flags() == ~uint32_t())
350 download->set_resume_flags(flags);
351
352 // Manual or end-of-download rehashing clears the resume data so
353 // we can just start the hashing again without clearing it again.
354 //
355 // It is also assumed the is_hash_checked flag gets cleared when
356 // 'hashing' was set.
357 if (!download->is_hash_checked()) {
358 // If the hash failed flag wasn't cleared then hashing won't be
359 // initiated.
360 if (download->is_hash_failed())
361 return;
362
363 if (rpc::call_command_value("d.hashing", rpc::make_target(download)) == Download::variable_hashing_stopped)
364 rpc::call_command("d.hashing.set", Download::variable_hashing_initial, rpc::make_target(download));
365
366 DL_TRIGGER_EVENT(download, "event.download.hash_queued");
367 return;
368 }
369
370 // This will never actually do anything due to the above hash check.
371 // open_throw(download);
372
373 rpc::call_command("d.state_changed.set", cachedTime.seconds(), rpc::make_target(download));
374 rpc::call_command("d.state_counter.set", rpc::call_command_value("d.state_counter", rpc::make_target(download)) + 1, rpc::make_target(download));
375
376 if (download->is_done()) {
377 torrent::Object conn_current = rpc::call_command("d.connection_seed", torrent::Object(), rpc::make_target(download));
378 torrent::Object choke_up = rpc::call_command("d.up.choke_heuristics.seed", torrent::Object(), rpc::make_target(download));
379 torrent::Object choke_down = rpc::call_command("d.down.choke_heuristics.seed", torrent::Object(), rpc::make_target(download));
380
381 if (conn_current.is_string_empty()) conn_current = rpc::call_command("protocol.connection.seed", torrent::Object(), rpc::make_target(download));
382 if (choke_up.is_string_empty()) choke_up = rpc::call_command("protocol.choke_heuristics.up.seed", torrent::Object(), rpc::make_target(download));
383 if (choke_down.is_string_empty()) choke_down = rpc::call_command("protocol.choke_heuristics.down.seed", torrent::Object(), rpc::make_target(download));
384
385 rpc::call_command("d.connection_current.set", conn_current, rpc::make_target(download));
386 rpc::call_command("d.up.choke_heuristics.set", choke_up, rpc::make_target(download));
387 rpc::call_command("d.down.choke_heuristics.set", choke_down, rpc::make_target(download));
388
389 } else {
390 torrent::Object conn_current = rpc::call_command("d.connection_leech", torrent::Object(), rpc::make_target(download));
391 torrent::Object choke_up = rpc::call_command("d.up.choke_heuristics.leech", torrent::Object(), rpc::make_target(download));
392 torrent::Object choke_down = rpc::call_command("d.down.choke_heuristics.leech", torrent::Object(), rpc::make_target(download));
393
394 if (conn_current.is_string_empty()) conn_current = rpc::call_command("protocol.connection.leech", torrent::Object(), rpc::make_target(download));
395 if (choke_up.is_string_empty()) choke_up = rpc::call_command("protocol.choke_heuristics.up.leech", torrent::Object(), rpc::make_target(download));
396 if (choke_down.is_string_empty()) choke_down = rpc::call_command("protocol.choke_heuristics.down.leech", torrent::Object(), rpc::make_target(download));
397
398 rpc::call_command("d.connection_current.set", conn_current, rpc::make_target(download));
399 rpc::call_command("d.up.choke_heuristics.set", choke_up, rpc::make_target(download));
400 rpc::call_command("d.down.choke_heuristics.set", choke_down, rpc::make_target(download));
401
402 // For the moment, clear the resume data so we force hash-check
403 // on non-complete downloads after a crash. This shouldn't be
404 // needed, but for some reason linux 2.6 is very lazy about
405 // updating mtime.
406 //
407 // Disabling this due to the new resume code.
408 // torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"), true);
409 }
410
411 // If the DHT server is set to auto, start it now.
412 if (!download->download()->info()->is_private())
413 control->dht_manager()->auto_start();
414
415 // Update the priority to ensure it has the correct
416 // seeding/unfinished modifiers.
417 download->set_priority(download->priority());
418 download->download()->start(download->resume_flags());
419
420 download->set_resume_flags(~uint32_t());
421
422 DL_TRIGGER_EVENT(download, "event.download.resumed");
423
424 } catch (torrent::local_error& e) {
425 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not resume download: %s", e.what());
426 }
427 }
428
429 void
pause(Download * download,int flags)430 DownloadList::pause(Download* download, int flags) {
431 check_contains(download);
432
433 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Pausing download: flags:%0x.", flags);
434
435 try {
436
437 download->set_resume_flags(~uint32_t());
438
439 rpc::parse_command_single(rpc::make_target(download), "view.set_not_visible=active");
440
441 // Always clear hashing on pause. When a hashing request is added,
442 // it should have cleared the hash resume data.
443 if (rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped) {
444 download->download()->hash_stop();
445 rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_stopped, rpc::make_target(download));
446
447 DL_TRIGGER_EVENT(download, "event.download.hash_removed");
448 }
449
450 if (!download->download()->info()->is_active())
451 return;
452
453 download->download()->stop(flags);
454 torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
455
456 // TODO: This is actually for pause, not stop... And doesn't get
457 // called when the download isn't active, but was in the 'started'
458 // view.
459 DL_TRIGGER_EVENT(download, "event.download.paused");
460
461 rpc::call_command("d.state_changed.set", cachedTime.seconds(), rpc::make_target(download));
462 rpc::call_command("d.state_counter.set", rpc::call_command_value("d.state_counter", rpc::make_target(download)), rpc::make_target(download));
463
464 // If initial seeding is complete, don't try it again when restarting.
465 if (download->is_done() &&
466 rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)).as_string() == "initial_seed")
467 rpc::call_command("d.connection_seed.set", rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)), rpc::make_target(download));
468
469 // Save the state after all the slots, etc have been called so we
470 // include the modifications they may make.
471 //control->core()->download_store()->save(download);
472
473 } catch (torrent::local_error& e) {
474 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not pause download: %s", e.what());
475 }
476 }
477
478 void
check_hash(Download * download)479 DownloadList::check_hash(Download* download) {
480 check_contains(download);
481
482 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Checking hash.");
483
484 try {
485 if (rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped)
486 return;
487
488 hash_queue(download, Download::variable_hashing_rehash);
489
490 } catch (torrent::local_error& e) {
491 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not check hash: %s", e.what());
492 }
493 }
494
495 void
hash_done(Download * download)496 DownloadList::hash_done(Download* download) {
497 check_contains(download);
498
499 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Hash done.");
500
501 if (download->is_hash_checking() || download->is_active())
502 throw torrent::internal_error("DownloadList::hash_done(...) download in invalid state.");
503
504 if (!download->is_hash_checked()) {
505 download->set_hash_failed(true);
506
507 DL_TRIGGER_EVENT(download, "event.download.hash_failed");
508 return;
509 }
510
511 // Need to find some sane conditional here. Can we check the total
512 // downloaded to ensure something was transferred, thus we didn't
513 // just hash an already completed torrent with lacking session data?
514 //
515 // Perhaps we should use a seperate variable or state, and check
516 // that. Thus we can bork the download if the hash check doesn't
517 // confirm all the data, avoiding large BW usage on f.ex. the
518 // ReiserFS bug with >4GB files.
519
520 int64_t hashing = rpc::call_command_value("d.hashing", rpc::make_target(download));
521 rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_stopped, rpc::make_target(download));
522
523 if (download->is_done() && download->download()->info()->is_meta_download())
524 return process_meta_download(download);
525
526 switch (hashing) {
527 case Download::variable_hashing_initial:
528 case Download::variable_hashing_rehash:
529 // Normal re/hashing.
530
531 // If the download was previously completed but the files were
532 // f.ex deleted, then we clear the state and complete.
533 if (rpc::call_command_value("d.complete", rpc::make_target(download)) && !download->is_done()) {
534 rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(download));
535 download->set_message("Download registered as completed, but hash check returned unfinished chunks.");
536 }
537
538 // Save resume data so we update time-stamps and priorities if
539 // they were invalid/changed while loading/hashing.
540 rpc::call_command("d.complete.set", (int64_t)download->is_done(), rpc::make_target(download));
541 torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
542
543 if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
544 resume(download, download->resume_flags());
545
546 break;
547
548 case Download::variable_hashing_last:
549
550 if (download->is_done()) {
551 confirm_finished(download);
552 } else {
553 download->set_message("Hash check on download completion found bad chunks, consider using \"safe_sync\".");
554 lt_log_print(torrent::LOG_TORRENT_ERROR, "Hash check on download completion found bad chunks, consider using \"safe_sync\".");
555 DL_TRIGGER_EVENT(download, "event.download.hash_final_failed");
556 }
557
558 // TODO: Should we skip the 'hash_done' event here?
559 return;
560
561 case Download::variable_hashing_stopped:
562 default:
563 // Either an error or someone wrote to the hashing variable...
564 download->set_message("Hash check completed but the \"hashing\" variable is in an invalid state.");
565 return;
566 }
567
568 DL_TRIGGER_EVENT(download, "event.download.hash_done");
569 }
570
571 void
hash_queue(Download * download,int type)572 DownloadList::hash_queue(Download* download, int type) {
573 check_contains(download);
574
575 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Hash queue.");
576
577 if (rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped)
578 throw torrent::internal_error("DownloadList::hash_queue(...) hashing already queued.");
579
580 // HACK
581 if (download->is_open()) {
582 pause(download, torrent::Download::stop_skip_tracker);
583 download->download()->close();
584
585 DL_TRIGGER_EVENT(download, "event.download.hash_removed");
586 DL_TRIGGER_EVENT(download, "event.download.closed");
587 }
588
589 torrent::resume_clear_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
590
591 download->set_hash_failed(false);
592 rpc::call_command_set_value("d.hashing.set", type, rpc::make_target(download));
593
594 if (download->is_open())
595 throw torrent::internal_error("DownloadList::hash_clear(...) download still open.");
596
597 // If any more stuff is added here, make sure resume etc are still
598 // correct.
599 DL_TRIGGER_EVENT(download, "event.download.hash_queued");
600 }
601
602 void
received_finished(Download * download)603 DownloadList::received_finished(Download* download) {
604 check_contains(download);
605
606 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Received finished.");
607
608 if (rpc::call_command_value("pieces.hash.on_completion"))
609 // Set some 'checking_finished_thingie' variable to make hash_done
610 // trigger correctly, also so it can bork on missing data.
611 hash_queue(download, Download::variable_hashing_last);
612 else
613 confirm_finished(download);
614 }
615
616 // The download must be open when we call this function.
617 void
confirm_finished(Download * download)618 DownloadList::confirm_finished(Download* download) {
619 check_contains(download);
620
621 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Confirming finished.");
622
623 if (download->download()->info()->is_meta_download())
624 return process_meta_download(download);
625
626 rpc::call_command("d.complete.set", (int64_t)1, rpc::make_target(download));
627
628 // Clean up these settings:
629 torrent::Object conn_current = rpc::call_command("d.connection_seed", torrent::Object(), rpc::make_target(download));
630 torrent::Object choke_up = rpc::call_command("d.up.choke_heuristics.seed", torrent::Object(), rpc::make_target(download));
631 torrent::Object choke_down = rpc::call_command("d.down.choke_heuristics.seed", torrent::Object(), rpc::make_target(download));
632
633 if (conn_current.is_string_empty()) conn_current = rpc::call_command("protocol.connection.seed", torrent::Object(), rpc::make_target(download));
634 if (choke_up.is_string_empty()) choke_up = rpc::call_command("protocol.choke_heuristics.up.seed", torrent::Object(), rpc::make_target(download));
635 if (choke_down.is_string_empty()) choke_down = rpc::call_command("protocol.choke_heuristics.down.seed", torrent::Object(), rpc::make_target(download));
636
637 rpc::call_command("d.connection_current.set", conn_current, rpc::make_target(download));
638 rpc::call_command("d.up.choke_heuristics.set", choke_up, rpc::make_target(download));
639 rpc::call_command("d.down.choke_heuristics.set", choke_down, rpc::make_target(download));
640
641 download->set_priority(download->priority());
642
643 if (rpc::call_command_value("d.peers_min", rpc::make_target(download)) == rpc::call_command_value("throttle.min_peers.normal") &&
644 rpc::call_command_value("throttle.min_peers.seed") >= 0)
645 rpc::call_command("d.peers_min.set", rpc::call_command("throttle.min_peers.seed"), rpc::make_target(download));
646
647 if (rpc::call_command_value("d.peers_max", rpc::make_target(download)) == rpc::call_command_value("throttle.max_peers.normal") &&
648 rpc::call_command_value("throttle.max_peers.seed") >= 0)
649 rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download));
650
651 // Do this before the slots are called in case one of them closes
652 // the download.
653 //
654 // Obsolete.
655 if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0) {
656 // torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
657 control->core()->download_store()->save_resume(download);
658 }
659
660 // Send the completed request before resuming so we don't reset the
661 // up/downloaded baseline.
662 download->download()->send_completed();
663
664 // Save the hash in case the finished event erases it.
665 torrent::HashString infohash = download->info()->hash();
666
667 DL_TRIGGER_EVENT(download, "event.download.finished");
668
669 if (find(infohash) == end())
670 return;
671
672 // if (download->resume_flags() != ~uint32_t())
673 // throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != ~uint32_t().");
674
675 // See #1292.
676 //
677 // Just reset the value for the moment. If a torrent finishes while
678 // others are hashing, or some other situtation that causes resume
679 // flag to change could cause the state to be invalid.
680 //
681 // TODO: Add a check when setting the flags to see if the torrent is
682 // being hashed.
683 download->set_resume_flags(~uint32_t());
684
685 if (!download->is_active() && rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
686 resume(download,
687 torrent::Download::start_no_create |
688 torrent::Download::start_skip_tracker |
689 torrent::Download::start_keep_baseline);
690 }
691
692 void
process_meta_download(Download * download)693 DownloadList::process_meta_download(Download* download) {
694 lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Processing meta download.");
695
696 rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download));
697 rpc::call_command("d.close", torrent::Object(), rpc::make_target(download));
698
699 std::string metafile = (*download->file_list()->begin())->frozen_path();
700 std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary);
701 if (!file.is_open()) {
702 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata.");
703 return;
704 }
705
706 torrent::Object* bencode = new torrent::Object(torrent::Object::create_map());
707 file >> bencode->insert_key("info", torrent::Object());
708 if (file.fail()) {
709 delete bencode;
710 lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
711 return;
712 }
713 file.close();
714
715 // Steal the keys we still need. The old download has no use for them.
716 bencode->insert_key("rtorrent_meta_download", torrent::Object()).swap(download->bencode()->get_key("rtorrent_meta_download"));
717 if (download->bencode()->has_key("announce"))
718 bencode->insert_key("announce", torrent::Object()).swap(download->bencode()->get_key("announce"));
719 if (download->bencode()->has_key("announce-list"))
720 bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list"));
721
722 erase_ptr(download);
723 control->core()->try_create_download_from_meta_download(bencode, metafile);
724 }
725
726 }
727