2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2003, Arvid Norberg
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
#include <ctime>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <iterator>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
|
|
|
#include <cctype>
|
2003-10-30 00:28:09 +01:00
|
|
|
#include <numeric>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push, 1)
|
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
#include <boost/filesystem/convenience.hpp>
|
2004-08-05 15:56:26 +02:00
|
|
|
#include <boost/bind.hpp>
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
|
2003-10-26 18:35:23 +01:00
|
|
|
#include "libtorrent/torrent_handle.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/session.hpp"
|
|
|
|
#include "libtorrent/torrent_info.hpp"
|
2004-01-31 11:46:15 +01:00
|
|
|
#include "libtorrent/tracker_manager.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/bencode.hpp"
|
|
|
|
#include "libtorrent/hasher.hpp"
|
|
|
|
#include "libtorrent/entry.hpp"
|
|
|
|
#include "libtorrent/peer.hpp"
|
|
|
|
#include "libtorrent/peer_id.hpp"
|
2003-12-22 08:14:35 +01:00
|
|
|
#include "libtorrent/alert.hpp"
|
|
|
|
#include "libtorrent/identify_client.hpp"
|
2004-01-18 20:12:18 +01:00
|
|
|
#include "libtorrent/alert_types.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
|
|
namespace std
|
|
|
|
{
|
|
|
|
using ::isalnum;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using namespace libtorrent;
|
2004-10-14 03:17:04 +02:00
|
|
|
using namespace boost::posix_time;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
// wait 60 seconds before retrying a failed tracker
|
2004-04-02 00:29:51 +02:00
|
|
|
tracker_retry_delay_min = 60
|
|
|
|
// when tracker_failed_max trackers
|
|
|
|
// has failed, wait 10 minutes instead
|
|
|
|
, tracker_retry_delay_max = 10 * 60
|
|
|
|
, tracker_failed_max = 5
|
2003-10-23 01:00:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
int calculate_block_size(const torrent_info& i)
|
|
|
|
{
|
2003-12-21 18:28:27 +01:00
|
|
|
const int default_block_size = 16 * 1024;
|
|
|
|
|
|
|
|
// if pieces are too small, adjust the block size
|
|
|
|
if (i.piece_length() < default_block_size)
|
|
|
|
{
|
2004-03-07 21:50:56 +01:00
|
|
|
return static_cast<int>(i.piece_length());
|
2003-12-21 18:28:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// if pieces are too large, adjust the block size
|
2004-01-15 17:45:34 +01:00
|
|
|
if (i.piece_length() / default_block_size > piece_picker::max_blocks_per_piece)
|
2003-12-21 18:28:27 +01:00
|
|
|
{
|
2004-03-07 21:50:56 +01:00
|
|
|
return static_cast<int>(i.piece_length() / piece_picker::max_blocks_per_piece);
|
2003-12-21 18:28:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, go with the default
|
|
|
|
return default_block_size;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-12 04:05:10 +01:00
|
|
|
struct find_peer_by_ip
|
|
|
|
{
|
2004-01-25 13:37:15 +01:00
|
|
|
find_peer_by_ip(const address& a, const torrent* t)
|
|
|
|
: ip(a)
|
|
|
|
, tor(t)
|
|
|
|
{ assert(t != 0); }
|
2004-01-12 04:05:10 +01:00
|
|
|
|
|
|
|
bool operator()(const detail::session_impl::connection_map::value_type& c) const
|
|
|
|
{
|
2004-01-15 17:45:34 +01:00
|
|
|
if (c.first->sender().ip() != ip.ip()) return false;
|
2004-01-12 04:05:10 +01:00
|
|
|
if (tor != c.second->associated_torrent()) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const address& ip;
|
|
|
|
const torrent* tor;
|
|
|
|
};
|
|
|
|
|
2004-01-13 04:08:59 +01:00
|
|
|
struct peer_by_id
|
|
|
|
{
|
|
|
|
peer_by_id(const peer_id& i): id(i) {}
|
|
|
|
|
|
|
|
bool operator()(const std::pair<address, peer_connection*>& p) const
|
|
|
|
{
|
|
|
|
if (p.second->get_peer_id() != id) return false;
|
|
|
|
// have a special case for all zeros. We can have any number
|
|
|
|
// of peers with that id, since it's used to indicate no id.
|
|
|
|
if (std::count(id.begin(), id.end(), 0) == 20) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const peer_id& id;
|
|
|
|
};
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
2003-12-07 06:53:04 +01:00
|
|
|
torrent::torrent(
|
|
|
|
detail::session_impl& ses
|
2004-06-14 01:30:42 +02:00
|
|
|
, entry const& metadata
|
|
|
|
, boost::filesystem::path const& save_path
|
2005-05-13 02:39:39 +02:00
|
|
|
, address const& net_interface
|
|
|
|
, bool compact_mode)
|
2004-06-14 01:30:42 +02:00
|
|
|
: m_torrent_file(metadata)
|
2003-10-23 01:00:57 +02:00
|
|
|
, m_abort(false)
|
2004-03-21 03:03:37 +01:00
|
|
|
, m_paused(false)
|
2004-07-24 13:54:17 +02:00
|
|
|
, m_just_paused(false)
|
2004-01-21 14:16:11 +01:00
|
|
|
, m_event(tracker_request::started)
|
2004-06-14 01:30:42 +02:00
|
|
|
, m_block_size(0)
|
|
|
|
, m_storage(0)
|
2004-10-14 03:17:04 +02:00
|
|
|
, m_next_request(second_clock::universal_time())
|
2003-10-23 01:00:57 +02:00
|
|
|
, m_duration(1800)
|
2005-02-21 14:59:24 +01:00
|
|
|
, m_complete(-1)
|
|
|
|
, m_incomplete(-1)
|
2004-07-24 13:54:17 +02:00
|
|
|
, m_policy()
|
2003-10-23 01:00:57 +02:00
|
|
|
, m_ses(ses)
|
2004-06-14 01:30:42 +02:00
|
|
|
, m_picker(0)
|
2004-09-12 12:12:16 +02:00
|
|
|
, m_trackers(m_torrent_file.trackers())
|
2004-01-31 11:20:19 +01:00
|
|
|
, m_last_working_tracker(-1)
|
2003-10-23 01:00:57 +02:00
|
|
|
, m_currently_trying_tracker(0)
|
2004-04-02 00:29:51 +02:00
|
|
|
, m_failed_trackers(0)
|
2003-12-01 06:01:40 +01:00
|
|
|
, m_time_scaler(0)
|
2003-12-07 06:53:04 +01:00
|
|
|
, m_priority(.5)
|
|
|
|
, m_num_pieces(0)
|
2003-12-22 08:14:35 +01:00
|
|
|
, m_got_tracker_response(false)
|
2004-01-12 04:05:10 +01:00
|
|
|
, m_ratio(0.f)
|
2004-04-18 14:28:02 +02:00
|
|
|
, m_total_failed_bytes(0)
|
2004-02-26 01:41:36 +01:00
|
|
|
, m_net_interface(net_interface.ip(), address::any_port)
|
2004-03-23 23:58:18 +01:00
|
|
|
, m_upload_bandwidth_limit(std::numeric_limits<int>::max())
|
2004-03-28 19:45:37 +02:00
|
|
|
, m_download_bandwidth_limit(std::numeric_limits<int>::max())
|
2004-10-18 12:36:47 +02:00
|
|
|
, m_save_path(complete(save_path))
|
2005-05-13 02:39:39 +02:00
|
|
|
, m_compact_mode(compact_mode)
|
2005-06-15 14:54:35 +02:00
|
|
|
, m_metadata_progress(0)
|
|
|
|
, m_metadata_size(0)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-10-29 15:21:09 +02:00
|
|
|
m_uploads_quota.min = 2;
|
|
|
|
m_connections_quota.min = 2;
|
2005-03-07 16:39:06 +01:00
|
|
|
// this will be corrected the next time the main session
|
|
|
|
// distributes resources, i.e. on average in 0.5 seconds
|
|
|
|
m_connections_quota.given = 100;
|
2004-10-29 15:21:09 +02:00
|
|
|
m_uploads_quota.max = std::numeric_limits<int>::max();
|
|
|
|
m_connections_quota.max = std::numeric_limits<int>::max();
|
|
|
|
|
2004-07-24 13:54:17 +02:00
|
|
|
m_policy.reset(new policy(this));
|
2004-06-14 01:30:42 +02:00
|
|
|
bencode(std::back_inserter(m_metadata), metadata["info"]);
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
torrent::torrent(
|
|
|
|
detail::session_impl& ses
|
|
|
|
, char const* tracker_url
|
|
|
|
, sha1_hash const& info_hash
|
|
|
|
, boost::filesystem::path const& save_path
|
2005-05-13 02:39:39 +02:00
|
|
|
, address const& net_interface
|
|
|
|
, bool compact_mode)
|
2004-10-10 02:42:48 +02:00
|
|
|
: m_torrent_file(info_hash)
|
2004-06-14 01:30:42 +02:00
|
|
|
, m_abort(false)
|
|
|
|
, m_paused(false)
|
2004-07-24 13:54:17 +02:00
|
|
|
, m_just_paused(false)
|
2004-06-14 01:30:42 +02:00
|
|
|
, m_event(tracker_request::started)
|
|
|
|
, m_block_size(0)
|
|
|
|
, m_storage(0)
|
2004-10-14 03:17:04 +02:00
|
|
|
, m_next_request(second_clock::universal_time())
|
2004-06-14 01:30:42 +02:00
|
|
|
, m_duration(1800)
|
2005-02-21 14:59:24 +01:00
|
|
|
, m_complete(-1)
|
|
|
|
, m_incomplete(-1)
|
2004-07-24 13:54:17 +02:00
|
|
|
, m_policy()
|
2004-06-14 01:30:42 +02:00
|
|
|
, m_ses(ses)
|
|
|
|
, m_picker(0)
|
|
|
|
, m_last_working_tracker(-1)
|
|
|
|
, m_currently_trying_tracker(0)
|
|
|
|
, m_failed_trackers(0)
|
|
|
|
, m_time_scaler(0)
|
|
|
|
, m_priority(.5)
|
|
|
|
, m_num_pieces(0)
|
|
|
|
, m_got_tracker_response(false)
|
|
|
|
, m_ratio(0.f)
|
|
|
|
, m_total_failed_bytes(0)
|
|
|
|
, m_net_interface(net_interface.ip(), address::any_port)
|
|
|
|
, m_upload_bandwidth_limit(std::numeric_limits<int>::max())
|
|
|
|
, m_download_bandwidth_limit(std::numeric_limits<int>::max())
|
2004-10-18 12:36:47 +02:00
|
|
|
, m_save_path(complete(save_path))
|
2005-05-13 02:39:39 +02:00
|
|
|
, m_compact_mode(compact_mode)
|
2005-06-15 14:54:35 +02:00
|
|
|
, m_metadata_progress(0)
|
|
|
|
, m_metadata_size(0)
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
2004-10-29 15:21:09 +02:00
|
|
|
m_uploads_quota.min = 2;
|
|
|
|
m_connections_quota.min = 2;
|
2005-03-07 16:39:06 +01:00
|
|
|
// this will be corrected the next time the main session
|
|
|
|
// distributes resources, i.e. on average in 0.5 seconds
|
|
|
|
m_connections_quota.given = 100;
|
2004-10-29 15:21:09 +02:00
|
|
|
m_uploads_quota.max = std::numeric_limits<int>::max();
|
|
|
|
m_connections_quota.max = std::numeric_limits<int>::max();
|
|
|
|
|
2004-09-12 12:12:16 +02:00
|
|
|
m_trackers.push_back(announce_entry(tracker_url));
|
2004-07-24 13:54:17 +02:00
|
|
|
m_requested_metadata.resize(256, 0);
|
|
|
|
m_policy.reset(new policy(this));
|
2004-06-14 01:30:42 +02:00
|
|
|
m_torrent_file.add_tracker(tracker_url);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
torrent::~torrent()
|
|
|
|
{
|
2004-02-29 22:33:17 +01:00
|
|
|
assert(m_connections.empty());
|
2003-12-14 06:56:12 +01:00
|
|
|
if (m_ses.m_abort) m_abort = true;
|
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
void torrent::init()
|
|
|
|
{
|
|
|
|
assert(m_torrent_file.is_valid());
|
2004-11-18 23:33:50 +01:00
|
|
|
assert(m_torrent_file.num_files() > 0);
|
|
|
|
assert(m_torrent_file.total_size() > 0);
|
2004-06-14 01:30:42 +02:00
|
|
|
|
|
|
|
m_have_pieces.resize(m_torrent_file.num_pieces(), false);
|
2004-10-18 00:23:08 +02:00
|
|
|
m_storage = std::auto_ptr<piece_manager>(new piece_manager(m_torrent_file, m_save_path));
|
2004-06-14 01:30:42 +02:00
|
|
|
m_block_size = calculate_block_size(m_torrent_file);
|
2004-10-18 00:23:08 +02:00
|
|
|
m_picker = std::auto_ptr<piece_picker>(new piece_picker(
|
2004-06-14 01:30:42 +02:00
|
|
|
static_cast<int>(m_torrent_file.piece_length() / m_block_size)
|
|
|
|
, static_cast<int>((m_torrent_file.total_size()+m_block_size-1)/m_block_size)));
|
|
|
|
}
|
|
|
|
|
2004-02-26 01:27:06 +01:00
|
|
|
void torrent::use_interface(const char* net_interface)
|
|
|
|
{
|
|
|
|
m_net_interface = address(net_interface, address::any_port);
|
|
|
|
}
|
|
|
|
|
2004-07-24 13:54:17 +02:00
|
|
|
// returns true if it is time for this torrent to make another
|
|
|
|
// tracker request
|
|
|
|
bool torrent::should_request()
|
|
|
|
{
|
|
|
|
if (m_just_paused)
|
|
|
|
{
|
|
|
|
m_just_paused = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return !m_paused &&
|
2004-10-14 03:17:04 +02:00
|
|
|
m_next_request < second_clock::universal_time();
|
2004-07-24 13:54:17 +02:00
|
|
|
}
|
|
|
|
|
2004-01-21 14:16:11 +01:00
|
|
|
void torrent::tracker_response(
|
2005-04-24 02:50:52 +02:00
|
|
|
tracker_request const&
|
2005-03-24 13:13:47 +01:00
|
|
|
, std::vector<peer_entry>& peer_list
|
2005-02-21 14:59:24 +01:00
|
|
|
, int interval
|
|
|
|
, int complete
|
2005-02-23 09:57:54 +01:00
|
|
|
, int incomplete)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-04-02 00:29:51 +02:00
|
|
|
m_failed_trackers = 0;
|
2004-09-16 19:18:10 +02:00
|
|
|
// less than 5 minutes announce intervals
|
2004-01-25 13:37:15 +01:00
|
|
|
// are insane.
|
2004-09-16 19:18:10 +02:00
|
|
|
if (interval < 60 * 5) interval = 60 * 5;
|
2004-01-25 13:37:15 +01:00
|
|
|
|
2004-01-21 14:16:11 +01:00
|
|
|
m_last_working_tracker
|
2004-09-12 12:12:16 +02:00
|
|
|
= prioritize_tracker(m_currently_trying_tracker);
|
2004-01-21 14:16:11 +01:00
|
|
|
m_currently_trying_tracker = 0;
|
2003-10-23 18:55:52 +02:00
|
|
|
|
2004-01-21 14:16:11 +01:00
|
|
|
m_duration = interval;
|
2005-06-12 02:21:37 +02:00
|
|
|
if (peer_list.empty() && !is_seed())
|
2004-07-06 20:20:07 +02:00
|
|
|
{
|
|
|
|
// if the peer list is empty, we should contact the
|
|
|
|
// tracker soon again to see if there are any peers
|
2004-12-21 13:30:09 +01:00
|
|
|
m_next_request = second_clock::universal_time() + boost::posix_time::minutes(2);
|
2004-07-06 20:20:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-10-14 03:17:04 +02:00
|
|
|
m_next_request = second_clock::universal_time() + boost::posix_time::seconds(m_duration);
|
2004-07-06 20:20:07 +02:00
|
|
|
}
|
2003-10-23 18:55:52 +02:00
|
|
|
|
2005-02-21 14:59:24 +01:00
|
|
|
if (complete >= 0) m_complete = complete;
|
|
|
|
if (incomplete >= 0) m_incomplete = incomplete;
|
|
|
|
|
2004-01-21 14:16:11 +01:00
|
|
|
// connect to random peers from the list
|
|
|
|
std::random_shuffle(peer_list.begin(), peer_list.end());
|
2003-10-23 18:55:52 +02:00
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2005-03-19 13:22:40 +01:00
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
2004-01-21 14:16:11 +01:00
|
|
|
std::stringstream s;
|
2004-01-22 23:45:52 +01:00
|
|
|
s << "TRACKER RESPONSE:\n"
|
|
|
|
"interval: " << m_duration << "\n"
|
|
|
|
"peers:\n";
|
2004-01-21 14:16:11 +01:00
|
|
|
for (std::vector<peer_entry>::const_iterator i = peer_list.begin();
|
|
|
|
i != peer_list.end();
|
|
|
|
++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-21 14:16:11 +01:00
|
|
|
s << " " << std::setfill(' ') << std::setw(16) << i->ip
|
2004-01-22 23:45:52 +01:00
|
|
|
<< " " << std::setw(5) << std::dec << i->port << " ";
|
|
|
|
if (!i->id.is_all_zeros()) s << " " << i->id << " " << identify_client(i->id);
|
|
|
|
s << "\n";
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-21 14:16:11 +01:00
|
|
|
debug_log(s.str());
|
|
|
|
#endif
|
|
|
|
// for each of the peers we got from the tracker
|
|
|
|
for (std::vector<peer_entry>::iterator i = peer_list.begin();
|
|
|
|
i != peer_list.end();
|
|
|
|
++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-21 14:16:11 +01:00
|
|
|
// don't make connections to ourself
|
|
|
|
if (i->id == m_ses.get_peer_id())
|
|
|
|
continue;
|
|
|
|
|
2004-02-26 01:27:06 +01:00
|
|
|
address a(i->ip.c_str(), i->port);
|
2004-01-21 14:16:11 +01:00
|
|
|
|
|
|
|
m_policy->peer_from_tracker(a, i->id);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2005-03-10 10:59:12 +01:00
|
|
|
if (m_ses.m_alerts.should_post(alert::info))
|
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << "Got response from tracker: "
|
|
|
|
<< m_trackers[m_last_working_tracker].url;
|
|
|
|
m_ses.m_alerts.post_alert(tracker_reply_alert(
|
|
|
|
get_handle(), s.str()));
|
|
|
|
}
|
2003-12-22 08:14:35 +01:00
|
|
|
m_got_tracker_response = true;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-18 02:58:33 +01:00
|
|
|
size_type torrent::bytes_left() const
|
2003-12-07 06:53:04 +01:00
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
// if we don't have the metadata yet, we
|
|
|
|
// cannot tell how big the torrent is.
|
|
|
|
if (!valid_metadata()) return -1;
|
2005-05-30 19:43:03 +02:00
|
|
|
return m_torrent_file.total_size() - boost::get<0>(bytes_done());
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
|
|
|
|
2005-05-30 19:43:03 +02:00
|
|
|
// the first value is the total number of bytes downloaded
|
|
|
|
// the second value is the number of bytes of those that haven't
|
|
|
|
// been filtered as not wanted we have downloaded
|
|
|
|
boost::tuple<size_type, size_type> torrent::bytes_done() const
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
if (!valid_metadata()) return 0;
|
|
|
|
|
|
|
|
assert(m_picker.get());
|
2005-05-30 19:43:03 +02:00
|
|
|
const int last_piece = m_torrent_file.num_pieces() - 1;
|
2004-01-15 17:45:34 +01:00
|
|
|
|
2005-05-30 19:43:03 +02:00
|
|
|
size_type wanted_done = (m_num_pieces - m_picker->num_have_filtered())
|
|
|
|
* m_torrent_file.piece_length();
|
|
|
|
|
2004-01-18 02:58:33 +01:00
|
|
|
size_type total_done
|
2004-01-15 17:45:34 +01:00
|
|
|
= m_num_pieces * m_torrent_file.piece_length();
|
|
|
|
|
|
|
|
// if we have the last piece, we have to correct
|
|
|
|
// the amount we have, since the first calculation
|
|
|
|
// assumed all pieces were of equal size
|
2003-12-07 06:53:04 +01:00
|
|
|
if (m_have_pieces[last_piece])
|
|
|
|
{
|
2005-05-30 19:43:03 +02:00
|
|
|
int corr = m_torrent_file.piece_size(last_piece)
|
|
|
|
- m_torrent_file.piece_length();
|
|
|
|
total_done += corr;
|
|
|
|
if (!m_picker->is_filtered(last_piece))
|
|
|
|
wanted_done += corr;
|
2003-12-07 06:53:04 +01:00
|
|
|
}
|
|
|
|
|
2004-01-15 17:45:34 +01:00
|
|
|
const std::vector<piece_picker::downloading_piece>& dl_queue
|
2004-06-14 01:30:42 +02:00
|
|
|
= m_picker->get_download_queue();
|
2004-01-15 17:45:34 +01:00
|
|
|
|
2005-05-30 19:43:03 +02:00
|
|
|
const int blocks_per_piece = static_cast<int>(
|
|
|
|
m_torrent_file.piece_length() / m_block_size);
|
2004-01-15 17:45:34 +01:00
|
|
|
|
|
|
|
for (std::vector<piece_picker::downloading_piece>::const_iterator i =
|
2005-05-30 19:43:03 +02:00
|
|
|
dl_queue.begin(); i != dl_queue.end(); ++i)
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2005-05-30 19:43:03 +02:00
|
|
|
int corr = 0;
|
2004-01-15 17:45:34 +01:00
|
|
|
assert(!m_have_pieces[i->index]);
|
|
|
|
|
|
|
|
for (int j = 0; j < blocks_per_piece; ++j)
|
|
|
|
{
|
2005-05-30 19:43:03 +02:00
|
|
|
corr += (i->finished_blocks[j]) * m_block_size;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// correction if this was the last piece
|
|
|
|
// and if we have the last block
|
|
|
|
if (i->index == last_piece
|
2004-06-14 01:30:42 +02:00
|
|
|
&& i->finished_blocks[m_picker->blocks_in_last_piece()-1])
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2005-05-30 19:43:03 +02:00
|
|
|
corr -= m_block_size;
|
|
|
|
corr += m_torrent_file.piece_size(last_piece) % m_block_size;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2005-05-30 19:43:03 +02:00
|
|
|
total_done += corr;
|
|
|
|
if (!m_picker->is_filtered(i->index))
|
|
|
|
wanted_done += corr;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
|
|
|
|
2004-09-16 03:14:16 +02:00
|
|
|
std::map<piece_block, int> downloading_piece;
|
|
|
|
for (const_peer_iterator i = begin(); i != end(); ++i)
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
|
|
|
boost::optional<piece_block_progress> p
|
|
|
|
= i->second->downloading_piece();
|
|
|
|
if (p)
|
|
|
|
{
|
|
|
|
if (m_have_pieces[p->piece_index])
|
|
|
|
continue;
|
2004-09-16 03:14:16 +02:00
|
|
|
|
|
|
|
piece_block block(p->piece_index, p->block_index);
|
|
|
|
if (m_picker->is_finished(block))
|
2004-01-15 17:45:34 +01:00
|
|
|
continue;
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2004-09-16 03:14:16 +02:00
|
|
|
std::map<piece_block, int>::iterator dp
|
|
|
|
= downloading_piece.find(block);
|
|
|
|
if (dp != downloading_piece.end())
|
|
|
|
{
|
|
|
|
if (dp->second < p->bytes_downloaded)
|
|
|
|
dp->second = p->bytes_downloaded;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
downloading_piece[block] = p->bytes_downloaded;
|
|
|
|
}
|
2004-01-15 17:45:34 +01:00
|
|
|
assert(p->bytes_downloaded <= p->full_block_bytes);
|
|
|
|
}
|
|
|
|
}
|
2004-09-16 03:14:16 +02:00
|
|
|
for (std::map<piece_block, int>::iterator i = downloading_piece.begin();
|
|
|
|
i != downloading_piece.end(); ++i)
|
2005-05-30 19:43:03 +02:00
|
|
|
{
|
2004-09-16 03:14:16 +02:00
|
|
|
total_done += i->second;
|
2005-05-30 19:43:03 +02:00
|
|
|
if (!m_picker->is_filtered(i->first.piece_index))
|
|
|
|
wanted_done += i->second;
|
|
|
|
}
|
|
|
|
return boost::make_tuple(total_done, wanted_done);
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2003-12-01 22:27:27 +01:00
|
|
|
void torrent::piece_failed(int index)
|
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(m_storage.get());
|
|
|
|
assert(m_picker.get());
|
2004-01-25 13:37:15 +01:00
|
|
|
assert(index >= 0);
|
|
|
|
assert(index < m_torrent_file.num_pieces());
|
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
if (m_ses.m_alerts.should_post(alert::info))
|
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << "hash for piece " << index << " failed";
|
2004-01-07 01:48:02 +01:00
|
|
|
m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index, s.str()));
|
2003-12-22 08:14:35 +01:00
|
|
|
}
|
2004-04-18 14:28:02 +02:00
|
|
|
// increase the total amount of failed bytes
|
|
|
|
m_total_failed_bytes += m_torrent_file.piece_size(index);
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
std::vector<address> downloaders;
|
2004-06-14 01:30:42 +02:00
|
|
|
m_picker->get_downloaders(downloaders, index);
|
2003-12-01 22:27:27 +01:00
|
|
|
|
|
|
|
// decrease the trust point of all peers that sent
|
|
|
|
// parts of this piece.
|
2004-09-12 12:12:16 +02:00
|
|
|
// first, build a set of all peers that participated
|
|
|
|
std::set<address> peers;
|
|
|
|
std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin()));
|
|
|
|
|
|
|
|
for (std::set<address>::iterator i = peers.begin()
|
|
|
|
, end(peers.end()); i != end; ++i)
|
2003-12-01 22:27:27 +01:00
|
|
|
{
|
2004-01-13 04:08:59 +01:00
|
|
|
peer_iterator p = m_connections.find(*i);
|
|
|
|
if (p == m_connections.end()) continue;
|
|
|
|
p->second->received_invalid_data();
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-09-12 12:12:16 +02:00
|
|
|
// either, we have received too many failed hashes
|
|
|
|
// or this was the only peer that sent us this piece.
|
|
|
|
if (p->second->trust_points() <= -7 || peers.size() == 1)
|
2003-12-01 22:27:27 +01:00
|
|
|
{
|
2003-12-14 06:56:12 +01:00
|
|
|
// we don't trust this peer anymore
|
|
|
|
// ban it.
|
2004-03-21 03:03:37 +01:00
|
|
|
if (m_ses.m_alerts.should_post(alert::info))
|
|
|
|
{
|
|
|
|
m_ses.m_alerts.post_alert(peer_ban_alert(
|
|
|
|
p->first
|
|
|
|
, get_handle()
|
|
|
|
, "banning peer because of too many corrupt pieces"));
|
|
|
|
}
|
2004-03-30 21:11:07 +02:00
|
|
|
m_policy->ban_peer(*p->second);
|
|
|
|
p->second->disconnect();
|
2003-12-01 22:27:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// we have to let the piece_picker know that
|
|
|
|
// this piece failed the check as it can restore it
|
|
|
|
// and mark it as being interesting for download
|
|
|
|
// TODO: do this more intelligently! and keep track
|
|
|
|
// of how much crap (data that failed hash-check) and
|
|
|
|
// how much redundant data we have downloaded
|
|
|
|
// if some clients has sent more than one piece
|
|
|
|
// start with redownloading the pieces that the client
|
|
|
|
// that has sent the least number of pieces
|
2004-06-14 01:30:42 +02:00
|
|
|
m_picker->restore_piece(index);
|
|
|
|
m_storage->mark_failed(index);
|
2004-01-09 11:50:22 +01:00
|
|
|
|
|
|
|
assert(m_have_pieces[index] == false);
|
2003-12-01 22:27:27 +01:00
|
|
|
}
|
|
|
|
|
2005-03-05 15:17:17 +01:00
|
|
|
void torrent::abort()
|
|
|
|
{
|
|
|
|
m_abort = true;
|
|
|
|
m_event = tracker_request::stopped;
|
|
|
|
// disconnect all peers and close all
|
|
|
|
// files belonging to the torrent
|
|
|
|
disconnect_all();
|
2005-03-10 12:41:22 +01:00
|
|
|
if (m_storage.get()) m_storage->release_files();
|
2005-03-05 15:17:17 +01:00
|
|
|
}
|
2003-12-01 22:27:27 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void torrent::announce_piece(int index)
|
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(m_picker.get());
|
2004-01-25 13:37:15 +01:00
|
|
|
assert(index >= 0);
|
|
|
|
assert(index < m_torrent_file.num_pieces());
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
std::vector<address> downloaders;
|
2004-06-14 01:30:42 +02:00
|
|
|
m_picker->get_downloaders(downloaders, index);
|
2003-12-01 22:27:27 +01:00
|
|
|
|
|
|
|
// increase the trust point of all peers that sent
|
|
|
|
// parts of this piece.
|
2004-09-12 12:12:16 +02:00
|
|
|
std::set<address> peers;
|
|
|
|
std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin()));
|
|
|
|
|
|
|
|
for (std::set<address>::iterator i = peers.begin()
|
|
|
|
, end(peers.end()); i != end; ++i)
|
2003-12-01 22:27:27 +01:00
|
|
|
{
|
2004-01-13 04:08:59 +01:00
|
|
|
peer_iterator p = m_connections.find(*i);
|
|
|
|
if (p == m_connections.end()) continue;
|
|
|
|
p->second->received_valid_data();
|
2003-12-01 22:27:27 +01:00
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
m_picker->we_have(index);
|
2004-01-13 04:08:59 +01:00
|
|
|
for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
|
|
|
|
i->second->announce_piece(index);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
std::string torrent::tracker_login() const
|
2004-02-22 23:40:45 +01:00
|
|
|
{
|
|
|
|
if (m_username.empty() && m_password.empty()) return "";
|
|
|
|
return m_username + ":" + m_password;
|
|
|
|
}
|
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
void torrent::filter_piece(int index, bool filter)
|
|
|
|
{
|
|
|
|
// this call is only valid on torrents with metadata
|
|
|
|
assert(m_picker.get());
|
|
|
|
assert(index >= 0);
|
|
|
|
assert(index < m_torrent_file.num_pieces());
|
|
|
|
|
2005-05-30 19:43:03 +02:00
|
|
|
// TODO: update peer's interesting-bit
|
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
if (filter) m_picker->mark_as_filtered(index);
|
|
|
|
else m_picker->mark_as_unfiltered(index);
|
|
|
|
}
|
|
|
|
|
2005-06-23 01:04:37 +02:00
|
|
|
void torrent::filter_pieces(std::vector<bool> const& bitmask)
|
|
|
|
{
|
|
|
|
// this call is only valid on torrents with metadata
|
|
|
|
assert(m_picker.get());
|
|
|
|
|
|
|
|
// TODO: update peer's interesting-bit
|
|
|
|
|
|
|
|
std::vector<std::pair<int, bool> > state;
|
|
|
|
state.reserve(100);
|
|
|
|
int index = 0;
|
|
|
|
for (std::vector<bool>::const_iterator i = bitmask.begin()
|
|
|
|
, end(bitmask.end()); i != end; ++i, ++index)
|
|
|
|
{
|
|
|
|
if (m_picker->is_filtered(index) == *i) continue;
|
|
|
|
state.push_back(std::make_pair(index, *i));
|
|
|
|
}
|
|
|
|
std::random_shuffle(state.begin(), state.end());
|
|
|
|
for (std::vector<std::pair<int, bool> >::iterator i = state.begin();
|
|
|
|
i != state.end(); ++i)
|
|
|
|
{
|
|
|
|
if (i->second) m_picker->mark_as_filtered(i->first);
|
|
|
|
else m_picker->mark_as_unfiltered(i->first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-30 19:43:03 +02:00
|
|
|
// TODO: add a function to set the filter with one call
|
|
|
|
|
2005-05-25 12:01:01 +02:00
|
|
|
bool torrent::is_piece_filtered(int index) const
|
|
|
|
{
|
|
|
|
// this call is only valid on torrents with metadata
|
|
|
|
assert(m_picker.get());
|
|
|
|
assert(index >= 0);
|
|
|
|
assert(index < m_torrent_file.num_pieces());
|
|
|
|
|
|
|
|
return m_picker->is_filtered(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::filtered_pieces(std::vector<bool>& bitmask) const
|
|
|
|
{
|
|
|
|
// this call is only valid on torrents with metadata
|
|
|
|
assert(m_picker.get());
|
|
|
|
m_picker->filtered_pieces(bitmask);
|
|
|
|
}
|
|
|
|
|
2005-07-04 17:31:27 +02:00
|
|
|
//suggest using this function on filter single file after the downloading progress
|
|
|
|
//that is after called filter_files function
|
|
|
|
//or tmp to change a single file filter policy
|
2005-07-02 10:47:46 +02:00
|
|
|
void torrent::filter_file(int index, bool filter)
|
|
|
|
{
|
2005-07-04 17:31:27 +02:00
|
|
|
// this call is only valid on torrents with metadata
|
|
|
|
if (!valid_metadata()) return;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
__int64 position = 0, start, piece_length;
|
|
|
|
int start_piece, end_piece;
|
2005-07-02 10:47:46 +02:00
|
|
|
|
2005-07-04 17:31:27 +02:00
|
|
|
if (0 < m_torrent_file.num_pieces())
|
|
|
|
{
|
|
|
|
piece_length = m_torrent_file.piece_length();
|
2005-07-02 10:47:46 +02:00
|
|
|
|
2005-07-04 17:31:27 +02:00
|
|
|
for (int i = 0;i < index;++i)
|
|
|
|
{
|
|
|
|
start = position;
|
|
|
|
position += m_torrent_file.file_at(i).size;
|
|
|
|
}
|
2005-07-02 10:47:46 +02:00
|
|
|
|
2005-07-04 17:31:27 +02:00
|
|
|
start_piece = (int)(start / piece_length);
|
|
|
|
end_piece = (int)(position / piece_length);
|
|
|
|
|
|
|
|
for (int filter_index = start_piece;filter_index <= end_piece;++filter_index)
|
|
|
|
{
|
|
|
|
filter_piece(filter_index, filter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
2005-07-02 10:47:46 +02:00
|
|
|
}
|
|
|
|
|
2005-07-04 17:31:27 +02:00
|
|
|
//suggest using this function on filter all files of a torrent file
|
|
|
|
//suggest using this functon as the global static filter policy of a torrent file
|
2005-07-04 01:33:47 +02:00
|
|
|
void torrent::filter_files(std::vector<bool> const& bitmask)
|
|
|
|
{
|
|
|
|
// this call is only valid on torrents with metadata
|
|
|
|
if (!valid_metadata()) return;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
__int64 position = 0, start, piece_length;
|
|
|
|
int start_piece, end_piece;
|
|
|
|
|
2005-07-04 17:31:27 +02:00
|
|
|
if (0 < m_torrent_file.num_pieces())
|
2005-07-04 01:33:47 +02:00
|
|
|
{
|
|
|
|
piece_length = m_torrent_file.piece_length();
|
|
|
|
std::vector<bool> vector_filter_files(m_torrent_file.num_pieces(), false);
|
|
|
|
for (int i=0;i<bitmask.size();i++)
|
|
|
|
{
|
|
|
|
start = position;
|
|
|
|
position += m_torrent_file.file_at(i).size;
|
|
|
|
// is the file selected for undownload?
|
|
|
|
if (true == bitmask[i])
|
|
|
|
{
|
|
|
|
// mark all pieces of the file as filtered
|
|
|
|
start_piece = (int)(start / piece_length);
|
|
|
|
end_piece = (int)(position / piece_length);
|
|
|
|
// if one piece spawns several files, we might
|
|
|
|
// come here several times with the same start_piece, end_piece
|
|
|
|
for (int index = start_piece;index<=end_piece;index++)
|
|
|
|
{
|
|
|
|
vector_filter_files[index] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
filter_pieces(vector_filter_files);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-12 12:12:16 +02:00
|
|
|
void torrent::replace_trackers(std::vector<announce_entry> const& urls)
|
|
|
|
{
|
|
|
|
assert(!urls.empty());
|
|
|
|
m_trackers = urls;
|
|
|
|
if (m_currently_trying_tracker >= (int)m_trackers.size())
|
2004-09-12 15:53:00 +02:00
|
|
|
m_currently_trying_tracker = (int)m_trackers.size()-1;
|
2004-09-12 12:12:16 +02:00
|
|
|
m_last_working_tracker = -1;
|
|
|
|
}
|
|
|
|
|
2004-03-21 03:03:37 +01:00
|
|
|
tracker_request torrent::generate_tracker_request()
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-03-27 23:02:31 +01:00
|
|
|
m_next_request
|
2004-10-14 03:17:04 +02:00
|
|
|
= second_clock::universal_time()
|
2004-04-02 00:29:51 +02:00
|
|
|
+ boost::posix_time::seconds(tracker_retry_delay_max);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-20 23:59:21 +01:00
|
|
|
tracker_request req;
|
|
|
|
req.info_hash = m_torrent_file.info_hash();
|
|
|
|
req.id = m_ses.get_peer_id();
|
|
|
|
req.downloaded = m_stat.total_payload_download();
|
|
|
|
req.uploaded = m_stat.total_payload_upload();
|
|
|
|
req.left = bytes_left();
|
2004-06-14 01:30:42 +02:00
|
|
|
if (req.left == -1) req.left = 1000;
|
2004-01-21 14:16:11 +01:00
|
|
|
req.event = m_event;
|
2004-11-18 23:33:50 +01:00
|
|
|
m_event = tracker_request::none;
|
2004-09-12 12:12:16 +02:00
|
|
|
req.url = m_trackers[m_currently_trying_tracker].url;
|
2005-03-07 16:39:06 +01:00
|
|
|
assert(m_connections_quota.given > 0);
|
2004-04-15 00:16:56 +02:00
|
|
|
req.num_want = std::max(
|
2004-10-29 15:21:09 +02:00
|
|
|
(m_connections_quota.given
|
2005-03-07 16:39:06 +01:00
|
|
|
- m_policy->num_peers()), 10);
|
2005-03-08 15:16:14 +01:00
|
|
|
// if we are aborting. we don't want any new peers
|
|
|
|
if (req.event == tracker_request::stopped)
|
|
|
|
req.num_want = 0;
|
2004-03-21 03:03:37 +01:00
|
|
|
|
|
|
|
// default initialize, these should be set by caller
|
|
|
|
// before passing the request to the tracker_manager
|
|
|
|
req.listen_port = 0;
|
|
|
|
req.key = 0;
|
|
|
|
|
2004-01-20 23:59:21 +01:00
|
|
|
return req;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::remove_peer(peer_connection* p)
|
|
|
|
{
|
2004-01-25 13:37:15 +01:00
|
|
|
assert(p != 0);
|
|
|
|
|
2004-01-13 04:08:59 +01:00
|
|
|
peer_iterator i = m_connections.find(p->get_socket()->sender());
|
2003-10-23 01:00:57 +02:00
|
|
|
assert(i != m_connections.end());
|
|
|
|
|
|
|
|
// if the peer_connection was downloading any pieces
|
|
|
|
// abort them
|
2003-12-08 22:59:48 +01:00
|
|
|
for (std::deque<piece_block>::const_iterator i = p->download_queue().begin();
|
2003-10-23 01:00:57 +02:00
|
|
|
i != p->download_queue().end();
|
|
|
|
++i)
|
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
m_picker->abort_download(*i);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
if (valid_metadata())
|
2003-12-18 04:30:41 +01:00
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
std::vector<int> piece_list;
|
|
|
|
const std::vector<bool>& pieces = p->get_bitfield();
|
|
|
|
|
|
|
|
for (std::vector<bool>::const_iterator i = pieces.begin();
|
|
|
|
i != pieces.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
if (*i) piece_list.push_back(static_cast<int>(i - pieces.begin()));
|
|
|
|
}
|
2003-12-18 04:30:41 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
std::random_shuffle(piece_list.begin(), piece_list.end());
|
2003-12-18 04:30:41 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
for (std::vector<int>::iterator i = piece_list.begin();
|
|
|
|
i != piece_list.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
peer_lost(*i);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_policy->connection_closed(*p);
|
|
|
|
m_connections.erase(i);
|
|
|
|
}
|
|
|
|
|
2004-01-15 17:45:34 +01:00
|
|
|
peer_connection& torrent::connect_to_peer(const address& a)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
boost::shared_ptr<socket> s(new socket(socket::tcp, false));
|
2004-02-26 01:27:06 +01:00
|
|
|
s->connect(a, m_net_interface);
|
2003-11-05 00:27:06 +01:00
|
|
|
boost::shared_ptr<peer_connection> c(new peer_connection(
|
|
|
|
m_ses
|
2003-12-07 06:53:04 +01:00
|
|
|
, m_ses.m_selector
|
2003-11-05 00:27:06 +01:00
|
|
|
, this
|
2004-01-15 17:45:34 +01:00
|
|
|
, s));
|
2004-01-13 04:08:59 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
detail::session_impl::connection_map::iterator p =
|
2003-12-07 06:53:04 +01:00
|
|
|
m_ses.m_connections.insert(std::make_pair(s, c)).first;
|
2003-12-01 06:01:40 +01:00
|
|
|
|
|
|
|
// add the newly connected peer to this torrent's peer list
|
2004-01-13 04:08:59 +01:00
|
|
|
assert(m_connections.find(p->second->get_socket()->sender())
|
2003-12-01 06:01:40 +01:00
|
|
|
== m_connections.end());
|
|
|
|
|
2004-01-13 04:08:59 +01:00
|
|
|
m_connections.insert(
|
|
|
|
std::make_pair(
|
|
|
|
p->second->get_socket()->sender()
|
|
|
|
, boost::get_pointer(p->second)));
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
m_ses.m_selector.monitor_readability(s);
|
|
|
|
m_ses.m_selector.monitor_errors(s);
|
2003-12-14 06:56:12 +01:00
|
|
|
return *c;
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::attach_peer(peer_connection* p)
|
|
|
|
{
|
2004-01-25 13:37:15 +01:00
|
|
|
assert(p != 0);
|
2004-01-13 04:08:59 +01:00
|
|
|
assert(m_connections.find(p->get_socket()->sender()) == m_connections.end());
|
2004-01-15 01:46:44 +01:00
|
|
|
assert(!p->is_local());
|
2004-01-13 04:08:59 +01:00
|
|
|
|
|
|
|
m_connections.insert(std::make_pair(p->get_socket()->sender(), p));
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
detail::session_impl::connection_map::iterator i
|
2003-12-07 06:53:04 +01:00
|
|
|
= m_ses.m_connections.find(p->get_socket());
|
|
|
|
assert(i != m_ses.m_connections.end());
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
m_policy->new_connection(*i->second);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-20 23:59:21 +01:00
|
|
|
void torrent::disconnect_all()
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-13 04:08:59 +01:00
|
|
|
for (peer_iterator i = m_connections.begin();
|
2004-01-20 12:01:50 +01:00
|
|
|
i != m_connections.end();
|
|
|
|
++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-13 04:08:59 +01:00
|
|
|
assert(i->second->associated_torrent() == this);
|
2004-01-20 12:01:50 +01:00
|
|
|
i->second->disconnect();
|
|
|
|
}
|
|
|
|
}
|
2004-01-13 04:08:59 +01:00
|
|
|
|
2005-06-11 01:12:50 +02:00
|
|
|
// called when torrent is finished (all interested pieces downloaded)
|
|
|
|
void torrent::finished()
|
2004-01-20 12:01:50 +01:00
|
|
|
{
|
2004-01-21 14:16:11 +01:00
|
|
|
if (alerts().should_post(alert::info))
|
|
|
|
{
|
|
|
|
alerts().post_alert(torrent_finished_alert(
|
|
|
|
get_handle()
|
2004-04-02 00:29:51 +02:00
|
|
|
, "torrent has finished downloading"));
|
2004-01-21 14:16:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// disconnect all seeds
|
2004-01-20 12:01:50 +01:00
|
|
|
for (peer_iterator i = m_connections.begin();
|
2005-06-11 01:12:50 +02:00
|
|
|
i != m_connections.end(); ++i)
|
2004-01-20 12:01:50 +01:00
|
|
|
{
|
|
|
|
assert(i->second->associated_torrent() == this);
|
|
|
|
if (i->second->is_seed())
|
|
|
|
i->second->disconnect();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-21 14:16:11 +01:00
|
|
|
|
2005-03-10 12:26:55 +01:00
|
|
|
m_storage->release_files();
|
2005-06-11 01:12:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// called when torrent is complete (all pieces downloaded)
|
|
|
|
void torrent::completed()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
if (alerts().should_post(alert::info))
|
|
|
|
{
|
|
|
|
alerts().post_alert(torrent_complete_alert(
|
|
|
|
get_handle()
|
|
|
|
, "torrent is complete"));
|
|
|
|
}
|
|
|
|
*/
|
2004-01-21 14:16:11 +01:00
|
|
|
// make the next tracker request
|
|
|
|
// be a completed-event
|
|
|
|
m_event = tracker_request::completed;
|
|
|
|
force_tracker_request();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-09-12 12:12:16 +02:00
|
|
|
// this will move the tracker with the given index
|
|
|
|
// to a prioritized position in the list (move it towards
|
|
|
|
// the begining) and return the new index to the tracker.
|
|
|
|
int torrent::prioritize_tracker(int index)
|
|
|
|
{
|
|
|
|
assert(index >= 0);
|
|
|
|
if (index >= (int)m_trackers.size()) return (int)m_trackers.size()-1;
|
|
|
|
|
|
|
|
while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier)
|
|
|
|
{
|
|
|
|
std::swap(m_trackers[index].url, m_trackers[index-1].url);
|
|
|
|
--index;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
void torrent::try_next_tracker()
|
|
|
|
{
|
2004-10-10 02:42:48 +02:00
|
|
|
using namespace boost::posix_time;
|
2004-04-02 00:29:51 +02:00
|
|
|
++m_currently_trying_tracker;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-09-12 12:12:16 +02:00
|
|
|
if ((unsigned)m_currently_trying_tracker >= m_trackers.size())
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-04-02 00:29:51 +02:00
|
|
|
int delay = tracker_retry_delay_min
|
|
|
|
+ std::min(m_failed_trackers, (int)tracker_failed_max)
|
|
|
|
* (tracker_retry_delay_max - tracker_retry_delay_min)
|
|
|
|
/ tracker_failed_max;
|
|
|
|
|
|
|
|
++m_failed_trackers;
|
2003-10-23 01:00:57 +02:00
|
|
|
// if we've looped the tracker list, wait a bit before retrying
|
|
|
|
m_currently_trying_tracker = 0;
|
2004-10-14 03:17:04 +02:00
|
|
|
m_next_request = second_clock::universal_time() + seconds(delay);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// don't delay before trying the next tracker
|
2004-10-14 03:17:04 +02:00
|
|
|
m_next_request = second_clock::universal_time();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-09-12 12:12:16 +02:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
void torrent::check_files(detail::piece_checker_data& data,
|
2005-05-29 19:25:13 +02:00
|
|
|
boost::mutex& mutex, bool lock_session)
|
2003-11-07 02:44:30 +01:00
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(m_storage.get());
|
2005-05-13 02:39:39 +02:00
|
|
|
m_storage->check_pieces(mutex, data, m_have_pieces, m_compact_mode);
|
2005-05-29 19:25:13 +02:00
|
|
|
|
|
|
|
// TODO: temporary solution. This function should only
|
|
|
|
// be called from the checker thread, and then this
|
|
|
|
// hack can be removed (because the session should always
|
|
|
|
// be locked then)
|
|
|
|
boost::mutex temp;
|
|
|
|
boost::mutex* m = &temp;
|
|
|
|
if (lock_session) m = &m_ses.m_mutex;
|
|
|
|
|
|
|
|
boost::mutex::scoped_lock l(mutex);
|
2005-06-15 14:54:35 +02:00
|
|
|
if (data.abort) return;
|
|
|
|
|
2005-05-29 19:25:13 +02:00
|
|
|
boost::mutex::scoped_lock l2(*m);
|
|
|
|
m_num_pieces = std::count(
|
2003-12-07 16:03:06 +01:00
|
|
|
m_have_pieces.begin()
|
|
|
|
, m_have_pieces.end()
|
2005-05-29 19:25:13 +02:00
|
|
|
, true);
|
2003-12-07 16:03:06 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
m_picker->files_checked(m_have_pieces, data.unfinished_pieces);
|
2003-11-07 02:44:30 +01:00
|
|
|
}
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
alert_manager& torrent::alerts() const
|
|
|
|
{
|
|
|
|
return m_ses.m_alerts;
|
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
boost::filesystem::path torrent::save_path() const
|
|
|
|
{
|
2004-10-10 02:42:48 +02:00
|
|
|
return m_save_path;
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
|
|
|
|
2004-07-18 02:39:58 +02:00
|
|
|
bool torrent::move_storage(boost::filesystem::path const& save_path)
|
|
|
|
{
|
2005-01-10 12:14:22 +01:00
|
|
|
bool ret = true;
|
|
|
|
if (m_storage.get())
|
|
|
|
{
|
|
|
|
ret = m_storage->move_storage(save_path);
|
|
|
|
m_save_path = m_storage->save_path();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_save_path = save_path;
|
|
|
|
}
|
2004-11-18 23:33:50 +01:00
|
|
|
return ret;
|
2004-07-18 02:39:58 +02:00
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
piece_manager& torrent::filesystem()
|
|
|
|
{
|
|
|
|
assert(m_storage.get());
|
|
|
|
return *m_storage;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
torrent_handle torrent::get_handle() const
|
|
|
|
{
|
|
|
|
return torrent_handle(&m_ses, 0, m_torrent_file.info_hash());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
#ifndef NDEBUG
|
2004-06-14 01:30:42 +02:00
|
|
|
void torrent::check_invariant() const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
assert(m_num_pieces
|
|
|
|
== std::count(m_have_pieces.begin(), m_have_pieces.end(), true));
|
2004-01-25 13:37:15 +01:00
|
|
|
assert(m_priority >= 0.f && m_priority < 1.f);
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(!valid_metadata() || m_block_size > 0);
|
|
|
|
assert(!valid_metadata() || (m_torrent_file.piece_length() % m_block_size) == 0);
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
void torrent::set_max_uploads(int limit)
|
|
|
|
{
|
|
|
|
assert(limit >= -1);
|
|
|
|
if (limit == -1) limit = std::numeric_limits<int>::max();
|
|
|
|
m_uploads_quota.max = std::max(m_uploads_quota.min, limit);
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::set_max_connections(int limit)
|
|
|
|
{
|
|
|
|
assert(limit >= -1);
|
|
|
|
if (limit == -1) limit = std::numeric_limits<int>::max();
|
|
|
|
m_connections_quota.max = std::max(m_connections_quota.min, limit);
|
|
|
|
}
|
|
|
|
|
2004-07-24 13:54:17 +02:00
|
|
|
void torrent::set_upload_limit(int limit)
|
|
|
|
{
|
|
|
|
assert(limit >= -1);
|
|
|
|
if (limit == -1) limit = std::numeric_limits<int>::max();
|
|
|
|
if (limit < num_peers() * 10) limit = num_peers() * 10;
|
|
|
|
m_upload_bandwidth_limit = limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::set_download_limit(int limit)
|
|
|
|
{
|
|
|
|
assert(limit >= -1);
|
|
|
|
if (limit == -1) limit = std::numeric_limits<int>::max();
|
|
|
|
if (limit < num_peers() * 10) limit = num_peers() * 10;
|
|
|
|
m_download_bandwidth_limit = limit;
|
|
|
|
}
|
|
|
|
|
2004-03-21 03:03:37 +01:00
|
|
|
void torrent::pause()
|
|
|
|
{
|
2004-12-21 13:30:09 +01:00
|
|
|
if (m_paused) return;
|
2004-03-21 03:03:37 +01:00
|
|
|
disconnect_all();
|
|
|
|
m_paused = true;
|
2004-07-24 13:54:17 +02:00
|
|
|
// tell the tracker that we stopped
|
|
|
|
m_event = tracker_request::stopped;
|
|
|
|
m_just_paused = true;
|
2005-03-05 15:17:17 +01:00
|
|
|
// this will make the storage close all
|
|
|
|
// files and flush all cached data
|
2005-03-10 12:26:55 +01:00
|
|
|
if (m_storage.get()) m_storage->release_files();
|
2004-03-21 03:03:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::resume()
|
|
|
|
{
|
2004-12-21 13:30:09 +01:00
|
|
|
if (!m_paused) return;
|
2004-03-21 03:03:37 +01:00
|
|
|
m_paused = false;
|
2004-07-24 13:54:17 +02:00
|
|
|
|
|
|
|
// tell the tracker that we're back
|
|
|
|
m_event = tracker_request::started;
|
|
|
|
force_tracker_request();
|
|
|
|
|
2004-03-21 03:03:37 +01:00
|
|
|
// make pulse be called as soon as possible
|
|
|
|
m_time_scaler = 0;
|
|
|
|
}
|
|
|
|
|
2004-04-18 15:41:08 +02:00
|
|
|
void torrent::second_tick(stat& accumulator)
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2004-10-29 15:21:09 +02:00
|
|
|
m_connections_quota.used = (int)m_connections.size();
|
|
|
|
m_uploads_quota.used = m_policy->num_uploads();
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
m_ul_bandwidth_quota.used = 0;
|
|
|
|
m_ul_bandwidth_quota.max = 0;
|
|
|
|
m_ul_bandwidth_quota.min = 0;
|
|
|
|
|
|
|
|
m_dl_bandwidth_quota.used = 0;
|
|
|
|
m_dl_bandwidth_quota.min = 0;
|
|
|
|
m_dl_bandwidth_quota.max = 0;
|
2004-03-23 23:58:18 +01:00
|
|
|
|
2005-03-30 01:47:13 +02:00
|
|
|
if (m_paused)
|
|
|
|
{
|
|
|
|
// let the stats fade out to 0
|
|
|
|
m_stat.second_tick();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-01-13 04:08:59 +01:00
|
|
|
for (peer_iterator i = m_connections.begin();
|
2005-03-30 01:47:13 +02:00
|
|
|
i != m_connections.end(); ++i)
|
2003-12-07 06:53:04 +01:00
|
|
|
{
|
2004-01-13 04:08:59 +01:00
|
|
|
peer_connection* p = i->second;
|
2004-03-23 23:58:18 +01:00
|
|
|
m_stat += p->statistics();
|
2004-03-28 19:45:37 +02:00
|
|
|
// updates the peer connection's ul/dl bandwidth
|
|
|
|
// resource requests
|
2003-12-07 06:53:04 +01:00
|
|
|
p->second_tick();
|
2004-03-28 19:45:37 +02:00
|
|
|
|
|
|
|
m_ul_bandwidth_quota.used += p->m_ul_bandwidth_quota.used;
|
|
|
|
m_ul_bandwidth_quota.min += p->m_ul_bandwidth_quota.min;
|
|
|
|
m_dl_bandwidth_quota.used += p->m_dl_bandwidth_quota.used;
|
|
|
|
m_dl_bandwidth_quota.min += p->m_dl_bandwidth_quota.min;
|
|
|
|
|
|
|
|
m_ul_bandwidth_quota.max = saturated_add(
|
|
|
|
m_ul_bandwidth_quota.max
|
|
|
|
, p->m_ul_bandwidth_quota.max);
|
|
|
|
|
|
|
|
m_dl_bandwidth_quota.max = saturated_add(
|
|
|
|
m_dl_bandwidth_quota.max
|
|
|
|
, p->m_dl_bandwidth_quota.max);
|
2003-12-07 06:53:04 +01:00
|
|
|
}
|
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
m_ul_bandwidth_quota.max
|
|
|
|
= std::min(m_ul_bandwidth_quota.max, m_upload_bandwidth_limit);
|
|
|
|
|
|
|
|
m_dl_bandwidth_quota.max
|
|
|
|
= std::min(m_dl_bandwidth_quota.max, m_download_bandwidth_limit);
|
2004-03-23 23:58:18 +01:00
|
|
|
|
2004-04-18 15:41:08 +02:00
|
|
|
accumulator += m_stat;
|
2003-12-07 06:53:04 +01:00
|
|
|
m_stat.second_tick();
|
|
|
|
}
|
|
|
|
|
2004-03-23 23:58:18 +01:00
|
|
|
void torrent::distribute_resources()
|
|
|
|
{
|
2004-10-29 15:21:09 +02:00
|
|
|
m_time_scaler--;
|
|
|
|
if (m_time_scaler <= 0)
|
|
|
|
{
|
|
|
|
m_time_scaler = 10;
|
|
|
|
m_policy->pulse();
|
|
|
|
}
|
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
// distribute allowed upload among the peers
|
|
|
|
allocate_resources(m_ul_bandwidth_quota.given
|
|
|
|
, m_connections
|
|
|
|
, &peer_connection::m_ul_bandwidth_quota);
|
|
|
|
|
|
|
|
// distribute allowed download among the peers
|
|
|
|
allocate_resources(m_dl_bandwidth_quota.given
|
2004-03-23 23:58:18 +01:00
|
|
|
, m_connections
|
2004-03-28 19:45:37 +02:00
|
|
|
, &peer_connection::m_dl_bandwidth_quota);
|
2004-03-23 23:58:18 +01:00
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
using boost::bind;
|
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
// tell all peers to reset their used quota. This is
|
|
|
|
// a new second and they can again use up their quota
|
2004-10-29 15:21:09 +02:00
|
|
|
|
2005-03-07 16:39:06 +01:00
|
|
|
for (std::map<address, peer_connection*>::iterator i
|
|
|
|
= m_connections.begin(); i != m_connections.end(); ++i)
|
2004-03-23 23:58:18 +01:00
|
|
|
{
|
|
|
|
i->second->reset_upload_quota();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
bool torrent::verify_piece(int piece_index)
|
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(m_storage.get());
|
2004-01-25 13:37:15 +01:00
|
|
|
assert(piece_index >= 0);
|
|
|
|
assert(piece_index < m_torrent_file.num_pieces());
|
2005-02-23 09:57:54 +01:00
|
|
|
assert(piece_index < (int)m_have_pieces.size());
|
2004-01-25 13:37:15 +01:00
|
|
|
|
2004-03-07 21:50:56 +01:00
|
|
|
int size = static_cast<int>(m_torrent_file.piece_size(piece_index));
|
2003-12-07 06:53:04 +01:00
|
|
|
std::vector<char> buffer(size);
|
2003-12-17 17:37:20 +01:00
|
|
|
assert(size > 0);
|
2004-06-14 01:30:42 +02:00
|
|
|
m_storage->read(&buffer[0], piece_index, 0, size);
|
2003-12-07 06:53:04 +01:00
|
|
|
|
|
|
|
hasher h;
|
|
|
|
h.update(&buffer[0], size);
|
|
|
|
sha1_hash digest = h.final();
|
|
|
|
|
|
|
|
if (m_torrent_file.hash_for_piece(piece_index) != digest)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!m_have_pieces[piece_index])
|
|
|
|
m_num_pieces++;
|
|
|
|
m_have_pieces[piece_index] = true;
|
|
|
|
|
|
|
|
assert(std::accumulate(m_have_pieces.begin(), m_have_pieces.end(), 0)
|
|
|
|
== m_num_pieces);
|
|
|
|
return true;
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
2004-03-01 01:50:00 +01:00
|
|
|
const address& torrent::current_tracker() const
|
|
|
|
{
|
|
|
|
return m_tracker_address;
|
|
|
|
}
|
|
|
|
|
2003-10-31 05:02:51 +01:00
|
|
|
torrent_status torrent::status() const
|
2003-10-26 18:35:23 +01:00
|
|
|
{
|
2004-01-15 17:45:34 +01:00
|
|
|
assert(std::accumulate(
|
|
|
|
m_have_pieces.begin()
|
|
|
|
, m_have_pieces.end()
|
|
|
|
, 0) == m_num_pieces);
|
2003-10-30 00:28:09 +01:00
|
|
|
|
2004-01-15 17:45:34 +01:00
|
|
|
torrent_status st;
|
2003-10-30 00:28:09 +01:00
|
|
|
|
2004-08-11 19:22:58 +02:00
|
|
|
st.block_size = block_size();
|
2004-08-05 15:56:26 +02:00
|
|
|
st.num_peers = num_peers();
|
2005-02-23 21:38:29 +01:00
|
|
|
st.num_complete = m_complete;
|
|
|
|
st.num_incomplete = m_incomplete;
|
2004-03-21 03:03:37 +01:00
|
|
|
st.paused = m_paused;
|
2005-05-30 19:43:03 +02:00
|
|
|
boost::tie(st.total_done, st.total_wanted_done) = bytes_done();
|
2003-10-31 13:07:07 +01:00
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
// payload transfer
|
|
|
|
st.total_payload_download = m_stat.total_payload_download();
|
|
|
|
st.total_payload_upload = m_stat.total_payload_upload();
|
|
|
|
|
|
|
|
// total transfer
|
|
|
|
st.total_download = m_stat.total_payload_download()
|
|
|
|
+ m_stat.total_protocol_download();
|
|
|
|
st.total_upload = m_stat.total_payload_upload()
|
|
|
|
+ m_stat.total_protocol_upload();
|
|
|
|
|
2004-04-18 14:28:02 +02:00
|
|
|
// failed bytes
|
|
|
|
st.total_failed_bytes = m_total_failed_bytes;
|
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
// transfer rate
|
2003-12-07 06:53:04 +01:00
|
|
|
st.download_rate = m_stat.download_rate();
|
|
|
|
st.upload_rate = m_stat.upload_rate();
|
2004-04-18 14:28:02 +02:00
|
|
|
st.download_payload_rate = m_stat.download_payload_rate();
|
|
|
|
st.upload_payload_rate = m_stat.upload_payload_rate();
|
2003-12-22 08:14:35 +01:00
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
st.next_announce = next_announce()
|
2004-10-14 03:17:04 +02:00
|
|
|
- second_clock::universal_time();
|
2004-03-21 03:03:37 +01:00
|
|
|
if (st.next_announce.is_negative()) st.next_announce
|
|
|
|
= boost::posix_time::seconds(0);
|
2004-01-17 21:04:19 +01:00
|
|
|
st.announce_interval = boost::posix_time::seconds(m_duration);
|
|
|
|
|
2005-03-11 18:21:56 +01:00
|
|
|
if (m_last_working_tracker >= 0)
|
|
|
|
{
|
|
|
|
st.current_tracker
|
|
|
|
= m_trackers[m_last_working_tracker].url;
|
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
// if we don't have any metadata, stop here
|
|
|
|
|
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
|
|
|
if (m_got_tracker_response == false)
|
|
|
|
st.state = torrent_status::connecting_to_tracker;
|
|
|
|
else
|
|
|
|
st.state = torrent_status::downloading_metadata;
|
2004-09-16 03:14:16 +02:00
|
|
|
|
2005-06-15 14:54:35 +02:00
|
|
|
if (m_metadata_size == 0) st.progress = 0.f;
|
|
|
|
else st.progress = std::min(1.f, m_metadata_progress / (float)m_metadata_size);
|
2004-09-16 03:14:16 +02:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
return st;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fill in status that depends on metadata
|
|
|
|
|
2005-05-30 19:43:03 +02:00
|
|
|
st.total_wanted = m_torrent_file.total_size();
|
|
|
|
|
|
|
|
if (m_picker.get() && (m_picker->num_filtered() > 0
|
|
|
|
|| m_picker->num_have_filtered() > 0))
|
|
|
|
{
|
|
|
|
int filtered_pieces = m_picker->num_filtered()
|
|
|
|
+ m_picker->num_have_filtered();
|
|
|
|
int last_piece_index = m_torrent_file.num_pieces() - 1;
|
|
|
|
if (m_picker->is_filtered(last_piece_index))
|
|
|
|
{
|
|
|
|
st.total_wanted -= m_torrent_file.piece_size(last_piece_index);
|
|
|
|
--filtered_pieces;
|
|
|
|
}
|
|
|
|
|
|
|
|
st.total_wanted -= filtered_pieces * m_torrent_file.piece_length();
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(st.total_wanted >= st.total_wanted_done);
|
|
|
|
|
|
|
|
st.progress = st.total_wanted_done
|
|
|
|
/ static_cast<float>(st.total_wanted);
|
2004-01-15 02:01:09 +01:00
|
|
|
|
2004-01-15 17:45:34 +01:00
|
|
|
st.pieces = &m_have_pieces;
|
2003-11-20 20:58:29 +01:00
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
if (m_got_tracker_response == false)
|
|
|
|
st.state = torrent_status::connecting_to_tracker;
|
2004-01-26 11:29:00 +01:00
|
|
|
else if (m_num_pieces == (int)m_have_pieces.size())
|
2003-10-31 05:02:51 +01:00
|
|
|
st.state = torrent_status::seeding;
|
2005-05-30 19:43:03 +02:00
|
|
|
else if (st.total_wanted_done == st.total_wanted)
|
|
|
|
st.state = torrent_status::finished;
|
2003-10-31 05:02:51 +01:00
|
|
|
else
|
|
|
|
st.state = torrent_status::downloading;
|
|
|
|
|
2004-08-05 15:56:26 +02:00
|
|
|
st.num_seeds = num_seeds();
|
|
|
|
st.distributed_copies = m_picker->distributed_copies();
|
2003-10-31 05:02:51 +01:00
|
|
|
return st;
|
2003-10-26 18:35:23 +01:00
|
|
|
}
|
|
|
|
|
2004-08-05 15:56:26 +02:00
|
|
|
int torrent::num_seeds() const
|
|
|
|
{
|
2004-10-18 00:23:08 +02:00
|
|
|
return (int)std::count_if(m_connections.begin(), m_connections.end(),
|
2004-08-05 15:56:26 +02:00
|
|
|
boost::bind(&peer_connection::is_seed,
|
|
|
|
boost::bind(&std::map<address,peer_connection*>::value_type::second, _1)));
|
|
|
|
}
|
|
|
|
|
2004-09-12 15:53:00 +02:00
|
|
|
int div_round_up(int numerator, int denominator)
|
|
|
|
{
|
|
|
|
return (numerator + denominator - 1) / denominator;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<int, int> req_to_offset(std::pair<int, int> req, int total_size)
|
|
|
|
{
|
|
|
|
assert(req.first >= 0);
|
|
|
|
assert(req.second > 0);
|
|
|
|
assert(req.second <= 256);
|
|
|
|
assert(req.first + req.second <= 256);
|
|
|
|
|
|
|
|
int start = div_round_up(req.first * total_size, 256);
|
|
|
|
int size = div_round_up((req.first + req.second) * total_size, 256) - start;
|
|
|
|
return std::make_pair(start, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<int, int> offset_to_req(std::pair<int, int> offset, int total_size)
|
|
|
|
{
|
|
|
|
int start = offset.first * 256 / total_size;
|
|
|
|
int size = (offset.first + offset.second) * 256 / total_size - start;
|
|
|
|
|
|
|
|
std::pair<int, int> ret(start, size);
|
|
|
|
|
|
|
|
assert(start >= 0);
|
|
|
|
assert(size > 0);
|
|
|
|
assert(start <= 256);
|
|
|
|
assert(start + size <= 256);
|
|
|
|
|
|
|
|
// assert the identity of this function
|
|
|
|
#ifndef NDEBUG
|
|
|
|
std::pair<int, int> identity = req_to_offset(ret, total_size);
|
|
|
|
assert(offset == identity);
|
|
|
|
#endif
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
bool torrent::received_metadata(char const* buf, int size, int offset, int total_size)
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
if (valid_metadata()) return false;
|
|
|
|
|
|
|
|
if ((int)m_metadata.size() < total_size)
|
|
|
|
m_metadata.resize(total_size);
|
|
|
|
|
|
|
|
std::copy(
|
|
|
|
buf
|
|
|
|
, buf + size
|
|
|
|
, &m_metadata[offset]);
|
|
|
|
|
|
|
|
if (m_have_metadata.empty())
|
2004-09-12 15:53:00 +02:00
|
|
|
m_have_metadata.resize(256, false);
|
2004-06-14 01:30:42 +02:00
|
|
|
|
2004-11-30 12:17:32 +01:00
|
|
|
std::pair<int, int> req = offset_to_req(std::make_pair(offset, size)
|
|
|
|
, total_size);
|
|
|
|
|
2005-02-23 09:57:54 +01:00
|
|
|
assert(req.first + req.second <= (int)m_have_metadata.size());
|
2004-06-14 01:30:42 +02:00
|
|
|
|
|
|
|
std::fill(
|
2004-09-12 15:53:00 +02:00
|
|
|
m_have_metadata.begin() + req.first
|
|
|
|
, m_have_metadata.begin() + req.first + req.second
|
2004-06-14 01:30:42 +02:00
|
|
|
, true);
|
|
|
|
|
2004-08-08 23:26:40 +02:00
|
|
|
bool have_all = std::count(
|
|
|
|
m_have_metadata.begin()
|
|
|
|
, m_have_metadata.end()
|
2004-09-12 15:53:00 +02:00
|
|
|
, true) == 256;
|
2004-08-08 23:26:40 +02:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
if (!have_all) return false;
|
|
|
|
|
|
|
|
hasher h;
|
2004-07-01 20:51:13 +02:00
|
|
|
h.update(&m_metadata[0], (int)m_metadata.size());
|
2004-06-14 01:30:42 +02:00
|
|
|
sha1_hash info_hash = h.final();
|
|
|
|
|
|
|
|
if (info_hash != m_torrent_file.info_hash())
|
|
|
|
{
|
|
|
|
std::fill(
|
|
|
|
m_have_metadata.begin()
|
2004-11-30 12:17:32 +01:00
|
|
|
, m_have_metadata.begin() + req.first + req.second
|
2004-06-14 01:30:42 +02:00
|
|
|
, false);
|
2005-06-15 14:54:35 +02:00
|
|
|
m_metadata_progress = 0;
|
|
|
|
m_metadata_size = 0;
|
2004-06-14 01:30:42 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_torrent_file.parse_info_section(bdecode(m_metadata.begin(), m_metadata.end()));
|
|
|
|
|
|
|
|
init();
|
|
|
|
|
|
|
|
boost::mutex m;
|
|
|
|
detail::piece_checker_data d;
|
|
|
|
d.abort = false;
|
|
|
|
// TODO: this check should be moved to the checker thread
|
2004-09-10 02:47:30 +02:00
|
|
|
// not really a high priority, since no files would usually
|
|
|
|
// be available if the metadata wasn't available.
|
2005-05-29 19:25:13 +02:00
|
|
|
check_files(d, m, false);
|
2004-06-14 01:30:42 +02:00
|
|
|
|
2004-09-10 02:47:30 +02:00
|
|
|
if (m_ses.m_alerts.should_post(alert::info))
|
|
|
|
{
|
|
|
|
m_ses.m_alerts.post_alert(metadata_received_alert(
|
|
|
|
get_handle(), "metadata successfully received from swarm"));
|
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
// all peer connections have to initialize themselves now that the metadata
|
|
|
|
// is available
|
2004-09-12 15:53:00 +02:00
|
|
|
typedef std::map<address, peer_connection*> conn_map;
|
|
|
|
for (conn_map::iterator i = m_connections.begin()
|
|
|
|
, end(m_connections.end()); i != end; ++i)
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
|
|
|
i->second->init();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
m_picker->integrity_check(this);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// clear the storage for the bitfield
|
2004-08-08 23:26:40 +02:00
|
|
|
std::vector<bool>().swap(m_have_metadata);
|
|
|
|
std::vector<int>().swap(m_requested_metadata);
|
2004-06-14 01:30:42 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2004-08-08 23:26:40 +02:00
|
|
|
std::pair<int, int> torrent::metadata_request()
|
|
|
|
{
|
2004-09-12 15:53:00 +02:00
|
|
|
// count the number of peers that supports the
|
|
|
|
// extension and that has metadata
|
|
|
|
int peers = 0;
|
|
|
|
typedef std::map<address, peer_connection*> conn_map;
|
|
|
|
for (conn_map::iterator i = m_connections.begin()
|
|
|
|
, end(m_connections.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
if (!i->second->supports_extension(
|
|
|
|
peer_connection::extended_metadata_message))
|
|
|
|
continue;
|
|
|
|
if (!i->second->has_metadata())
|
|
|
|
continue;
|
|
|
|
++peers;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the number of blocks to request
|
|
|
|
int num_blocks = 256 / (peers + 1);
|
|
|
|
if (num_blocks < 1) num_blocks = 1;
|
|
|
|
assert(num_blocks <= 128);
|
|
|
|
|
|
|
|
int min_element = std::numeric_limits<int>::max();
|
|
|
|
int best_index = 0;
|
|
|
|
for (int i = 0; i < 256 - num_blocks + 1; ++i)
|
|
|
|
{
|
|
|
|
int min = *std::min_element(m_requested_metadata.begin() + i
|
|
|
|
, m_requested_metadata.begin() + i + num_blocks);
|
|
|
|
min += std::accumulate(m_requested_metadata.begin() + i
|
|
|
|
, m_requested_metadata.begin() + i + num_blocks, (int)0);
|
|
|
|
|
|
|
|
if (min_element > min)
|
|
|
|
{
|
|
|
|
best_index = i;
|
|
|
|
min_element = min;
|
|
|
|
}
|
|
|
|
}
|
2004-08-08 23:26:40 +02:00
|
|
|
|
2004-09-12 15:53:00 +02:00
|
|
|
std::pair<int, int> ret(best_index, num_blocks);
|
2004-08-08 23:26:40 +02:00
|
|
|
for (int i = ret.first; i < ret.first + ret.second; ++i)
|
|
|
|
m_requested_metadata[i]++;
|
2004-09-12 15:53:00 +02:00
|
|
|
|
|
|
|
assert(ret.first >= 0);
|
|
|
|
assert(ret.second > 0);
|
|
|
|
assert(ret.second <= 256);
|
|
|
|
assert(ret.first + ret.second <= 256);
|
|
|
|
|
2004-08-08 23:26:40 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2004-09-12 15:53:00 +02:00
|
|
|
void torrent::cancel_metadata_request(std::pair<int, int> req)
|
|
|
|
{
|
|
|
|
for (int i = req.first; i < req.first + req.second; ++i)
|
|
|
|
{
|
|
|
|
assert(m_requested_metadata[i] > 0);
|
2005-03-24 13:13:47 +01:00
|
|
|
if (m_requested_metadata[i] > 0)
|
|
|
|
--m_requested_metadata[i];
|
2004-09-12 15:53:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-24 13:13:47 +01:00
|
|
|
void torrent::tracker_request_timed_out(
|
|
|
|
tracker_request const&)
|
2003-12-22 08:14:35 +01:00
|
|
|
{
|
2005-03-19 13:22:40 +01:00
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
2003-12-22 08:14:35 +01:00
|
|
|
debug_log("*** tracker timed out");
|
|
|
|
#endif
|
|
|
|
if (m_ses.m_alerts.should_post(alert::warning))
|
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << "tracker: \""
|
2004-09-12 12:12:16 +02:00
|
|
|
<< m_trackers[m_currently_trying_tracker].url
|
2003-12-22 08:14:35 +01:00
|
|
|
<< "\" timed out";
|
2004-09-12 12:12:16 +02:00
|
|
|
m_ses.m_alerts.post_alert(tracker_alert(get_handle()
|
2005-04-21 01:00:27 +02:00
|
|
|
, m_failed_trackers + 1, 0, s.str()));
|
2003-12-22 08:14:35 +01:00
|
|
|
}
|
|
|
|
try_next_tracker();
|
|
|
|
}
|
|
|
|
|
2004-01-26 11:29:00 +01:00
|
|
|
// TODO: with some response codes, we should just consider
|
2003-12-22 08:14:35 +01:00
|
|
|
// the tracker as a failure and not retry
|
|
|
|
// it anymore
|
2005-04-24 02:50:52 +02:00
|
|
|
void torrent::tracker_request_error(tracker_request const&
|
2005-03-24 13:13:47 +01:00
|
|
|
, int response_code, const std::string& str)
|
2003-12-22 08:14:35 +01:00
|
|
|
{
|
2005-03-19 13:22:40 +01:00
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
2003-12-22 08:14:35 +01:00
|
|
|
debug_log(std::string("*** tracker error: ") + str);
|
|
|
|
#endif
|
|
|
|
if (m_ses.m_alerts.should_post(alert::warning))
|
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << "tracker: \""
|
2004-09-12 12:12:16 +02:00
|
|
|
<< m_trackers[m_currently_trying_tracker].url
|
2003-12-22 08:14:35 +01:00
|
|
|
<< "\" " << str;
|
2004-09-12 12:12:16 +02:00
|
|
|
m_ses.m_alerts.post_alert(tracker_alert(get_handle()
|
2005-04-21 01:00:27 +02:00
|
|
|
, m_failed_trackers + 1, response_code, s.str()));
|
2003-12-22 08:14:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try_next_tracker();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-19 13:22:40 +01:00
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
2003-11-20 20:58:29 +01:00
|
|
|
void torrent::debug_log(const std::string& line)
|
|
|
|
{
|
2003-12-07 06:53:04 +01:00
|
|
|
(*m_ses.m_logger) << line << "\n";
|
2003-11-20 20:58:29 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2003-10-24 04:18:11 +02:00
|
|
|
}
|
|
|
|
|
2005-06-15 14:54:35 +02:00
|
|
|
void torrent::metadata_progress(int total_size, int received)
|
|
|
|
{
|
|
|
|
m_metadata_progress += received;
|
|
|
|
m_metadata_size = total_size;
|
|
|
|
}
|
|
|
|
|