00001
00002
00003
00004
00005
00006
00007
00008 #include <ctime>
00009 #include <iostream>
00010 #include <fstream>
00011 #include <iomanip>
00012 #include <iterator>
00013 #include <algorithm>
00014 #include <set>
00015 #include <cctype>
00016 #include <algorithm>
00017
00018 #ifdef _MSC_VER
00019 #pragma warning(push, 1)
00020 #endif
00021
00022 #include <boost/lexical_cast.hpp>
00023 #include <boost/filesystem/convenience.hpp>
00024 #include <boost/optional.hpp>
00025 #include <boost/bind.hpp>
00026
00027 #ifdef _MSC_VER
00028 #pragma warning(pop)
00029 #endif
00030
00031 #include "libtorrent/peer_id.hpp"
00032 #include "libtorrent/bt_peer_connection.hpp"
00033 #include "libtorrent/torrent_info.hpp"
00034 #include "libtorrent/tracker_manager.hpp"
00035 #include "libtorrent/bencode.hpp"
00036 #include "libtorrent/hasher.hpp"
00037 #include "libtorrent/entry.hpp"
00038 #include "libtorrent/session.hpp"
00039 #include "libtorrent/aux_/session_impl.hpp"
00040 #include "libtorrent/invariant_check.hpp"
00041
00042 #if defined(_MSC_VER) && _MSC_VER < 1300
00043 namespace std
00044 {
00045 using ::srand;
00046 using ::isalnum;
00047 };
00048 #endif
00049
00050 using boost::bind;
00051 using boost::mutex;
00052 using libtorrent::aux::session_impl;
00053
00054 namespace libtorrent
00055 {
00056 namespace
00057 {
00058 void throw_invalid_handle()
00059 {
00060 throw invalid_handle();
00061 }
00062
00063 template<class Ret, class F>
00064 Ret call_member(
00065 session_impl* ses
00066 , aux::checker_impl* chk
00067 , sha1_hash const& hash
00068 , F f)
00069 {
00070 if (ses == 0) throw_invalid_handle();
00071
00072 if (chk)
00073 {
00074 mutex::scoped_lock l(chk->m_mutex);
00075 aux::piece_checker_data* d = chk->find_torrent(hash);
00076 if (d != 0) return f(*d->torrent_ptr);
00077 }
00078
00079 {
00080 session_impl::mutex_t::scoped_lock l(ses->m_mutex);
00081 boost::shared_ptr<torrent> t = ses->find_torrent(hash).lock();
00082 if (t) return f(*t);
00083 }
00084
00085
00086
00087
00088 throw invalid_handle();
00089 }
00090 }
00091
00092 #ifndef NDEBUG
00093
00094 void torrent_handle::check_invariant() const
00095 {
00096 assert((m_ses == 0 && m_chk == 0) || (m_ses != 0));
00097 }
00098
00099 #endif
00100
00101 void torrent_handle::set_max_uploads(int max_uploads) const
00102 {
00103 INVARIANT_CHECK;
00104
00105 assert(max_uploads >= 2 || max_uploads == -1);
00106
00107 call_member<void>(m_ses, m_chk, m_info_hash
00108 , bind(&torrent::set_max_uploads, _1, max_uploads));
00109 }
00110
00111 void torrent_handle::use_interface(const char* net_interface) const
00112 {
00113 INVARIANT_CHECK;
00114
00115 call_member<void>(m_ses, m_chk, m_info_hash
00116 , bind(&torrent::use_interface, _1, net_interface));
00117 }
00118
00119 void torrent_handle::set_max_connections(int max_connections) const
00120 {
00121 INVARIANT_CHECK;
00122
00123 assert(max_connections >= 2 || max_connections == -1);
00124
00125 call_member<void>(m_ses, m_chk, m_info_hash
00126 , bind(&torrent::set_max_connections, _1, max_connections));
00127 }
00128
00129 void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const
00130 {
00131 INVARIANT_CHECK;
00132 assert(limit >= -1);
00133
00134 call_member<void>(m_ses, m_chk, m_info_hash
00135 , bind(&torrent::set_peer_upload_limit, _1, ip, limit));
00136 }
00137
00138 void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const
00139 {
00140 INVARIANT_CHECK;
00141 assert(limit >= -1);
00142
00143 call_member<void>(m_ses, m_chk, m_info_hash
00144 , bind(&torrent::set_peer_download_limit, _1, ip, limit));
00145 }
00146
00147 void torrent_handle::set_upload_limit(int limit) const
00148 {
00149 INVARIANT_CHECK;
00150
00151 assert(limit >= -1);
00152
00153 call_member<void>(m_ses, m_chk, m_info_hash
00154 , bind(&torrent::set_upload_limit, _1, limit));
00155 }
00156
00157 void torrent_handle::set_download_limit(int limit) const
00158 {
00159 INVARIANT_CHECK;
00160
00161 assert(limit >= -1);
00162
00163 call_member<void>(m_ses, m_chk, m_info_hash
00164 , bind(&torrent::set_download_limit, _1, limit));
00165 }
00166
00167 bool torrent_handle::move_storage(
00168 boost::filesystem::path const& save_path) const
00169 {
00170 INVARIANT_CHECK;
00171
00172 return call_member<bool>(m_ses, m_chk, m_info_hash
00173 , bind(&torrent::move_storage, _1, save_path));
00174 }
00175
00176 bool torrent_handle::has_metadata() const
00177 {
00178 INVARIANT_CHECK;
00179
00180 return call_member<bool>(m_ses, m_chk, m_info_hash
00181 , bind(&torrent::valid_metadata, _1));
00182 }
00183
00184 bool torrent_handle::is_seed() const
00185 {
00186 INVARIANT_CHECK;
00187
00188 return call_member<bool>(m_ses, m_chk, m_info_hash
00189 , bind(&torrent::is_seed, _1));
00190 }
00191
00192 bool torrent_handle::is_paused() const
00193 {
00194 INVARIANT_CHECK;
00195
00196 return call_member<bool>(m_ses, m_chk, m_info_hash
00197 , bind(&torrent::is_paused, _1));
00198 }
00199
00200 void torrent_handle::pause() const
00201 {
00202 INVARIANT_CHECK;
00203
00204 call_member<void>(m_ses, m_chk, m_info_hash
00205 , bind(&torrent::pause, _1));
00206 }
00207
00208 void torrent_handle::resume() const
00209 {
00210 INVARIANT_CHECK;
00211
00212 call_member<void>(m_ses, m_chk, m_info_hash
00213 , bind(&torrent::resume, _1));
00214 }
00215
00216 void torrent_handle::set_tracker_login(std::string const& name
00217 , std::string const& password) const
00218 {
00219 INVARIANT_CHECK;
00220
00221 call_member<void>(m_ses, m_chk, m_info_hash
00222 , bind(&torrent::set_tracker_login, _1, name, password));
00223 }
00224
00225 void torrent_handle::file_progress(std::vector<float>& progress)
00226 {
00227 INVARIANT_CHECK;
00228
00229 if (m_ses == 0) throw_invalid_handle();
00230
00231 if (m_chk)
00232 {
00233 mutex::scoped_lock l(m_chk->m_mutex);
00234
00235 aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
00236 if (d != 0)
00237 {
00238 if (!d->processing)
00239 {
00240 torrent_info const& info = d->torrent_ptr->torrent_file();
00241 progress.clear();
00242 progress.resize(info.num_files(), 0.f);
00243 return;
00244 }
00245 d->torrent_ptr->file_progress(progress);
00246 return;
00247 }
00248 }
00249
00250 {
00251 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00252 boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
00253 if (t) return t->file_progress(progress);
00254 }
00255
00256 throw_invalid_handle();
00257 }
00258
00259 torrent_status torrent_handle::status() const
00260 {
00261 INVARIANT_CHECK;
00262
00263 if (m_ses == 0) throw_invalid_handle();
00264
00265 if (m_chk)
00266 {
00267 mutex::scoped_lock l(m_chk->m_mutex);
00268
00269 aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
00270 if (d != 0)
00271 {
00272 torrent_status st;
00273
00274 if (d->processing)
00275 {
00276 if (d->torrent_ptr->is_allocating())
00277 st.state = torrent_status::allocating;
00278 else
00279 st.state = torrent_status::checking_files;
00280 }
00281 else
00282 st.state = torrent_status::queued_for_checking;
00283 st.progress = d->progress;
00284 st.paused = d->torrent_ptr->is_paused();
00285 return st;
00286 }
00287 }
00288
00289 {
00290 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00291 boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
00292 if (t) return t->status();
00293 }
00294
00295 throw_invalid_handle();
00296 return torrent_status();
00297 }
00298
00299 void torrent_handle::set_sequenced_download_threshold(int threshold) const
00300 {
00301 INVARIANT_CHECK;
00302 call_member<void>(m_ses, m_chk, m_info_hash
00303 , bind(&torrent::set_sequenced_download_threshold, _1, threshold));
00304 }
00305
00306 void torrent_handle::filter_piece(int index, bool filter) const
00307 {
00308 INVARIANT_CHECK;
00309 call_member<void>(m_ses, m_chk, m_info_hash
00310 , bind(&torrent::filter_piece, _1, index, filter));
00311 }
00312
00313 void torrent_handle::filter_pieces(std::vector<bool> const& pieces) const
00314 {
00315 INVARIANT_CHECK;
00316 call_member<void>(m_ses, m_chk, m_info_hash
00317 , bind(&torrent::filter_pieces, _1, pieces));
00318 }
00319
00320 bool torrent_handle::is_piece_filtered(int index) const
00321 {
00322 INVARIANT_CHECK;
00323 return call_member<bool>(m_ses, m_chk, m_info_hash
00324 , bind(&torrent::is_piece_filtered, _1, index));
00325 }
00326
00327 std::string torrent_handle::name() const
00328 {
00329 INVARIANT_CHECK;
00330 return call_member<std::string>(m_ses, m_chk, m_info_hash
00331 , bind(&torrent::name, _1));
00332 }
00333
00334 std::vector<bool> torrent_handle::filtered_pieces() const
00335 {
00336 INVARIANT_CHECK;
00337 std::vector<bool> ret;
00338 call_member<void>(m_ses, m_chk, m_info_hash
00339 , bind(&torrent::filtered_pieces, _1, boost::ref(ret)));
00340 return ret;
00341 }
00342
00343 void torrent_handle::filter_files(std::vector<bool> const& files) const
00344 {
00345 INVARIANT_CHECK;
00346 call_member<void>(m_ses, m_chk, m_info_hash
00347 , bind(&torrent::filter_files, _1, files));
00348 }
00349
00350 std::vector<announce_entry> const& torrent_handle::trackers() const
00351 {
00352 INVARIANT_CHECK;
00353
00354 return call_member<std::vector<announce_entry> const&>(m_ses
00355 , m_chk, m_info_hash, bind(&torrent::trackers, _1));
00356 }
00357
00358 void torrent_handle::add_url_seed(std::string const& url)
00359 {
00360 INVARIANT_CHECK;
00361
00362 return call_member<void>(m_ses, m_chk, m_info_hash
00363 , bind(&torrent::add_url_seed, _1, url));
00364 }
00365
00366 void torrent_handle::replace_trackers(
00367 std::vector<announce_entry> const& urls) const
00368 {
00369 INVARIANT_CHECK;
00370
00371 call_member<void>(m_ses, m_chk, m_info_hash
00372 , bind(&torrent::replace_trackers, _1, urls));
00373 }
00374
00375 torrent_info const& torrent_handle::get_torrent_info() const
00376 {
00377 INVARIANT_CHECK;
00378
00379 if (!has_metadata()) throw_invalid_handle();
00380 return call_member<torrent_info const&>(m_ses, m_chk, m_info_hash
00381 , bind(&torrent::torrent_file, _1));
00382 }
00383
00384 bool torrent_handle::is_valid() const
00385 {
00386 INVARIANT_CHECK;
00387
00388 if (m_ses == 0) return false;
00389
00390 if (m_chk)
00391 {
00392 mutex::scoped_lock l(m_chk->m_mutex);
00393 aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
00394 if (d != 0) return true;
00395 }
00396
00397 {
00398 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00399 boost::weak_ptr<torrent> t = m_ses->find_torrent(m_info_hash);
00400 if (!t.expired()) return true;
00401 }
00402
00403 return false;
00404 }
00405
00406 entry torrent_handle::write_resume_data() const
00407 {
00408 INVARIANT_CHECK;
00409
00410 std::vector<int> piece_index;
00411 if (m_ses == 0) return entry();
00412
00413 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00414 boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
00415 if (!t) return entry();
00416
00417 if (!t->valid_metadata()) return entry();
00418
00419 t->filesystem().export_piece_map(piece_index);
00420
00421 entry ret(entry::dictionary_t);
00422
00423 ret["file-format"] = "libtorrent resume file";
00424 ret["file-version"] = 1;
00425
00426 const sha1_hash& info_hash = t->torrent_file().info_hash();
00427 ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
00428
00429 ret["slots"] = entry(entry::list_t);
00430 entry::list_type& slots = ret["slots"].list();
00431 std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
00432
00433
00434 int num_blocks_per_piece =
00435 static_cast<int>(t->torrent_file().piece_length()) / t->block_size();
00436 ret["blocks per piece"] = num_blocks_per_piece;
00437
00438
00439
00440 if (!t->is_seed())
00441 {
00442 const piece_picker& p = t->picker();
00443
00444 const std::vector<piece_picker::downloading_piece>& q
00445 = p.get_download_queue();
00446
00447
00448 ret["unfinished"] = entry::list_type();
00449 entry::list_type& up = ret["unfinished"].list();
00450
00451
00452 for (std::vector<piece_picker::downloading_piece>::const_iterator i
00453 = q.begin(); i != q.end(); ++i)
00454 {
00455 if (i->finished_blocks.count() == 0) continue;
00456
00457 entry piece_struct(entry::dictionary_t);
00458
00459
00460 piece_struct["piece"] = i->index;
00461
00462 std::string bitmask;
00463 const int num_bitmask_bytes
00464 = std::max(num_blocks_per_piece / 8, 1);
00465
00466 for (int j = 0; j < num_bitmask_bytes; ++j)
00467 {
00468 unsigned char v = 0;
00469 for (int k = 0; k < 8; ++k)
00470 v |= i->finished_blocks[j*8+k]?(1 << k):0;
00471 bitmask.insert(bitmask.end(), v);
00472 }
00473 piece_struct["bitmask"] = bitmask;
00474
00475 assert(t->filesystem().slot_for_piece(i->index) >= 0);
00476 unsigned long adler
00477 = t->filesystem().piece_crc(
00478 t->filesystem().slot_for_piece(i->index)
00479 , t->block_size()
00480 , i->finished_blocks);
00481
00482 piece_struct["adler32"] = adler;
00483
00484
00485 up.push_back(piece_struct);
00486 }
00487 }
00488
00489
00490 ret["peers"] = entry::list_type();
00491 entry::list_type& peer_list = ret["peers"].list();
00492
00493 policy& pol = t->get_policy();
00494
00495 for (policy::iterator i = pol.begin_peer()
00496 , end(pol.end_peer()); i != end; ++i)
00497 {
00498
00499
00500
00501
00502
00503
00504
00505 if (i->type == policy::peer::not_connectable
00506 || i->banned) continue;
00507
00508 tcp::endpoint ip = i->ip;
00509 entry peer(entry::dictionary_t);
00510 peer["ip"] = ip.address().to_string();
00511 peer["port"] = ip.port();
00512 peer_list.push_back(peer);
00513 }
00514
00515 std::vector<std::pair<size_type, std::time_t> > file_sizes
00516 = get_filesizes(t->torrent_file(), t->save_path());
00517
00518 ret["file sizes"] = entry::list_type();
00519 entry::list_type& fl = ret["file sizes"].list();
00520 for (std::vector<std::pair<size_type, std::time_t> >::iterator i
00521 = file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
00522 {
00523 entry::list_type p;
00524 p.push_back(entry(i->first));
00525 p.push_back(entry(i->second));
00526 fl.push_back(entry(p));
00527 }
00528
00529 return ret;
00530 }
00531
00532 boost::filesystem::path torrent_handle::save_path() const
00533 {
00534 INVARIANT_CHECK;
00535
00536 return call_member<boost::filesystem::path>(m_ses, m_chk, m_info_hash
00537 , bind(&torrent::save_path, _1));
00538 }
00539
00540 void torrent_handle::connect_peer(tcp::endpoint const& adr) const
00541 {
00542 INVARIANT_CHECK;
00543
00544 if (m_ses == 0) throw_invalid_handle();
00545
00546 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00547 boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
00548
00549 if (!t)
00550 {
00551
00552
00553
00554 mutex::scoped_lock l2(m_chk->m_mutex);
00555
00556 aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
00557 if (d == 0) throw_invalid_handle();
00558 d->peers.push_back(adr);
00559 return;
00560 }
00561
00562 peer_id id;
00563 std::fill(id.begin(), id.end(), 0);
00564 t->get_policy().peer_from_tracker(adr, id);
00565 }
00566
00567 void torrent_handle::force_reannounce(
00568 boost::posix_time::time_duration duration) const
00569 {
00570 INVARIANT_CHECK;
00571
00572 if (m_ses == 0) throw_invalid_handle();
00573
00574 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00575 boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
00576 if (!t) throw_invalid_handle();
00577
00578 using boost::posix_time::second_clock;
00579 t->force_tracker_request(second_clock::universal_time()
00580 + duration);
00581 }
00582
00583 void torrent_handle::force_reannounce() const
00584 {
00585 INVARIANT_CHECK;
00586
00587 if (m_ses == 0) throw_invalid_handle();
00588
00589 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00590 boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
00591 if (!t) throw_invalid_handle();
00592
00593 t->force_tracker_request();
00594 }
00595
00596 void torrent_handle::set_ratio(float ratio) const
00597 {
00598 INVARIANT_CHECK;
00599
00600 assert(ratio >= 0.f);
00601
00602 if (ratio < 1.f && ratio > 0.f)
00603 ratio = 1.f;
00604
00605 call_member<void>(m_ses, m_chk, m_info_hash
00606 , bind(&torrent::set_ratio, _1, ratio));
00607 }
00608
00609 void torrent_handle::resolve_countries(bool r)
00610 {
00611 INVARIANT_CHECK;
00612 call_member<void>(m_ses, m_chk, m_info_hash
00613 , bind(&torrent::resolve_countries, _1, r));
00614 }
00615
00616 bool torrent_handle::resolve_countries() const
00617 {
00618 INVARIANT_CHECK;
00619 return call_member<bool>(m_ses, m_chk, m_info_hash
00620 , bind(&torrent::resolving_countries, _1));
00621 }
00622
00623 void torrent_handle::get_peer_info(std::vector<peer_info>& v) const
00624 {
00625 INVARIANT_CHECK;
00626
00627 v.clear();
00628 if (m_ses == 0) throw_invalid_handle();
00629
00630 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00631
00632 boost::shared_ptr<const torrent> t = m_ses->find_torrent(m_info_hash).lock();
00633 if (!t) return;
00634
00635 for (torrent::const_peer_iterator i = t->begin();
00636 i != t->end(); ++i)
00637 {
00638 peer_connection* peer = i->second;
00639
00640
00641
00642 if (peer->associated_torrent().expired()) continue;
00643
00644 v.push_back(peer_info());
00645 peer_info& p = v.back();
00646
00647 peer->get_peer_info(p);
00648 if (t->resolving_countries())
00649 t->resolve_peer_country(intrusive_ptr<peer_connection>(peer));
00650 }
00651 }
00652
00653 void torrent_handle::get_download_queue(std::vector<partial_piece_info>& queue) const
00654 {
00655 INVARIANT_CHECK;
00656
00657 if (m_ses == 0) throw_invalid_handle();
00658
00659 session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
00660 boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
00661
00662 queue.clear();
00663 if (!t) return;
00664 if (!t->valid_metadata()) return;
00665
00666 if (t->is_seed()) return;
00667
00668 const piece_picker& p = t->picker();
00669
00670 const std::vector<piece_picker::downloading_piece>& q
00671 = p.get_download_queue();
00672
00673 for (std::vector<piece_picker::downloading_piece>::const_iterator i
00674 = q.begin(); i != q.end(); ++i)
00675 {
00676 partial_piece_info pi;
00677 pi.finished_blocks = i->finished_blocks;
00678 pi.requested_blocks = i->requested_blocks;
00679 for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j)
00680 {
00681 pi.peer[j] = i->info[j].peer;
00682 pi.num_downloads[j] = i->info[j].num_downloads;
00683 }
00684 pi.piece_index = i->index;
00685 pi.blocks_in_piece = p.blocks_in_piece(i->index);
00686 queue.push_back(pi);
00687 }
00688 }
00689
00690 }
00691