2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
2016-01-18 00:57:46 +01:00
|
|
|
Copyright (c) 2003-2016, Arvid Norberg
|
2003-10-23 01:00:57 +02:00
|
|
|
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.
|
|
|
|
|
|
|
|
*/
|
2007-03-17 18:15:16 +01:00
|
|
|
|
2015-04-18 04:33:39 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
#include <cstdarg> // for va_list
|
2003-10-23 01:00:57 +02:00
|
|
|
#include <ctime>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
2015-07-06 01:32:13 +02:00
|
|
|
#include <map>
|
|
|
|
#include <vector>
|
2003-10-23 01:00:57 +02:00
|
|
|
#include <cctype>
|
2003-10-30 00:28:09 +01:00
|
|
|
#include <numeric>
|
2014-12-01 11:43:34 +01:00
|
|
|
#include <limits> // for numeric_limits
|
2016-05-17 15:24:06 +02:00
|
|
|
#include <cstdio> // for snprintf
|
2016-05-25 06:31:52 +02:00
|
|
|
#include <functional>
|
|
|
|
|
|
|
|
#include "libtorrent/aux_/disable_warnings_push.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2015-04-18 04:33:39 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
|
|
|
#include "libtorrent/ssl_stream.hpp"
|
|
|
|
#include <boost/asio/ssl/context.hpp>
|
|
|
|
#include <boost/asio/ssl/verify_context.hpp>
|
|
|
|
#endif // TORRENT_USE_OPENSSL
|
|
|
|
|
2015-04-21 03:16:28 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
2004-01-25 19:18:36 +01:00
|
|
|
|
2003-10-26 18:35:23 +01:00
|
|
|
#include "libtorrent/torrent_handle.hpp"
|
2015-09-18 06:23:45 +02:00
|
|
|
#include "libtorrent/announce_entry.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/torrent_info.hpp"
|
2004-01-31 11:46:15 +01:00
|
|
|
#include "libtorrent/tracker_manager.hpp"
|
2008-05-17 16:19:34 +02:00
|
|
|
#include "libtorrent/parse_url.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"
|
2009-12-25 17:52:57 +01:00
|
|
|
#include "libtorrent/peer_connection.hpp"
|
2006-04-25 23:04:48 +02:00
|
|
|
#include "libtorrent/bt_peer_connection.hpp"
|
|
|
|
#include "libtorrent/web_peer_connection.hpp"
|
2008-12-30 04:54:07 +01:00
|
|
|
#include "libtorrent/http_seed_connection.hpp"
|
2015-07-25 18:15:24 +02:00
|
|
|
#include "libtorrent/peer_connection_handle.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/peer_id.hpp"
|
2003-12-22 08:14:35 +01:00
|
|
|
#include "libtorrent/identify_client.hpp"
|
2004-01-18 20:12:18 +01:00
|
|
|
#include "libtorrent/alert_types.hpp"
|
2006-11-14 01:08:16 +01:00
|
|
|
#include "libtorrent/extensions.hpp"
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/aux_/session_interface.hpp"
|
2007-04-25 20:26:35 +02:00
|
|
|
#include "libtorrent/instantiate_connection.hpp"
|
2007-09-10 08:12:41 +02:00
|
|
|
#include "libtorrent/assert.hpp"
|
2008-12-26 08:00:21 +01:00
|
|
|
#include "libtorrent/broadcast_socket.hpp"
|
2009-05-03 22:21:24 +02:00
|
|
|
#include "libtorrent/kademlia/dht_tracker.hpp"
|
2009-11-26 06:45:43 +01:00
|
|
|
#include "libtorrent/peer_info.hpp"
|
2010-12-30 02:47:30 +01:00
|
|
|
#include "libtorrent/http_connection.hpp"
|
2011-02-26 08:55:51 +01:00
|
|
|
#include "libtorrent/random.hpp"
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/peer_class.hpp" // for peer_class
|
|
|
|
#include "libtorrent/socket_io.hpp" // for read_*_endpoint
|
|
|
|
#include "libtorrent/ip_filter.hpp"
|
|
|
|
#include "libtorrent/request_blocks.hpp"
|
|
|
|
#include "libtorrent/performance_counters.hpp" // for counters
|
|
|
|
#include "libtorrent/resolver_interface.hpp"
|
2014-04-22 06:21:14 +02:00
|
|
|
#include "libtorrent/alloca.hpp"
|
2015-03-21 01:12:40 +01:00
|
|
|
#include "libtorrent/resolve_links.hpp"
|
2015-06-29 04:47:11 +02:00
|
|
|
#include "libtorrent/aux_/file_progress.hpp"
|
2016-09-24 17:46:56 +02:00
|
|
|
#include "libtorrent/aux_/has_block.hpp"
|
2015-07-06 03:41:53 +02:00
|
|
|
#include "libtorrent/alert_manager.hpp"
|
|
|
|
#include "libtorrent/disk_interface.hpp"
|
2016-05-05 23:09:11 +02:00
|
|
|
#include "libtorrent/broadcast_socket.hpp" // for is_ip_address
|
2016-06-04 16:01:43 +02:00
|
|
|
#include "libtorrent/hex.hpp" // to_hex
|
2015-07-06 03:41:53 +02:00
|
|
|
// TODO: factor out cache_status to its own header
|
|
|
|
#include "libtorrent/disk_io_thread.hpp" // for cache_status
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/aux_/session_impl.hpp" // for tracker_logger
|
|
|
|
#endif
|
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
using namespace std::placeholders;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
2015-04-20 06:52:49 +02:00
|
|
|
namespace {
|
|
|
|
|
2010-02-14 02:39:55 +01:00
|
|
|
int root2(int x)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
x >>= 1;
|
|
|
|
while (x > 0)
|
|
|
|
{
|
|
|
|
// if this assert triggers, the block size
|
|
|
|
// is not an even 2 exponent!
|
|
|
|
TORRENT_ASSERT(x == 1 || (x & 1) == 0);
|
|
|
|
++ret;
|
|
|
|
x >>= 1;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-12-24 02:00:24 +01:00
|
|
|
constexpr int default_piece_priority = 4;
|
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2014-12-31 23:05:34 +01:00
|
|
|
web_seed_t::web_seed_t(web_seed_entry const& wse)
|
|
|
|
: web_seed_entry(wse)
|
|
|
|
{
|
|
|
|
peer_info.web_seed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
web_seed_t::web_seed_t(std::string const& url_, web_seed_entry::type_t type_
|
|
|
|
, std::string const& auth_
|
|
|
|
, web_seed_entry::headers_t const& extra_headers_)
|
|
|
|
: web_seed_entry(url_, type_, auth_, extra_headers_)
|
|
|
|
{
|
|
|
|
peer_info.web_seed = true;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
torrent_hot_members::torrent_hot_members(aux::session_interface& ses
|
2016-06-03 13:32:48 +02:00
|
|
|
, add_torrent_params const& p, int const block_size
|
|
|
|
, bool const session_paused)
|
2014-07-06 21:18:00 +02:00
|
|
|
: m_ses(ses)
|
|
|
|
, m_complete(0xffffff)
|
2015-11-20 05:37:45 +01:00
|
|
|
, m_upload_mode((p.flags & add_torrent_params::flag_upload_mode) != 0)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_connections_initialized(false)
|
|
|
|
, m_abort(false)
|
2016-06-01 07:05:32 +02:00
|
|
|
, m_paused((p.flags & add_torrent_params::flag_paused) != 0)
|
2016-06-03 13:32:48 +02:00
|
|
|
, m_session_paused(session_paused)
|
2015-11-20 05:37:45 +01:00
|
|
|
, m_share_mode((p.flags & add_torrent_params::flag_share_mode) != 0)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_have_all(false)
|
|
|
|
, m_graceful_pause_mode(false)
|
2015-11-20 05:37:45 +01:00
|
|
|
, m_state_subscription((p.flags & add_torrent_params::flag_update_subscribe) != 0)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_max_connections(0xffffff)
|
|
|
|
, m_block_size_shift(root2(block_size))
|
|
|
|
, m_state(torrent_status::checking_resume_data)
|
|
|
|
{}
|
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
torrent::torrent(
|
2014-07-06 21:18:00 +02:00
|
|
|
aux::session_interface& ses
|
2016-06-03 13:32:48 +02:00
|
|
|
, int const block_size
|
|
|
|
, int const seq
|
|
|
|
, bool const session_paused
|
2010-12-30 02:47:30 +01:00
|
|
|
, add_torrent_params const& p
|
|
|
|
, sha1_hash const& info_hash)
|
2016-06-03 13:32:48 +02:00
|
|
|
: torrent_hot_members(ses, p, block_size, session_paused)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_tracker_timer(ses.get_io_service())
|
2014-10-06 00:30:09 +02:00
|
|
|
, m_inactivity_timer(ses.get_io_service())
|
2010-11-18 06:51:52 +01:00
|
|
|
, m_trackerid(p.trackerid)
|
2010-12-30 02:47:30 +01:00
|
|
|
, m_save_path(complete(p.save_path))
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2010-12-30 02:47:30 +01:00
|
|
|
, m_url(p.url)
|
2011-01-18 04:41:54 +01:00
|
|
|
, m_uuid(p.uuid)
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2014-08-01 09:32:54 +02:00
|
|
|
, m_stats_counters(ses.stats_counters())
|
2009-02-03 08:46:24 +01:00
|
|
|
, m_storage_constructor(p.storage)
|
2016-07-09 22:26:26 +02:00
|
|
|
, m_added_time(time(nullptr))
|
2014-11-12 17:57:59 +01:00
|
|
|
, m_info_hash(info_hash)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_last_saved_resume(ses.session_time())
|
|
|
|
, m_started(ses.session_time())
|
2015-11-03 06:12:30 +01:00
|
|
|
, m_error_file(torrent_status::error_file_none)
|
2011-12-23 20:30:23 +01:00
|
|
|
, m_sequence_number(seq)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_announce_to_trackers((p.flags & add_torrent_params::flag_paused) == 0)
|
|
|
|
, m_announce_to_lsd((p.flags & add_torrent_params::flag_paused) == 0)
|
|
|
|
, m_has_incoming(false)
|
|
|
|
, m_files_checked(false)
|
2010-02-14 02:39:55 +01:00
|
|
|
, m_storage_mode(p.storage_mode)
|
|
|
|
, m_announcing(false)
|
2016-10-19 07:18:05 +02:00
|
|
|
, m_added(false)
|
2010-02-14 02:39:55 +01:00
|
|
|
, m_active_time(0)
|
|
|
|
, m_finished_time(0)
|
2008-05-19 04:52:32 +02:00
|
|
|
, m_sequential_download(false)
|
2016-02-07 08:37:32 +01:00
|
|
|
, m_auto_sequential(false)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_seed_mode(false)
|
2008-12-08 07:36:22 +01:00
|
|
|
, m_super_seeding(false)
|
2016-02-08 23:45:56 +01:00
|
|
|
, m_stop_when_ready((p.flags & add_torrent_params::flag_stop_when_ready) != 0)
|
2016-04-17 22:56:07 +02:00
|
|
|
, m_need_save_resume_data((p.flags & add_torrent_params::flag_need_save_resume) != 0)
|
2010-02-14 02:39:55 +01:00
|
|
|
, m_seeding_time(0)
|
2012-02-18 09:23:48 +01:00
|
|
|
, m_max_uploads((1<<24)-1)
|
2010-02-14 02:39:55 +01:00
|
|
|
, m_num_uploads(0)
|
2013-11-26 08:47:48 +01:00
|
|
|
, m_need_connect_boost(true)
|
|
|
|
, m_lsd_seq(0)
|
|
|
|
, m_magnet_link(false)
|
2015-11-20 05:37:45 +01:00
|
|
|
, m_apply_ip_filter((p.flags & add_torrent_params::flag_apply_ip_filter) != 0)
|
2016-02-16 07:43:06 +01:00
|
|
|
, m_pending_active_change(false)
|
2010-02-14 02:39:55 +01:00
|
|
|
, m_padding(0)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_incomplete(0xffffff)
|
|
|
|
, m_announce_to_dht((p.flags & add_torrent_params::flag_paused) == 0)
|
2013-11-26 08:47:48 +01:00
|
|
|
, m_in_state_updates(false)
|
|
|
|
, m_is_active_download(false)
|
|
|
|
, m_is_active_finished(false)
|
|
|
|
, m_ssl_torrent(false)
|
|
|
|
, m_deleted(false)
|
2016-04-17 22:56:07 +02:00
|
|
|
, m_auto_managed((p.flags & add_torrent_params::flag_auto_managed) != 0)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_current_gauge_state(no_gauge_state)
|
|
|
|
, m_moving_storage(false)
|
|
|
|
, m_inactive(false)
|
2013-02-04 08:17:31 +01:00
|
|
|
, m_downloaded(0xffffff)
|
2013-11-26 08:47:48 +01:00
|
|
|
, m_progress_ppm(0)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-06-21 00:17:49 +02:00
|
|
|
// we cannot log in the constructor, because it relies on shared_from_this
|
|
|
|
// being initialized, which happens after the constructor returns.
|
|
|
|
|
2016-02-12 23:50:29 +01:00
|
|
|
// TODO: 3 we could probably get away with just saving a few fields here
|
2016-02-13 22:41:38 +01:00
|
|
|
// TODO: 2 p should probably be moved in here
|
2016-02-12 23:50:29 +01:00
|
|
|
m_add_torrent_params.reset(new add_torrent_params(p));
|
|
|
|
|
2012-04-28 23:13:55 +02:00
|
|
|
#if TORRENT_USE_UNC_PATHS
|
|
|
|
m_save_path = canonicalize_path(m_save_path);
|
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_apply_ip_filter)
|
|
|
|
{
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::non_filter_torrents);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2012-06-21 05:51:39 +02:00
|
|
|
|
2011-02-26 08:48:05 +01:00
|
|
|
if (!p.ti || !p.ti->is_valid())
|
|
|
|
{
|
|
|
|
// we don't have metadata for this torrent. We'll download
|
|
|
|
// it either through the URL passed in, or through a metadata
|
|
|
|
// extension. Make sure that when we save resume data for this
|
|
|
|
// torrent, we also save the metadata
|
|
|
|
m_magnet_link = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_torrent_file)
|
2016-08-17 23:26:35 +02:00
|
|
|
m_torrent_file = (p.ti ? p.ti : std::make_shared<torrent_info>(info_hash));
|
2011-02-26 08:48:05 +01:00
|
|
|
|
2016-02-11 19:58:18 +01:00
|
|
|
// --- WEB SEEDS ---
|
|
|
|
|
|
|
|
// if override web seed flag is set, don't load any web seeds from the
|
|
|
|
// torrent file.
|
|
|
|
if ((p.flags & add_torrent_params::flag_override_web_seeds) == 0)
|
|
|
|
{
|
|
|
|
std::vector<web_seed_entry> const& web_seeds = m_torrent_file->web_seeds();
|
|
|
|
m_web_seeds.insert(m_web_seeds.end(), web_seeds.begin(), web_seeds.end());
|
|
|
|
}
|
2015-09-29 07:51:01 +02:00
|
|
|
|
2014-04-15 11:31:28 +02:00
|
|
|
// add web seeds from add_torrent_params
|
2016-02-11 19:58:18 +01:00
|
|
|
bool const multi_file = m_torrent_file->is_valid()
|
|
|
|
&& m_torrent_file->num_files() > 1;
|
|
|
|
|
2016-06-03 13:32:48 +02:00
|
|
|
for (auto const& u : p.url_seeds)
|
2014-04-15 11:31:28 +02:00
|
|
|
{
|
2016-06-03 13:32:48 +02:00
|
|
|
m_web_seeds.push_back(web_seed_t(u, web_seed_entry::url_seed));
|
2014-04-15 11:31:28 +02:00
|
|
|
|
2016-02-11 19:58:18 +01:00
|
|
|
// correct URLs to end with a "/" for multi-file torrents
|
|
|
|
std::string& url = m_web_seeds.back().url;
|
|
|
|
if (multi_file && url[url.size()-1] != '/') url += '/';
|
2011-02-26 08:48:05 +01:00
|
|
|
}
|
2016-06-03 13:32:48 +02:00
|
|
|
|
|
|
|
for (auto const& e : p.http_seeds)
|
2011-02-26 08:48:05 +01:00
|
|
|
{
|
2016-06-03 13:32:48 +02:00
|
|
|
m_web_seeds.push_back(web_seed_t(e, web_seed_entry::http_seed));
|
2011-02-26 08:48:05 +01:00
|
|
|
}
|
|
|
|
|
2016-02-11 19:58:18 +01:00
|
|
|
// --- TRACKERS ---
|
2011-01-18 04:41:54 +01:00
|
|
|
|
2016-02-11 19:58:18 +01:00
|
|
|
// if override trackers flag is set, don't load trackers from torrent file
|
|
|
|
if ((p.flags & add_torrent_params::flag_override_trackers) == 0)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-02-11 19:58:18 +01:00
|
|
|
m_trackers = m_torrent_file->trackers();
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2009-02-03 08:46:24 +01:00
|
|
|
|
2015-12-15 06:03:43 +01:00
|
|
|
int tier = 0;
|
2016-06-03 13:32:48 +02:00
|
|
|
auto tier_iter = p.tracker_tiers.begin();
|
2016-10-08 20:07:11 +02:00
|
|
|
for (auto const& url : p.trackers)
|
2015-12-15 06:03:43 +01:00
|
|
|
{
|
2016-10-08 20:07:11 +02:00
|
|
|
announce_entry e(url);
|
2015-12-15 06:03:43 +01:00
|
|
|
if (tier_iter != p.tracker_tiers.end())
|
|
|
|
tier = *tier_iter++;
|
|
|
|
|
|
|
|
e.fail_limit = 0;
|
|
|
|
e.source = announce_entry::source_magnet_link;
|
2016-11-21 16:08:26 +01:00
|
|
|
e.tier = std::uint8_t(tier);
|
2016-10-07 21:36:40 +02:00
|
|
|
if (!find_tracker(e.url))
|
|
|
|
{
|
|
|
|
m_trackers.push_back(e);
|
|
|
|
}
|
2015-12-15 06:03:43 +01:00
|
|
|
}
|
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
std::sort(m_trackers.begin(), m_trackers.end()
|
|
|
|
, [] (announce_entry const& lhs, announce_entry const& rhs)
|
|
|
|
{ return lhs.tier < rhs.tier; });
|
2016-02-12 21:42:39 +01:00
|
|
|
|
|
|
|
if (settings().get_bool(settings_pack::prefer_udp_trackers))
|
|
|
|
prioritize_udp_trackers();
|
|
|
|
|
2016-02-13 22:41:38 +01:00
|
|
|
// --- MERKLE TREE ---
|
|
|
|
|
|
|
|
if (m_torrent_file->is_valid()
|
|
|
|
&& m_torrent_file->is_merkle_torrent())
|
|
|
|
{
|
|
|
|
if (p.merkle_tree.size() == m_torrent_file->merkle_tree().size())
|
|
|
|
{
|
|
|
|
// TODO: 2 set_merkle_tree should probably take the vector as &&
|
|
|
|
std::vector<sha1_hash> tree(p.merkle_tree);
|
|
|
|
m_torrent_file->set_merkle_tree(tree);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: 0 if this is a merkle torrent and we can't
|
|
|
|
// restore the tree, we need to wipe all the
|
|
|
|
// bits in the have array, but not necessarily
|
|
|
|
// we might want to do a full check to see if we have
|
|
|
|
// all the pieces. This is low priority since almost
|
|
|
|
// no one uses merkle torrents
|
2016-05-02 18:36:21 +02:00
|
|
|
TORRENT_ASSERT_FAIL();
|
2016-02-13 22:41:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-11 19:58:18 +01:00
|
|
|
if (m_torrent_file->is_valid())
|
|
|
|
{
|
2016-02-16 07:43:06 +01:00
|
|
|
// setting file- or piece priorities for seed mode makes no sense. If a
|
|
|
|
// torrent ends up in seed mode by accident, it can be very confusing,
|
|
|
|
// so assume the seed mode flag is not intended and don't enable it in
|
|
|
|
// that case. Also, if the resume data says we're missing a piece, we
|
|
|
|
// can't be in seed-mode.
|
|
|
|
m_seed_mode = (p.flags & add_torrent_params::flag_seed_mode) != 0
|
2016-09-22 01:51:51 +02:00
|
|
|
&& std::find(p.file_priorities.begin(), p.file_priorities.end(), 0) == p.file_priorities.end()
|
|
|
|
&& std::find(p.piece_priorities.begin(), p.piece_priorities.end(), 0) == p.piece_priorities.end()
|
|
|
|
&& std::find(p.have_pieces.begin(), p.have_pieces.end(), false) == p.have_pieces.end();
|
2016-02-16 07:43:06 +01:00
|
|
|
|
2016-02-11 19:58:18 +01:00
|
|
|
m_connections_initialized = true;
|
|
|
|
m_block_size_shift = root2((std::min)(block_size, m_torrent_file->piece_length()));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!p.name.empty()) m_name.reset(new std::string(p.name));
|
|
|
|
}
|
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2016-02-11 19:58:18 +01:00
|
|
|
if (!m_url.empty() && m_uuid.empty()) m_uuid = m_url;
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2016-02-11 19:58:18 +01:00
|
|
|
|
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-12-22 16:42:33 +01:00
|
|
|
m_file_priority.assign(p.file_priorities.begin(), p.file_priorities.end());
|
2016-02-11 19:58:18 +01:00
|
|
|
|
|
|
|
if (m_seed_mode)
|
|
|
|
{
|
|
|
|
m_verified.resize(m_torrent_file->num_pieces(), false);
|
|
|
|
m_verifying.resize(m_torrent_file->num_pieces(), false);
|
|
|
|
}
|
|
|
|
|
2015-12-15 06:03:43 +01:00
|
|
|
m_total_uploaded = p.total_uploaded;
|
|
|
|
m_total_downloaded = p.total_downloaded;
|
|
|
|
|
2016-11-15 18:03:11 +01:00
|
|
|
// the number of seconds this torrent has spent in started, finished and
|
2015-12-15 06:03:43 +01:00
|
|
|
// seeding state so far, respectively.
|
|
|
|
m_active_time = p.active_time;
|
|
|
|
m_finished_time = p.finished_time;
|
|
|
|
m_seeding_time = p.seeding_time;
|
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
m_added_time = p.added_time ? p.added_time : time(nullptr);
|
2015-12-15 06:03:43 +01:00
|
|
|
m_completed_time = p.completed_time;
|
|
|
|
if (m_completed_time != 0 && m_completed_time < m_added_time)
|
|
|
|
m_completed_time = m_added_time;
|
2007-04-11 19:44:15 +02:00
|
|
|
}
|
2007-04-11 19:22:19 +02:00
|
|
|
|
2014-07-13 06:56:53 +02:00
|
|
|
void torrent::inc_stats_counter(int c, int value)
|
|
|
|
{ m_ses.stats_counters().inc_stats_counter(c, value); }
|
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2011-01-18 04:41:54 +01:00
|
|
|
void torrent::on_torrent_download(error_code const& ec
|
2016-10-23 04:00:47 +02:00
|
|
|
, http_parser const& parser, char const* data, int size) try
|
2011-01-18 04:41:54 +01:00
|
|
|
{
|
|
|
|
if (m_abort) return;
|
|
|
|
|
2015-06-06 08:10:53 +02:00
|
|
|
if (ec && ec != boost::asio::error::eof)
|
2011-01-18 04:41:54 +01:00
|
|
|
{
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(ec, torrent_status::error_file_url);
|
2011-01-18 04:41:54 +01:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parser.status_code() != 200)
|
|
|
|
{
|
2016-10-02 21:27:50 +02:00
|
|
|
set_error(error_code(parser.status_code(), http_category()), torrent_status::error_file_url);
|
2011-01-18 04:41:54 +01:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
error_code e;
|
2016-08-17 23:26:35 +02:00
|
|
|
auto tf = std::make_shared<torrent_info>(data, size, std::ref(e), 0);
|
2011-01-18 04:41:54 +01:00
|
|
|
if (e)
|
|
|
|
{
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(e, torrent_status::error_file_url);
|
2011-01-18 04:41:54 +01:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2011-01-18 04:41:54 +01:00
|
|
|
// update our torrent_info object and move the
|
|
|
|
// torrent from the old info-hash to the new one
|
|
|
|
// as we replace the torrent_info object
|
2012-05-16 07:32:12 +02:00
|
|
|
// we're about to erase the session's reference to this
|
|
|
|
// torrent, create another reference
|
2016-10-06 06:08:14 +02:00
|
|
|
auto me = shared_from_this();
|
2012-05-16 07:32:12 +02:00
|
|
|
|
|
|
|
m_ses.remove_torrent_impl(me, 0);
|
|
|
|
|
2013-05-17 05:19:23 +02:00
|
|
|
if (alerts().should_post<torrent_update_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_update_alert>(get_handle(), info_hash(), tf->info_hash());
|
2013-05-17 05:19:23 +02:00
|
|
|
|
2011-01-18 04:41:54 +01:00
|
|
|
m_torrent_file = tf;
|
2014-11-12 17:57:59 +01:00
|
|
|
m_info_hash = tf->info_hash();
|
2012-05-19 23:33:42 +02:00
|
|
|
|
|
|
|
// now, we might already have this torrent in the session.
|
2016-08-31 14:27:36 +02:00
|
|
|
std::shared_ptr<torrent> t = m_ses.find_torrent(m_torrent_file->info_hash()).lock();
|
2014-07-06 21:18:00 +02:00
|
|
|
if (t)
|
2012-05-19 23:33:42 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_uuid.empty() && t->uuid().empty())
|
|
|
|
t->set_uuid(m_uuid);
|
|
|
|
if (!m_url.empty() && t->url().empty())
|
|
|
|
t->set_url(m_url);
|
2012-05-19 23:33:42 +02:00
|
|
|
|
|
|
|
// insert this torrent in the uuid index
|
|
|
|
if (!m_uuid.empty() || !m_url.empty())
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
m_ses.insert_uuid_torrent(m_uuid.empty() ? m_url : m_uuid, t);
|
2012-05-19 23:33:42 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// TODO: if the existing torrent doesn't have metadata, insert
|
|
|
|
// the metadata we just downloaded into it.
|
|
|
|
|
2016-10-02 21:27:50 +02:00
|
|
|
set_error(errors::duplicate_torrent, torrent_status::error_file_url);
|
2012-05-19 23:33:42 +02:00
|
|
|
abort();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_ses.insert_torrent(m_torrent_file->info_hash(), me, m_uuid);
|
2011-01-18 04:41:54 +01:00
|
|
|
|
2012-05-19 23:33:42 +02:00
|
|
|
// if the user added any trackers while downloading the
|
2014-07-06 21:18:00 +02:00
|
|
|
// .torrent file, merge them into the new tracker list
|
2012-05-19 23:33:42 +02:00
|
|
|
std::vector<announce_entry> new_trackers = m_torrent_file->trackers();
|
2016-11-15 18:03:11 +01:00
|
|
|
for (auto const& tr : m_trackers)
|
2012-05-19 23:33:42 +02:00
|
|
|
{
|
|
|
|
// if we already have this tracker, ignore it
|
2016-05-25 06:31:52 +02:00
|
|
|
if (std::any_of(new_trackers.begin(), new_trackers.end()
|
2016-11-15 18:03:11 +01:00
|
|
|
, [&tr] (announce_entry const& ae) { return ae.url == tr.url; }))
|
2012-05-19 23:33:42 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// insert the tracker ordered by tier
|
|
|
|
new_trackers.insert(std::find_if(new_trackers.begin(), new_trackers.end()
|
2016-11-15 18:03:11 +01:00
|
|
|
, [&tr] (announce_entry const& ae) { return ae.tier >= tr.tier; }), tr);
|
2012-05-19 23:33:42 +02:00
|
|
|
}
|
|
|
|
m_trackers.swap(new_trackers);
|
2010-12-30 02:47:30 +01:00
|
|
|
|
2016-01-17 05:57:33 +01:00
|
|
|
// add the web seeds from the .torrent file
|
|
|
|
std::vector<web_seed_entry> const& web_seeds = m_torrent_file->web_seeds();
|
|
|
|
m_web_seeds.insert(m_web_seeds.end(), web_seeds.begin(), web_seeds.end());
|
|
|
|
|
2014-11-23 07:14:47 +01:00
|
|
|
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
|
2016-07-24 00:57:04 +02:00
|
|
|
static char const req2[4] = {'r', 'e', 'q', '2'};
|
|
|
|
hasher h(req2);
|
2016-07-22 16:29:39 +02:00
|
|
|
h.update(m_torrent_file->info_hash());
|
2014-07-06 21:18:00 +02:00
|
|
|
m_ses.add_obfuscated_hash(h.final(), shared_from_this());
|
2010-12-30 02:47:30 +01:00
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<metadata_received_alert>())
|
2010-12-30 02:47:30 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<metadata_received_alert>(
|
|
|
|
get_handle());
|
2010-12-30 02:47:30 +01:00
|
|
|
}
|
|
|
|
|
2012-05-19 23:33:42 +02:00
|
|
|
state_updated();
|
|
|
|
|
2010-12-30 02:47:30 +01:00
|
|
|
set_state(torrent_status::downloading);
|
|
|
|
|
|
|
|
init();
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2010-12-30 02:47:30 +01:00
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif // TORRENT_NO_DEPRECATE
|
2014-03-13 09:00:53 +01:00
|
|
|
|
2015-11-28 20:06:40 +01:00
|
|
|
int torrent::current_stats_state() const
|
|
|
|
{
|
2016-10-19 07:18:05 +02:00
|
|
|
if (m_abort || !m_added)
|
|
|
|
return counters::num_checking_torrents + no_gauge_state;
|
2015-11-28 20:06:40 +01:00
|
|
|
|
|
|
|
if (has_error()) return counters::num_error_torrents;
|
2016-06-01 07:05:32 +02:00
|
|
|
if (m_paused || m_graceful_pause_mode)
|
2015-11-28 20:06:40 +01:00
|
|
|
{
|
|
|
|
if (!is_auto_managed()) return counters::num_stopped_torrents;
|
|
|
|
if (is_seed()) return counters::num_queued_seeding_torrents;
|
|
|
|
return counters::num_queued_download_torrents;
|
|
|
|
}
|
|
|
|
if (state() == torrent_status::checking_files
|
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
|| state() == torrent_status::queued_for_checking
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
return counters::num_checking_torrents;
|
|
|
|
else if (is_seed()) return counters::num_seeding_torrents;
|
|
|
|
else if (is_upload_only()) return counters::num_upload_only_torrents;
|
|
|
|
return counters::num_downloading_torrents;
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::update_gauge()
|
|
|
|
{
|
2016-10-19 07:18:05 +02:00
|
|
|
int const new_gauge_state = current_stats_state() - counters::num_checking_torrents;
|
2015-11-28 20:06:40 +01:00
|
|
|
TORRENT_ASSERT(new_gauge_state >= 0);
|
|
|
|
TORRENT_ASSERT(new_gauge_state <= no_gauge_state);
|
|
|
|
|
|
|
|
if (new_gauge_state == m_current_gauge_state) return;
|
|
|
|
|
|
|
|
if (m_current_gauge_state != no_gauge_state)
|
|
|
|
inc_stats_counter(m_current_gauge_state + counters::num_checking_torrents, -1);
|
|
|
|
if (new_gauge_state != no_gauge_state)
|
|
|
|
inc_stats_counter(new_gauge_state + counters::num_checking_torrents, 1);
|
|
|
|
|
|
|
|
m_current_gauge_state = new_gauge_state;
|
|
|
|
}
|
|
|
|
|
2015-07-21 19:12:37 +02:00
|
|
|
void torrent::leave_seed_mode(bool skip_checking)
|
2014-03-13 09:00:53 +01:00
|
|
|
{
|
|
|
|
if (!m_seed_mode) return;
|
|
|
|
|
2015-07-21 19:12:37 +02:00
|
|
|
if (!skip_checking)
|
2014-03-13 09:00:53 +01:00
|
|
|
{
|
|
|
|
// this means the user promised we had all the
|
|
|
|
// files, but it turned out we didn't. This is
|
|
|
|
// an error.
|
|
|
|
|
|
|
|
// TODO: 2 post alert
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-03-13 09:00:53 +01:00
|
|
|
debug_log("*** FAILED SEED MODE, rechecking");
|
2011-01-18 04:41:54 +01:00
|
|
|
#endif
|
2014-03-13 09:00:53 +01:00
|
|
|
}
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-07-21 19:12:37 +02:00
|
|
|
debug_log("*** LEAVING SEED MODE (%s)"
|
|
|
|
, skip_checking ? "as seed" : "as non-seed");
|
2014-03-13 09:00:53 +01:00
|
|
|
#endif
|
|
|
|
m_seed_mode = false;
|
|
|
|
// seed is false if we turned out not
|
|
|
|
// to be a seed after all
|
2015-07-21 19:12:37 +02:00
|
|
|
if (!skip_checking)
|
2014-03-13 09:00:53 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
m_have_all = false;
|
2014-03-13 09:00:53 +01:00
|
|
|
set_state(torrent_status::downloading);
|
|
|
|
force_recheck();
|
|
|
|
}
|
|
|
|
m_num_verified = 0;
|
|
|
|
m_verified.clear();
|
2014-07-06 21:18:00 +02:00
|
|
|
m_verifying.clear();
|
2014-07-13 00:03:29 +02:00
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2014-03-13 09:00:53 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::verified(piece_index_t const piece)
|
2014-03-13 09:00:53 +01:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_verified.get_bit(piece) == false);
|
|
|
|
++m_num_verified;
|
|
|
|
m_verified.set_bit(piece);
|
|
|
|
}
|
2011-01-18 04:41:54 +01:00
|
|
|
|
2015-06-21 00:17:49 +02:00
|
|
|
void torrent::start(add_torrent_params const& p)
|
2008-07-01 01:14:31 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-06-23 19:19:35 +02:00
|
|
|
TORRENT_ASSERT(m_was_started == false);
|
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
m_was_started = true;
|
|
|
|
#endif
|
2015-06-21 00:17:49 +02:00
|
|
|
|
2016-02-20 00:56:26 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
if (m_add_torrent_params
|
|
|
|
&& m_add_torrent_params->internal_resume_data_error
|
|
|
|
&& m_ses.alerts().should_post<fastresume_rejected_alert>())
|
|
|
|
{
|
|
|
|
m_ses.alerts().emplace_alert<fastresume_rejected_alert>(get_handle()
|
|
|
|
, m_add_torrent_params->internal_resume_data_error, "", "");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-02-14 00:07:02 +01:00
|
|
|
// TODO: 3 why isn't this done in the constructor?
|
2015-12-15 06:03:43 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("creating torrent: %s max-uploads: %d max-connections: %d "
|
2016-09-23 22:51:20 +02:00
|
|
|
"upload-limit: %d download-limit: %d flags: %s%s%s%s%s%s%s%s%s%s%s "
|
2016-09-14 04:46:07 +02:00
|
|
|
"save-path: %s"
|
|
|
|
, torrent_file().name().c_str()
|
|
|
|
, p.max_uploads
|
|
|
|
, p.max_connections
|
|
|
|
, p.upload_limit
|
|
|
|
, p.download_limit
|
|
|
|
, (p.flags & add_torrent_params::flag_seed_mode)
|
|
|
|
? "seed-mode " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_upload_mode)
|
|
|
|
? "upload-mode " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_share_mode)
|
|
|
|
? "share-mode " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_apply_ip_filter)
|
|
|
|
? "apply-ip-filter " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_paused)
|
|
|
|
? "paused " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_auto_managed)
|
|
|
|
? "auto-managed " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_update_subscribe)
|
|
|
|
? "update-subscribe " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_super_seeding)
|
|
|
|
? "super-seeding " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_sequential_download)
|
|
|
|
? "sequential-download " : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_override_trackers)
|
|
|
|
? "override-trackers" : ""
|
|
|
|
, (p.flags & add_torrent_params::flag_override_web_seeds)
|
|
|
|
? "override-web-seeds " : ""
|
|
|
|
, p.save_path.c_str()
|
|
|
|
);
|
|
|
|
}
|
2010-10-17 20:36:37 +02:00
|
|
|
#endif
|
2015-06-21 00:17:49 +02:00
|
|
|
if (p.flags & add_torrent_params::flag_sequential_download)
|
|
|
|
m_sequential_download = true;
|
|
|
|
|
|
|
|
if (p.flags & add_torrent_params::flag_super_seeding)
|
|
|
|
{
|
|
|
|
m_super_seeding = true;
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2015-06-21 00:17:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
set_max_uploads(p.max_uploads, false);
|
|
|
|
set_max_connections(p.max_connections, false);
|
|
|
|
set_limit_impl(p.upload_limit, peer_connection::upload_channel, false);
|
|
|
|
set_limit_impl(p.download_limit, peer_connection::download_channel, false);
|
|
|
|
|
2016-05-27 21:35:53 +02:00
|
|
|
for (auto const& peer : p.peers)
|
2015-06-21 00:17:49 +02:00
|
|
|
{
|
2016-05-27 21:35:53 +02:00
|
|
|
add_peer(peer, peer_info::resume_data);
|
2015-06-21 00:17:49 +02:00
|
|
|
}
|
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2015-06-21 00:17:49 +02:00
|
|
|
if (!m_name && !m_url.empty()) m_name.reset(new std::string(m_url));
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2015-06-21 00:17:49 +02:00
|
|
|
|
2016-10-15 19:32:59 +02:00
|
|
|
if (valid_metadata())
|
2015-06-21 00:17:49 +02:00
|
|
|
{
|
|
|
|
inc_stats_counter(counters::num_total_pieces_added
|
|
|
|
, m_torrent_file->num_pieces());
|
|
|
|
}
|
|
|
|
|
|
|
|
update_gauge();
|
|
|
|
|
2015-06-29 04:47:11 +02:00
|
|
|
m_file_progress.clear();
|
2008-11-12 04:30:19 +01:00
|
|
|
|
2015-07-12 20:08:04 +02:00
|
|
|
update_want_peers();
|
|
|
|
update_want_scrape();
|
|
|
|
update_want_tick();
|
|
|
|
update_state_list();
|
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2010-12-30 02:47:30 +01:00
|
|
|
if (!m_torrent_file->is_valid() && !m_url.empty())
|
2008-11-10 05:17:16 +01:00
|
|
|
{
|
2010-12-30 02:47:30 +01:00
|
|
|
// we need to download the .torrent file from m_url
|
|
|
|
start_download_url();
|
|
|
|
}
|
2016-02-20 21:49:49 +01:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if (m_torrent_file->is_valid())
|
2010-12-30 02:47:30 +01:00
|
|
|
{
|
2008-11-10 05:17:16 +01:00
|
|
|
init();
|
|
|
|
}
|
2008-08-29 19:21:56 +02:00
|
|
|
else
|
|
|
|
{
|
2011-06-08 10:45:28 +02:00
|
|
|
// we need to start announcing since we don't have any
|
|
|
|
// metadata. To receive peers to ask for it.
|
2008-08-29 19:21:56 +02:00
|
|
|
set_state(torrent_status::downloading_metadata);
|
2010-06-17 19:01:28 +02:00
|
|
|
start_announcing();
|
2008-08-29 19:21:56 +02:00
|
|
|
}
|
2015-07-12 20:08:04 +02:00
|
|
|
|
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
|
|
|
check_invariant();
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2010-12-30 02:47:30 +01:00
|
|
|
void torrent::start_download_url()
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(!m_url.empty());
|
|
|
|
TORRENT_ASSERT(!m_torrent_file->is_valid());
|
2016-08-31 18:45:45 +02:00
|
|
|
std::shared_ptr<http_connection> conn(
|
2014-10-03 22:56:57 +02:00
|
|
|
new http_connection(m_ses.get_io_service()
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_ses.get_resolver()
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_torrent_download, shared_from_this()
|
2012-10-19 03:28:47 +02:00
|
|
|
, _1, _2, _3, _4)
|
2014-04-06 07:07:10 +02:00
|
|
|
, true // bottled
|
2014-07-06 21:18:00 +02:00
|
|
|
//bottled buffer size
|
2015-05-16 08:33:37 +02:00
|
|
|
, settings().get_int(settings_pack::max_http_recv_buffer_size)
|
2014-04-06 07:07:10 +02:00
|
|
|
, http_connect_handler()
|
|
|
|
, http_filter_handler()
|
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
|
|
|
, m_ssl_ctx.get()
|
|
|
|
#endif
|
2012-10-19 03:28:47 +02:00
|
|
|
));
|
2015-08-25 04:18:10 +02:00
|
|
|
aux::proxy_settings ps = m_ses.proxy();
|
2014-07-06 21:18:00 +02:00
|
|
|
conn->get(m_url, seconds(30), 0, &ps
|
2016-09-19 00:56:12 +02:00
|
|
|
, 5
|
|
|
|
, settings().get_bool(settings_pack::anonymous_mode)
|
|
|
|
? "" : settings().get_str(settings_pack::user_agent));
|
2010-12-30 02:47:30 +01:00
|
|
|
set_state(torrent_status::downloading_metadata);
|
|
|
|
}
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2010-12-30 02:47:30 +01:00
|
|
|
|
2011-02-27 18:26:57 +01:00
|
|
|
void torrent::set_apply_ip_filter(bool b)
|
|
|
|
{
|
|
|
|
if (b == m_apply_ip_filter) return;
|
2011-03-02 18:37:10 +01:00
|
|
|
if (b)
|
|
|
|
{
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::non_filter_torrents, -1);
|
2011-03-02 18:37:10 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::non_filter_torrents);
|
2011-03-02 18:37:10 +01:00
|
|
|
}
|
2011-02-27 18:26:57 +01:00
|
|
|
m_apply_ip_filter = b;
|
2014-07-06 21:18:00 +02:00
|
|
|
ip_filter_updated();
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2011-02-27 18:26:57 +01:00
|
|
|
}
|
|
|
|
|
2016-09-01 03:42:18 +02:00
|
|
|
void torrent::set_ip_filter(std::shared_ptr<const ip_filter> ipf)
|
2015-05-16 08:33:37 +02:00
|
|
|
{
|
|
|
|
m_ip_filter = ipf;
|
|
|
|
if (!m_apply_ip_filter) return;
|
|
|
|
ip_filter_updated();
|
|
|
|
}
|
|
|
|
|
2007-02-12 10:20:49 +01:00
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
|
|
|
bool torrent::should_announce_dht() const
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_ses.announce_dht()) return false;
|
2007-11-25 10:33:26 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_ses.dht()) return false;
|
2008-08-30 00:58:47 +02:00
|
|
|
if (m_torrent_file->is_valid() && !m_files_checked) return false;
|
2010-03-29 02:34:04 +02:00
|
|
|
if (!m_announce_to_dht) return false;
|
2016-06-01 07:05:32 +02:00
|
|
|
if (m_paused) return false;
|
2011-01-16 03:56:57 +01:00
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2011-01-16 03:56:57 +01:00
|
|
|
// if we don't have the metadata, and we're waiting
|
|
|
|
// for a web server to serve it to us, no need to announce
|
|
|
|
// because the info-hash is just the URL hash
|
|
|
|
if (!m_torrent_file->is_valid() && !m_url.empty()) return false;
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2007-11-25 10:33:26 +01:00
|
|
|
|
2007-02-12 10:20:49 +01:00
|
|
|
// don't announce private torrents
|
2007-09-01 05:00:31 +02:00
|
|
|
if (m_torrent_file->is_valid() && m_torrent_file->priv()) return false;
|
2007-02-12 10:20:49 +01:00
|
|
|
if (m_trackers.empty()) return true;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!settings().get_bool(settings_pack::use_dht_as_fallback)) return true;
|
2008-11-29 09:38:40 +01:00
|
|
|
|
|
|
|
int verified_trackers = 0;
|
2016-11-15 18:03:11 +01:00
|
|
|
for (auto const& tr : m_trackers)
|
|
|
|
if (tr.verified) ++verified_trackers;
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2009-07-18 18:50:56 +02:00
|
|
|
return verified_trackers == 0;
|
2007-02-12 10:20:49 +01:00
|
|
|
}
|
2009-10-25 03:37:45 +01:00
|
|
|
|
2007-02-12 10:20:49 +01:00
|
|
|
#endif
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
torrent::~torrent()
|
|
|
|
{
|
2016-10-19 07:18:05 +02:00
|
|
|
// TODO: 3 assert there are no outstanding async operations on this
|
|
|
|
// torrent
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-06-18 14:31:07 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2014-07-06 21:18:00 +02:00
|
|
|
for (int i = 0; i < aux::session_interface::num_torrent_lists; ++i)
|
2011-03-03 04:57:47 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_links[i].in_list()) continue;
|
|
|
|
m_links[i].unlink(m_ses.torrent_list(i), i);
|
2011-03-03 04:57:47 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
|
2006-05-28 21:03:54 +02:00
|
|
|
// The invariant can't be maintained here, since the torrent
|
|
|
|
// is being destructed, all weak references to it have been
|
|
|
|
// reset, which means that all its peers already have an
|
|
|
|
// invalidated torrent pointer (so it cannot be verified to be correct)
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2006-05-28 21:03:54 +02:00
|
|
|
// i.e. the invariant can only be maintained if all connections have
|
|
|
|
// been closed by the time the torrent is destructed. And they are
|
|
|
|
// supposed to be closed. So we can still do the invariant check.
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// however, the torrent object may be destructed from the main
|
|
|
|
// thread when shutting down, if the disk cache has references to it.
|
|
|
|
// this means that the invariant check that this is called from the
|
|
|
|
// network thread cannot be maintained
|
2007-08-16 14:41:46 +02:00
|
|
|
|
2016-05-07 20:52:10 +02:00
|
|
|
TORRENT_ASSERT(m_peer_class == 0);
|
2012-04-23 07:48:46 +02:00
|
|
|
TORRENT_ASSERT(m_connections.empty());
|
2005-11-08 01:56:26 +01:00
|
|
|
if (!m_connections.empty())
|
2015-02-15 06:17:09 +01:00
|
|
|
disconnect_all(errors::torrent_aborted, op_bittorrent);
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2008-12-14 20:47:02 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::read_piece(piece_index_t const piece)
|
2008-12-14 20:47:02 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_abort || m_deleted)
|
2013-01-13 23:35:19 +01:00
|
|
|
{
|
|
|
|
// failed
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<read_piece_alert>(
|
2015-11-24 06:50:51 +01:00
|
|
|
get_handle(), piece, error_code(boost::system::errc::operation_canceled, generic_category()));
|
2013-01-13 23:35:19 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-03 05:28:39 +02:00
|
|
|
const int piece_size = m_torrent_file->piece_size(piece);
|
|
|
|
const int blocks_in_piece = (piece_size + block_size() - 1) / block_size();
|
2008-12-14 20:47:02 +01:00
|
|
|
|
2011-02-24 05:25:35 +01:00
|
|
|
TORRENT_ASSERT(blocks_in_piece > 0);
|
|
|
|
TORRENT_ASSERT(piece_size > 0);
|
|
|
|
|
2015-05-03 05:28:39 +02:00
|
|
|
if (blocks_in_piece == 0)
|
|
|
|
{
|
|
|
|
// this shouldn't actually happen
|
|
|
|
boost::shared_array<char> buf;
|
|
|
|
m_ses.alerts().emplace_alert<read_piece_alert>(
|
|
|
|
get_handle(), piece, buf, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<read_piece_struct> rp = std::make_shared<read_piece_struct>();
|
2008-12-14 20:47:02 +01:00
|
|
|
rp->piece_data.reset(new (std::nothrow) char[piece_size]);
|
|
|
|
rp->blocks_left = 0;
|
|
|
|
rp->fail = false;
|
|
|
|
|
|
|
|
peer_request r;
|
|
|
|
r.piece = piece;
|
|
|
|
r.start = 0;
|
2014-07-06 21:18:00 +02:00
|
|
|
rp->blocks_left = blocks_in_piece;
|
2010-02-14 02:39:55 +01:00
|
|
|
for (int i = 0; i < blocks_in_piece; ++i, r.start += block_size())
|
2008-12-14 20:47:02 +01:00
|
|
|
{
|
2010-02-14 02:39:55 +01:00
|
|
|
r.length = (std::min)(piece_size - r.start, block_size());
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_read(m_storage, r
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_disk_read_complete
|
2016-12-29 02:47:18 +01:00
|
|
|
, shared_from_this(), _1, _2, _3, r, rp), reinterpret_cast<void*>(1));
|
2008-12-14 20:47:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-05 18:01:36 +02:00
|
|
|
void torrent::send_share_mode()
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2014-07-06 21:18:00 +02:00
|
|
|
for (peer_iterator i = m_connections.begin()
|
2010-09-05 18:01:36 +02:00
|
|
|
, end(m_connections.end()); i != end; ++i)
|
|
|
|
{
|
2016-09-24 19:47:17 +02:00
|
|
|
if ((*i)->type() != connection_type::bittorrent) continue;
|
2015-08-11 02:03:24 +02:00
|
|
|
bt_peer_connection* p = static_cast<bt_peer_connection*>(*i);
|
2010-09-05 18:01:36 +02:00
|
|
|
p->write_share_mode();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-11-24 19:49:59 +01:00
|
|
|
void torrent::send_upload_only()
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2011-04-10 01:57:56 +02:00
|
|
|
if (share_mode()) return;
|
|
|
|
if (super_seeding()) return;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int idx = 0;
|
|
|
|
for (peer_iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end(); ++idx)
|
2009-11-24 19:49:59 +01:00
|
|
|
{
|
2011-04-10 01:57:56 +02:00
|
|
|
// since the call to disconnect_if_redundant() may
|
|
|
|
// delete the entry from this container, make sure
|
|
|
|
// to increment the iterator early
|
2016-10-08 18:29:46 +02:00
|
|
|
peer_connection* p = *i;
|
2016-09-24 19:47:17 +02:00
|
|
|
if (p->type() == connection_type::bittorrent)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-08 20:17:51 +02:00
|
|
|
bt_peer_connection* btp = static_cast<bt_peer_connection*>(p);
|
|
|
|
std::shared_ptr<peer_connection> me(btp->self());
|
2016-10-08 18:29:46 +02:00
|
|
|
if (!btp->is_disconnecting())
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-08 18:29:46 +02:00
|
|
|
btp->send_not_interested();
|
|
|
|
btp->write_upload_only();
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (p->is_disconnecting())
|
|
|
|
{
|
|
|
|
i = m_connections.begin() + idx;
|
|
|
|
--idx;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
}
|
2009-11-24 19:49:59 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-09-05 18:01:36 +02:00
|
|
|
void torrent::set_share_mode(bool s)
|
|
|
|
{
|
|
|
|
if (s == m_share_mode) return;
|
|
|
|
|
|
|
|
m_share_mode = s;
|
2015-06-20 23:51:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("*** set-share-mode: %d", s);
|
|
|
|
#endif
|
2010-09-05 18:01:36 +02:00
|
|
|
|
|
|
|
// in share mode, all pieces have their priorities initialized to 0
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_share_mode && valid_metadata())
|
|
|
|
{
|
|
|
|
m_file_priority.clear();
|
|
|
|
m_file_priority.resize(m_torrent_file->num_files(), 0);
|
|
|
|
}
|
2010-09-05 18:01:36 +02:00
|
|
|
|
|
|
|
update_piece_priorities();
|
|
|
|
|
|
|
|
if (m_share_mode) recalc_share_mode();
|
|
|
|
}
|
|
|
|
|
2009-06-19 00:32:55 +02:00
|
|
|
void torrent::set_upload_mode(bool b)
|
|
|
|
{
|
|
|
|
if (b == m_upload_mode) return;
|
|
|
|
|
|
|
|
m_upload_mode = b;
|
2015-06-20 23:51:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("*** set-upload-mode: %d", b);
|
|
|
|
#endif
|
2009-06-19 00:32:55 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2009-11-24 19:49:59 +01:00
|
|
|
send_upload_only();
|
|
|
|
|
2009-06-19 00:32:55 +02:00
|
|
|
if (m_upload_mode)
|
|
|
|
{
|
|
|
|
// clear request queues of all peers
|
2014-07-06 21:18:00 +02:00
|
|
|
for (peer_iterator i = m_connections.begin()
|
2009-06-19 00:32:55 +02:00
|
|
|
, end(m_connections.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
peer_connection* p = (*i);
|
2015-06-16 07:24:35 +02:00
|
|
|
// we may want to disconnect other upload-only peers
|
|
|
|
if (p->upload_only())
|
|
|
|
p->update_interest();
|
2009-06-19 00:32:55 +02:00
|
|
|
p->cancel_all_requests();
|
|
|
|
}
|
|
|
|
// this is used to try leaving upload only mode periodically
|
2014-08-27 08:36:28 +02:00
|
|
|
m_upload_mode_time = m_ses.session_time();
|
2009-06-19 00:32:55 +02:00
|
|
|
}
|
2014-10-26 08:34:31 +01:00
|
|
|
else if (m_peer_list)
|
2009-06-19 00:32:55 +02:00
|
|
|
{
|
2011-03-25 06:14:14 +01:00
|
|
|
// reset last_connected, to force fast reconnect after leaving upload mode
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto pe : *m_peer_list)
|
2011-03-25 06:14:14 +01:00
|
|
|
{
|
2016-06-01 07:05:17 +02:00
|
|
|
pe->last_connected = 0;
|
2011-03-25 06:14:14 +01:00
|
|
|
}
|
|
|
|
|
2009-06-19 00:32:55 +02:00
|
|
|
// send_block_requests on all peers
|
2014-07-06 21:18:00 +02:00
|
|
|
for (peer_iterator i = m_connections.begin()
|
2009-06-19 00:32:55 +02:00
|
|
|
, end(m_connections.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
peer_connection* p = (*i);
|
2015-06-16 07:24:35 +02:00
|
|
|
// we may be interested now, or no longer interested
|
|
|
|
p->update_interest();
|
2009-06-19 00:32:55 +02:00
|
|
|
p->send_block_requests();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
void torrent::need_peer_list()
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2014-10-26 08:34:31 +01:00
|
|
|
if (m_peer_list) return;
|
|
|
|
m_peer_list.reset(new peer_list);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
void torrent::handle_exception()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
catch (system_error const& err)
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("torrent exception: (%d) %s: %s"
|
|
|
|
, err.code().value(), err.code().message().c_str()
|
|
|
|
, err.what());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
set_error(err.code(), torrent_status::error_file_exception);
|
|
|
|
}
|
|
|
|
catch (std::exception const& err)
|
|
|
|
{
|
|
|
|
TORRENT_UNUSED(err);
|
|
|
|
set_error(error_code(), torrent_status::error_file_exception);
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("torrent exception: %s", err.what());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
set_error(error_code(), torrent_status::error_file_exception);
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("torrent exception: unknown");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-16 03:57:44 +01:00
|
|
|
void torrent::handle_disk_error(string_view job_name
|
|
|
|
, storage_error const& error
|
|
|
|
, peer_connection* c
|
|
|
|
, disk_class rw)
|
2009-06-01 00:41:53 +02:00
|
|
|
{
|
2016-11-16 08:16:58 +01:00
|
|
|
TORRENT_UNUSED(job_name);
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-11-16 03:57:44 +01:00
|
|
|
TORRENT_ASSERT(error);
|
2009-06-01 00:41:53 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
2016-11-16 03:57:44 +01:00
|
|
|
debug_log("disk error: (%d) %s [%*s : %s] in file: %s"
|
|
|
|
, error.ec.value(), error.ec.message().c_str()
|
|
|
|
, int(job_name.size()), job_name.data(), error.operation_str()
|
2016-12-22 16:42:33 +01:00
|
|
|
, resolve_filename(error.file()).c_str());
|
2016-09-14 04:46:07 +02:00
|
|
|
}
|
2009-06-01 00:41:53 +02:00
|
|
|
#endif
|
|
|
|
|
2016-11-16 03:57:44 +01:00
|
|
|
if (error.ec == boost::system::errc::not_enough_memory)
|
2009-06-01 00:41:53 +02:00
|
|
|
{
|
|
|
|
if (alerts().should_post<file_error_alert>())
|
2016-11-16 03:57:44 +01:00
|
|
|
alerts().emplace_alert<file_error_alert>(error.ec
|
2016-12-22 16:42:33 +01:00
|
|
|
, resolve_filename(error.file()), error.operation_str(), get_handle());
|
2015-02-15 06:17:09 +01:00
|
|
|
if (c) c->disconnect(errors::no_memory, op_file);
|
2009-06-01 00:41:53 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-16 03:57:44 +01:00
|
|
|
if (error.ec == boost::asio::error::operation_aborted) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2009-06-01 00:41:53 +02:00
|
|
|
// notify the user of the error
|
|
|
|
if (alerts().should_post<file_error_alert>())
|
2016-11-16 03:57:44 +01:00
|
|
|
alerts().emplace_alert<file_error_alert>(error.ec
|
2016-12-22 16:42:33 +01:00
|
|
|
, resolve_filename(error.file()), error.operation_str(), get_handle());
|
2009-06-01 00:41:53 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// if a write operation failed, and future writes are likely to
|
|
|
|
// fail, while reads may succeed, just set the torrent to upload mode
|
|
|
|
// if we make an incorrect assumption here, it's not the end of the
|
|
|
|
// world, if we ever issue a read request and it fails as well, we
|
|
|
|
// won't get in here and we'll actually end up pausing the torrent
|
2016-11-16 03:57:44 +01:00
|
|
|
if (rw == disk_class::write
|
|
|
|
&& (error.ec == boost::system::errc::read_only_file_system
|
|
|
|
|| error.ec == boost::system::errc::permission_denied
|
|
|
|
|| error.ec == boost::system::errc::operation_not_permitted
|
|
|
|
|| error.ec == boost::system::errc::no_space_on_device
|
|
|
|
|| error.ec == boost::system::errc::file_too_large))
|
2009-06-10 11:20:55 +02:00
|
|
|
{
|
|
|
|
// if we failed to write, stop downloading and just
|
|
|
|
// keep seeding.
|
2013-01-21 02:40:59 +01:00
|
|
|
// TODO: 1 make this depend on the error and on the filesystem the
|
2009-06-10 11:20:55 +02:00
|
|
|
// files are being downloaded to. If the error is no_space_left_on_device
|
|
|
|
// and the filesystem doesn't support sparse files, only zero the priorities
|
|
|
|
// of the pieces that are at the tails of all files, leaving everything
|
|
|
|
// up to the highest written piece in each file
|
2009-06-19 00:32:55 +02:00
|
|
|
set_upload_mode(true);
|
|
|
|
return;
|
2009-06-10 11:20:55 +02:00
|
|
|
}
|
|
|
|
|
2015-06-15 08:27:09 +02:00
|
|
|
// put the torrent in an error-state
|
2016-12-22 16:42:33 +01:00
|
|
|
set_error(error.ec, error.file());
|
2015-06-15 08:27:09 +02:00
|
|
|
|
2011-05-16 09:47:21 +02:00
|
|
|
// if the error appears to be more serious than a full disk, just pause the torrent
|
2009-06-01 00:41:53 +02:00
|
|
|
pause();
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::on_piece_fail_sync(piece_index_t, piece_block) try
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-03-02 07:16:08 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
// some peers that previously was no longer interesting may
|
|
|
|
// now have become interesting, since we lack this one piece now.
|
|
|
|
for (peer_iterator i = begin(); i != end();)
|
|
|
|
{
|
|
|
|
peer_connection* p = *i;
|
|
|
|
// update_interest may disconnect the peer and
|
|
|
|
// invalidate the iterator
|
|
|
|
++i;
|
|
|
|
// no need to do anything with peers that
|
|
|
|
// already are interested. Gaining a piece may
|
|
|
|
// only make uninteresting peers interesting again.
|
|
|
|
if (p->is_interesting()) continue;
|
|
|
|
p->update_interest();
|
|
|
|
if (!m_abort)
|
|
|
|
{
|
|
|
|
if (request_a_block(*this, *p))
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::hash_fail_piece_picks);
|
2014-07-06 21:18:00 +02:00
|
|
|
p->send_block_requests();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-12-29 02:47:18 +01:00
|
|
|
void torrent::on_disk_read_complete(disk_buffer_holder buffer
|
|
|
|
, int, storage_error const& se
|
2016-11-21 05:58:48 +01:00
|
|
|
, peer_request r, std::shared_ptr<read_piece_struct> rp) try
|
2008-12-14 20:47:02 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// hold a reference until this function returns
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-12-14 20:47:02 +01:00
|
|
|
|
|
|
|
--rp->blocks_left;
|
2016-11-21 05:58:48 +01:00
|
|
|
if (se)
|
2008-12-14 20:47:02 +01:00
|
|
|
{
|
|
|
|
rp->fail = true;
|
2016-11-21 05:58:48 +01:00
|
|
|
rp->error = se.ec;
|
|
|
|
handle_disk_error("read", se);
|
2008-12-14 20:47:02 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-12-29 02:47:18 +01:00
|
|
|
std::memcpy(rp->piece_data.get() + r.start, buffer.get(), r.length);
|
2008-12-14 20:47:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rp->blocks_left == 0)
|
|
|
|
{
|
|
|
|
int size = m_torrent_file->piece_size(r.piece);
|
|
|
|
if (rp->fail)
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<read_piece_alert>(
|
|
|
|
get_handle(), r.piece, rp->error);
|
2013-01-29 03:18:32 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<read_piece_alert>(
|
|
|
|
get_handle(), r.piece, rp->piece_data, size);
|
2008-12-14 20:47:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2008-12-14 20:47:02 +01:00
|
|
|
|
2015-09-18 06:23:45 +02:00
|
|
|
storage_mode_t torrent::storage_mode() const
|
|
|
|
{ return storage_mode_t(m_storage_mode); }
|
|
|
|
|
2016-12-31 18:35:10 +01:00
|
|
|
storage_interface* torrent::get_storage_impl() const
|
2015-09-18 06:23:45 +02:00
|
|
|
{
|
2016-12-31 18:35:10 +01:00
|
|
|
return m_ses.disk_thread().get_torrent(m_storage);
|
2015-09-18 06:23:45 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::need_picker()
|
|
|
|
{
|
|
|
|
if (m_picker) return;
|
|
|
|
|
2015-08-28 03:48:52 +02:00
|
|
|
TORRENT_ASSERT(valid_metadata());
|
2015-08-29 16:21:15 +02:00
|
|
|
TORRENT_ASSERT(m_connections_initialized);
|
2015-08-28 03:48:52 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
// if we have all pieces we should not have a picker
|
2016-07-02 22:20:12 +02:00
|
|
|
// unless we're in suggest mode
|
|
|
|
TORRENT_ASSERT(!m_have_all
|
|
|
|
|| settings().get_int(settings_pack::suggest_mode)
|
|
|
|
== settings_pack::suggest_read_cache);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
std::unique_ptr<piece_picker> pp(new piece_picker());
|
2016-06-15 19:29:54 +02:00
|
|
|
int const blocks_per_piece
|
|
|
|
= (m_torrent_file->piece_length() + block_size() - 1) / block_size();
|
|
|
|
int const blocks_in_last_piece
|
|
|
|
= ((m_torrent_file->total_size() % m_torrent_file->piece_length())
|
2014-07-06 21:18:00 +02:00
|
|
|
+ block_size() - 1) / block_size();
|
2016-10-23 04:00:47 +02:00
|
|
|
|
|
|
|
// TODO: 3 the init function should be merged with the constructor
|
|
|
|
pp->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces());
|
|
|
|
|
|
|
|
m_picker = std::move(pp);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-07-03 20:48:18 +02:00
|
|
|
// initialize the file progress too
|
|
|
|
if (m_file_progress.empty())
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(has_picker());
|
|
|
|
m_file_progress.init(picker(), m_torrent_file->files());
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
|
2016-09-23 22:51:20 +02:00
|
|
|
for (auto const& p : m_connections)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-09-23 22:51:20 +02:00
|
|
|
peer_has(p->get_bitfield(), p);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-26 08:38:05 +01:00
|
|
|
struct piece_refcount
|
|
|
|
{
|
|
|
|
piece_refcount(piece_picker& p, piece_index_t piece)
|
|
|
|
: m_picker(p)
|
|
|
|
, m_piece(piece)
|
|
|
|
{
|
|
|
|
m_picker.inc_refcount(m_piece, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
piece_refcount(piece_refcount const&) = delete;
|
|
|
|
piece_refcount& operator=(piece_refcount const&) = delete;
|
|
|
|
|
|
|
|
~piece_refcount()
|
|
|
|
{
|
|
|
|
m_picker.dec_refcount(m_piece, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
piece_picker& m_picker;
|
|
|
|
piece_index_t m_piece;
|
|
|
|
};
|
|
|
|
|
2016-07-15 20:35:57 +02:00
|
|
|
// TODO: 3 there's some duplication between this function and
|
|
|
|
// peer_connection::incoming_piece(). is there a way to merge something?
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::add_piece(piece_index_t const piece, char const* data, int const flags)
|
2008-12-07 22:04:19 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-12-07 22:04:19 +01:00
|
|
|
int piece_size = m_torrent_file->piece_size(piece);
|
2010-02-14 02:39:55 +01:00
|
|
|
int blocks_in_piece = (piece_size + block_size() - 1) / block_size();
|
2008-12-07 22:04:19 +01:00
|
|
|
|
2013-08-22 07:27:45 +02:00
|
|
|
if (m_deleted) return;
|
|
|
|
|
2011-02-09 03:56:00 +01:00
|
|
|
// avoid crash trying to access the picker when there is none
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_have_all && !has_picker()) return;
|
|
|
|
|
|
|
|
need_picker();
|
2010-01-27 07:06:57 +01:00
|
|
|
|
2010-05-01 19:47:28 +02:00
|
|
|
if (picker().have_piece(piece)
|
|
|
|
&& (flags & torrent::overwrite_existing) == 0)
|
|
|
|
return;
|
2010-04-22 03:53:09 +02:00
|
|
|
|
2008-12-07 22:04:19 +01:00
|
|
|
peer_request p;
|
|
|
|
p.piece = piece;
|
|
|
|
p.start = 0;
|
2016-12-26 08:38:05 +01:00
|
|
|
piece_refcount refcount{picker(), piece};
|
2010-02-14 02:39:55 +01:00
|
|
|
for (int i = 0; i < blocks_in_piece; ++i, p.start += block_size())
|
2008-12-07 22:04:19 +01:00
|
|
|
{
|
|
|
|
if (picker().is_finished(piece_block(piece, i))
|
|
|
|
&& (flags & torrent::overwrite_existing) == 0)
|
|
|
|
continue;
|
|
|
|
|
2016-12-26 08:38:05 +01:00
|
|
|
p.length = std::min(piece_size - p.start, int(block_size()));
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2016-07-15 20:35:57 +02:00
|
|
|
m_stats_counters.inc_stats_counter(counters::queued_write_bytes, p.length);
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_write(m_storage, p, data + p.start, nullptr
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_disk_write_complete
|
2014-07-06 21:18:00 +02:00
|
|
|
, shared_from_this(), _1, p));
|
2016-07-15 20:35:57 +02:00
|
|
|
|
2008-12-07 22:04:19 +01:00
|
|
|
piece_block block(piece, i);
|
2016-07-15 20:35:57 +02:00
|
|
|
bool const was_finished = picker().is_piece_finished(p.piece);
|
|
|
|
bool const multi = picker().num_peers(block) > 1;
|
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
picker().mark_as_downloading(block, nullptr);
|
|
|
|
picker().mark_as_writing(block, nullptr);
|
2016-07-15 20:35:57 +02:00
|
|
|
|
|
|
|
if (multi) cancel_block(block);
|
|
|
|
|
|
|
|
// did we just finish the piece?
|
|
|
|
// this means all blocks are either written
|
|
|
|
// to disk or are in the disk write cache
|
|
|
|
if (picker().is_piece_finished(p.piece) && !was_finished)
|
|
|
|
{
|
|
|
|
verify_piece(p.piece);
|
|
|
|
}
|
2008-12-07 22:04:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-21 05:58:48 +01:00
|
|
|
void torrent::on_disk_write_complete(storage_error const& error
|
2016-10-23 04:00:47 +02:00
|
|
|
, peer_request p) try
|
2008-12-07 22:04:19 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-07-15 20:35:57 +02:00
|
|
|
m_stats_counters.inc_stats_counter(counters::queued_write_bytes, -p.length);
|
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
// std::fprintf(stderr, "torrent::on_disk_write_complete ret:%d piece:%d block:%d\n"
|
2014-07-06 21:18:00 +02:00
|
|
|
// , j->ret, j->piece, j->offset/0x4000);
|
2008-12-07 22:04:19 +01:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2016-07-15 20:35:57 +02:00
|
|
|
if (m_abort) return;
|
2010-02-14 02:39:55 +01:00
|
|
|
piece_block block_finished(p.piece, p.start / block_size());
|
2008-12-07 22:04:19 +01:00
|
|
|
|
2016-11-21 05:58:48 +01:00
|
|
|
if (error)
|
2008-12-07 22:04:19 +01:00
|
|
|
{
|
2016-11-21 05:58:48 +01:00
|
|
|
handle_disk_error("write", error);
|
2008-12-07 22:04:19 +01:00
|
|
|
return;
|
|
|
|
}
|
2010-01-27 07:06:57 +01:00
|
|
|
|
2012-04-25 21:16:22 +02:00
|
|
|
if (!has_picker()) return;
|
|
|
|
|
2010-01-27 07:06:57 +01:00
|
|
|
// if we already have this block, just ignore it.
|
|
|
|
// this can happen if the same block is passed in through
|
|
|
|
// add_piece() multiple times
|
|
|
|
if (picker().is_finished(block_finished)) return;
|
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
picker().mark_as_finished(block_finished, nullptr);
|
2014-07-06 21:18:00 +02:00
|
|
|
maybe_done_flushing();
|
2016-07-15 20:35:57 +02:00
|
|
|
|
|
|
|
if (alerts().should_post<block_finished_alert>())
|
|
|
|
{
|
|
|
|
alerts().emplace_alert<block_finished_alert>(get_handle(),
|
|
|
|
tcp::endpoint(), peer_id(), int(block_finished.block_index)
|
2016-12-22 16:42:33 +01:00
|
|
|
, block_finished.piece_index);
|
2016-07-15 20:35:57 +02:00
|
|
|
}
|
2008-12-07 22:04:19 +01:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
bool torrent::add_merkle_nodes(std::map<int, sha1_hash> const& nodes
|
|
|
|
, piece_index_t const piece)
|
2009-03-13 07:09:39 +01:00
|
|
|
{
|
|
|
|
return m_torrent_file->add_merkle_nodes(nodes, piece);
|
|
|
|
}
|
|
|
|
|
2010-02-23 17:26:24 +01:00
|
|
|
peer_request torrent::to_req(piece_block const& p) const
|
2007-10-22 22:58:42 +02:00
|
|
|
{
|
2010-02-14 02:39:55 +01:00
|
|
|
int block_offset = p.block_index * block_size();
|
|
|
|
int block = (std::min)(torrent_file().piece_size(
|
2016-08-13 01:24:03 +02:00
|
|
|
p.piece_index) - block_offset, block_size());
|
2010-02-14 02:39:55 +01:00
|
|
|
TORRENT_ASSERT(block > 0);
|
|
|
|
TORRENT_ASSERT(block <= block_size());
|
2007-10-22 22:58:42 +02:00
|
|
|
|
|
|
|
peer_request r;
|
|
|
|
r.piece = p.piece_index;
|
|
|
|
r.start = block_offset;
|
2010-02-14 02:39:55 +01:00
|
|
|
r.length = block;
|
2007-10-22 22:58:42 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
std::string torrent::name() const
|
|
|
|
{
|
2007-09-01 05:00:31 +02:00
|
|
|
if (valid_metadata()) return m_torrent_file->name();
|
2006-11-14 01:08:16 +01:00
|
|
|
if (m_name) return *m_name;
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2007-11-08 02:45:35 +01:00
|
|
|
|
2016-08-17 20:30:24 +02:00
|
|
|
void torrent::add_extension(std::shared_ptr<torrent_plugin> ext)
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
|
|
|
m_extensions.push_back(ext);
|
|
|
|
}
|
2007-11-08 02:45:35 +01:00
|
|
|
|
2016-08-17 20:30:24 +02:00
|
|
|
void torrent::remove_extension(std::shared_ptr<torrent_plugin> ext)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-05-08 00:46:42 +02:00
|
|
|
auto i = std::find(m_extensions.begin(), m_extensions.end(), ext);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (i == m_extensions.end()) return;
|
|
|
|
m_extensions.erase(i);
|
|
|
|
}
|
|
|
|
|
2016-08-17 20:30:24 +02:00
|
|
|
void torrent::add_extension_fun(std::function<std::shared_ptr<torrent_plugin>(torrent_handle const&, void*)> const& ext
|
2007-11-08 02:45:35 +01:00
|
|
|
, void* userdata)
|
|
|
|
{
|
2016-08-17 20:30:24 +02:00
|
|
|
std::shared_ptr<torrent_plugin> tp(ext(get_handle(), userdata));
|
2007-11-08 02:45:35 +01:00
|
|
|
if (!tp) return;
|
|
|
|
|
2016-08-13 01:24:03 +02:00
|
|
|
add_extension(std::move(tp));
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2016-08-13 01:24:03 +02:00
|
|
|
for (auto p : m_connections)
|
2007-11-08 02:45:35 +01:00
|
|
|
{
|
2016-08-17 20:30:24 +02:00
|
|
|
std::shared_ptr<peer_plugin> pp(tp->new_connection(peer_connection_handle(p->self())));
|
2016-08-13 01:24:03 +02:00
|
|
|
if (pp) p->add_extension(std::move(pp));
|
2007-11-08 02:45:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// if files are checked for this torrent, call the extension
|
|
|
|
// to let it initialize itself
|
|
|
|
if (m_connections_initialized)
|
|
|
|
tp->on_files_checked();
|
|
|
|
}
|
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
#endif
|
|
|
|
|
2011-08-28 23:06:15 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2016-09-07 23:51:18 +02:00
|
|
|
#ifdef TORRENT_MACOS_DEPRECATED_LIBCRYPTO
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
#endif
|
2012-01-16 00:34:43 +01:00
|
|
|
|
|
|
|
bool torrent::verify_peer_cert(bool preverified, boost::asio::ssl::verify_context& ctx)
|
2011-08-28 23:06:15 +02:00
|
|
|
{
|
2012-01-16 00:34:43 +01:00
|
|
|
// if the cert wasn't signed by the correct CA, fail the verification
|
|
|
|
if (!preverified) return false;
|
|
|
|
|
|
|
|
// we're only interested in checking the certificate at the end of the chain.
|
2013-07-28 17:06:28 +02:00
|
|
|
// TODO: is verify_peer_cert called once per certificate in the chain, and
|
|
|
|
// this function just tells us which depth we're at right now? If so, the comment
|
|
|
|
// makes sense.
|
|
|
|
// any certificate that isn't the leaf (i.e. the one presented by the peer)
|
|
|
|
// should be accepted automatically, given preverified is true. The leaf certificate
|
|
|
|
// need to be verified to make sure its DN matches the info-hash
|
2012-01-16 00:34:43 +01:00
|
|
|
int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle());
|
|
|
|
if (depth > 0) return true;
|
|
|
|
|
|
|
|
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
|
|
|
|
|
|
|
|
// Go through the alternate names in the certificate looking for matching DNS entries
|
|
|
|
GENERAL_NAMES* gens = static_cast<GENERAL_NAMES*>(
|
2016-07-10 20:27:42 +02:00
|
|
|
X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
|
2012-01-16 00:34:43 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-01-16 00:34:43 +01:00
|
|
|
std::string names;
|
|
|
|
bool match = false;
|
|
|
|
#endif
|
2015-09-07 23:00:27 +02:00
|
|
|
for (int i = 0; i < aux::openssl_num_general_names(gens); ++i)
|
2012-01-16 00:34:43 +01:00
|
|
|
{
|
2015-09-07 23:00:27 +02:00
|
|
|
GENERAL_NAME* gen = aux::openssl_general_name_value(gens, i);
|
2012-01-16 00:34:43 +01:00
|
|
|
if (gen->type != GEN_DNS) continue;
|
|
|
|
ASN1_IA5STRING* domain = gen->d.dNSName;
|
|
|
|
if (domain->type != V_ASN1_IA5STRING || !domain->data || !domain->length) continue;
|
|
|
|
const char* torrent_name = reinterpret_cast<const char*>(domain->data);
|
|
|
|
std::size_t name_length = domain->length;
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-01-16 00:34:43 +01:00
|
|
|
if (i > 1) names += " | n: ";
|
|
|
|
names.append(torrent_name, name_length);
|
|
|
|
#endif
|
|
|
|
if (strncmp(torrent_name, "*", name_length) == 0
|
|
|
|
|| strncmp(torrent_name, m_torrent_file->name().c_str(), name_length) == 0)
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-01-16 00:34:43 +01:00
|
|
|
match = true;
|
|
|
|
// if we're logging, keep looping over all names,
|
|
|
|
// for completeness of the log
|
|
|
|
continue;
|
2015-09-02 07:30:40 +02:00
|
|
|
#else
|
2012-01-16 00:34:43 +01:00
|
|
|
return true;
|
2015-09-02 07:30:40 +02:00
|
|
|
#endif
|
2012-01-16 00:34:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// no match in the alternate names, so try the common names. We should only
|
|
|
|
// use the "most specific" common name, which is the last one in the list.
|
|
|
|
X509_NAME* name = X509_get_subject_name(cert);
|
|
|
|
int i = -1;
|
2016-07-10 20:27:42 +02:00
|
|
|
ASN1_STRING* common_name = nullptr;
|
2012-01-16 00:34:43 +01:00
|
|
|
while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0)
|
|
|
|
{
|
|
|
|
X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i);
|
|
|
|
common_name = X509_NAME_ENTRY_get_data(name_entry);
|
|
|
|
}
|
|
|
|
if (common_name && common_name->data && common_name->length)
|
|
|
|
{
|
|
|
|
const char* torrent_name = reinterpret_cast<const char*>(common_name->data);
|
|
|
|
std::size_t name_length = common_name->length;
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-01-16 00:34:43 +01:00
|
|
|
if (!names.empty()) names += " | n: ";
|
|
|
|
names.append(torrent_name, name_length);
|
|
|
|
#endif
|
|
|
|
|
2016-12-11 02:51:55 +01:00
|
|
|
if (std::strncmp(torrent_name, "*", name_length) == 0
|
|
|
|
|| std::strncmp(torrent_name, m_torrent_file->name().c_str(), name_length) == 0)
|
2012-01-16 00:34:43 +01:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifdef TORRENT_DISABLE_LOGGING
|
2012-01-16 00:34:43 +01:00
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
match = true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("<== incoming SSL CONNECTION [ n: %s | match: %s ]"
|
|
|
|
, names.c_str(), match?"yes":"no");
|
2012-01-16 00:34:43 +01:00
|
|
|
return match;
|
2015-09-02 07:30:40 +02:00
|
|
|
#else
|
2012-01-16 00:34:43 +01:00
|
|
|
return false;
|
2015-09-02 07:30:40 +02:00
|
|
|
#endif
|
2011-08-28 23:06:15 +02:00
|
|
|
}
|
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
void torrent::init_ssl(std::string const& cert)
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
2011-09-12 05:51:49 +02:00
|
|
|
using boost::asio::ssl::context;
|
2004-06-14 01:30:42 +02:00
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
// this is needed for openssl < 1.0 to decrypt keys created by openssl 1.0+
|
|
|
|
OpenSSL_add_all_algorithms();
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint64_t now = clock_type::now().time_since_epoch().count();
|
2012-01-16 00:34:43 +01:00
|
|
|
// assume 9 bits of entropy (i.e. about 1 millisecond)
|
|
|
|
RAND_add(&now, 8, 1.125);
|
|
|
|
RAND_add(&info_hash()[0], 20, 3);
|
|
|
|
// entropy is also added on incoming and completed connection attempts
|
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
TORRENT_ASSERT(RAND_status() == 1);
|
|
|
|
|
|
|
|
// create the SSL context for this torrent. We need to
|
|
|
|
// inject the root certificate, and no other, to
|
|
|
|
// verify other peers against
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<context> ctx = std::make_shared<context>(m_ses.get_io_service(), context::sslv23);
|
2011-09-12 05:51:49 +02:00
|
|
|
|
|
|
|
if (!ctx)
|
2011-08-28 23:06:15 +02:00
|
|
|
{
|
2016-12-05 14:39:53 +01:00
|
|
|
error_code ec(int(::ERR_get_error()),
|
2015-06-06 08:10:53 +02:00
|
|
|
boost::asio::error::get_ssl_category());
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(ec, torrent_status::error_file_ssl_ctx);
|
2011-09-12 05:51:49 +02:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
2011-08-28 23:06:15 +02:00
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
ctx->set_options(context::default_workarounds
|
|
|
|
| boost::asio::ssl::context::no_sslv2
|
|
|
|
| boost::asio::ssl::context::single_dh_use);
|
2011-08-28 23:06:15 +02:00
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
error_code ec;
|
|
|
|
ctx->set_verify_mode(context::verify_peer
|
|
|
|
| context::verify_fail_if_no_peer_cert
|
|
|
|
| context::verify_client_once, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(ec, torrent_status::error_file_ssl_ctx);
|
2011-09-12 05:51:49 +02:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-16 00:34:43 +01:00
|
|
|
// the verification function verifies the distinguished name
|
|
|
|
// of a peer certificate to make sure it matches the info-hash
|
|
|
|
// of the torrent, or that it's a "star-cert"
|
2016-05-25 06:31:52 +02:00
|
|
|
ctx->set_verify_callback(std::bind(&torrent::verify_peer_cert, this, _1, _2), ec);
|
2011-09-12 05:51:49 +02:00
|
|
|
if (ec)
|
|
|
|
{
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(ec, torrent_status::error_file_ssl_ctx);
|
2011-09-12 05:51:49 +02:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
2012-01-16 00:34:43 +01:00
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
SSL_CTX* ssl_ctx = ctx->impl();
|
|
|
|
// create a new x.509 certificate store
|
|
|
|
X509_STORE* cert_store = X509_STORE_new();
|
|
|
|
if (!cert_store)
|
|
|
|
{
|
2016-12-05 14:39:53 +01:00
|
|
|
ec.assign(int(::ERR_get_error()),
|
2015-06-06 08:10:53 +02:00
|
|
|
boost::asio::error::get_ssl_category());
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(ec, torrent_status::error_file_ssl_ctx);
|
2011-09-12 05:51:49 +02:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// wrap the PEM certificate in a BIO, for openssl to read
|
2015-09-02 07:30:40 +02:00
|
|
|
BIO* bp = BIO_new_mem_buf(
|
|
|
|
const_cast<void*>(static_cast<void const*>(cert.c_str()))
|
2016-05-02 18:36:21 +02:00
|
|
|
, int(cert.size()));
|
2011-09-12 05:51:49 +02:00
|
|
|
|
|
|
|
// parse the certificate into OpenSSL's internal
|
|
|
|
// representation
|
2016-07-10 20:27:42 +02:00
|
|
|
X509* certificate = PEM_read_bio_X509_AUX(bp, nullptr, nullptr, nullptr);
|
2011-09-12 05:51:49 +02:00
|
|
|
|
|
|
|
BIO_free(bp);
|
|
|
|
|
|
|
|
if (!certificate)
|
|
|
|
{
|
2016-12-05 14:39:53 +01:00
|
|
|
ec.assign(int(::ERR_get_error()),
|
2015-06-06 08:10:53 +02:00
|
|
|
boost::asio::error::get_ssl_category());
|
2011-09-12 05:51:49 +02:00
|
|
|
X509_STORE_free(cert_store);
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(ec, torrent_status::error_file_ssl_ctx);
|
2011-09-12 05:51:49 +02:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add cert to cert_store
|
|
|
|
X509_STORE_add_cert(cert_store, certificate);
|
|
|
|
|
2012-04-07 02:35:25 +02:00
|
|
|
X509_free(certificate);
|
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
// and lastly, replace the default cert store with ours
|
|
|
|
SSL_CTX_set_cert_store(ssl_ctx, cert_store);
|
|
|
|
#if 0
|
|
|
|
char filename[100];
|
2016-05-17 15:24:06 +02:00
|
|
|
std::snprintf(filename, sizeof(filename), "/tmp/%u.pem", random());
|
2011-09-12 05:51:49 +02:00
|
|
|
FILE* f = fopen(filename, "w+");
|
|
|
|
fwrite(cert.c_str(), cert.size(), 1, f);
|
|
|
|
fclose(f);
|
|
|
|
ctx->load_verify_file(filename);
|
|
|
|
#endif
|
|
|
|
// if all went well, set the torrent ssl context to this one
|
|
|
|
m_ssl_ctx = ctx;
|
|
|
|
// tell the client we need a cert for this torrent
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_need_cert_alert>(get_handle());
|
2011-09-12 05:51:49 +02:00
|
|
|
}
|
2016-09-07 23:51:18 +02:00
|
|
|
#ifdef TORRENT_MACOS_DEPRECATED_LIBCRYPTO
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
#endif
|
2012-01-14 17:04:25 +01:00
|
|
|
#endif // TORRENT_OPENSSL
|
2011-09-12 05:51:49 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::construct_storage()
|
|
|
|
{
|
|
|
|
storage_params params;
|
|
|
|
|
|
|
|
if (&m_torrent_file->orig_files() != &m_torrent_file->files())
|
|
|
|
{
|
|
|
|
params.mapped_files = &m_torrent_file->files();
|
|
|
|
params.files = &m_torrent_file->orig_files();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
params.files = &m_torrent_file->files();
|
2016-07-09 22:26:26 +02:00
|
|
|
params.mapped_files = nullptr;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
params.path = m_save_path;
|
|
|
|
params.pool = &m_ses.disk_thread().files();
|
2015-08-11 02:03:24 +02:00
|
|
|
params.mode = static_cast<storage_mode_t>(m_storage_mode);
|
2014-07-06 21:18:00 +02:00
|
|
|
params.priorities = &m_file_priority;
|
|
|
|
params.info = m_torrent_file.get();
|
|
|
|
|
|
|
|
TORRENT_ASSERT(m_storage_constructor);
|
2016-12-31 18:35:10 +01:00
|
|
|
|
|
|
|
std::unique_ptr<storage_interface> storage(m_storage_constructor(params));
|
|
|
|
storage->set_files(&m_torrent_file->files());
|
2014-07-06 21:18:00 +02:00
|
|
|
// the shared_from_this() will create an intentional
|
|
|
|
// cycle of ownership, se the hpp file for description.
|
2016-12-31 18:35:10 +01:00
|
|
|
storage->set_owner(shared_from_this());
|
|
|
|
m_storage = m_ses.disk_thread().new_torrent(std::move(storage));
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2012-12-31 07:54:54 +01:00
|
|
|
peer_connection* torrent::find_lowest_ranking_peer() const
|
|
|
|
{
|
2013-01-28 03:13:25 +01:00
|
|
|
const_peer_iterator lowest_rank = end();
|
|
|
|
for (const_peer_iterator i = begin(); i != end(); ++i)
|
|
|
|
{
|
|
|
|
// disconnecting peers don't count
|
|
|
|
if ((*i)->is_disconnecting()) continue;
|
|
|
|
if (lowest_rank == end() || (*lowest_rank)->peer_rank() > (*i)->peer_rank())
|
|
|
|
lowest_rank = i;
|
|
|
|
}
|
2012-12-31 07:54:54 +01:00
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
if (lowest_rank == end()) return nullptr;
|
2012-12-31 07:54:54 +01:00
|
|
|
return *lowest_rank;
|
|
|
|
}
|
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
// this may not be called from a constructor because of the call to
|
2015-12-15 06:03:43 +01:00
|
|
|
// shared_from_this(). It's either called when we start() the torrent, or at a
|
|
|
|
// later time if it's a magnet link, once the metadata is downloaded
|
2011-09-12 05:51:49 +02:00
|
|
|
void torrent::init()
|
|
|
|
{
|
2015-07-27 09:01:19 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-12-09 10:08:26 +01:00
|
|
|
debug_log("init torrent: %s", torrent_file().name().c_str());
|
|
|
|
#endif
|
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
TORRENT_ASSERT(valid_metadata());
|
2011-09-12 05:51:49 +02:00
|
|
|
TORRENT_ASSERT(m_torrent_file->num_files() > 0);
|
|
|
|
TORRENT_ASSERT(m_torrent_file->total_size() >= 0);
|
|
|
|
|
2014-05-10 05:23:05 +02:00
|
|
|
if (int(m_file_priority.size()) > m_torrent_file->num_files())
|
2013-01-28 05:57:35 +01:00
|
|
|
m_file_priority.resize(m_torrent_file->num_files());
|
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
std::string cert = m_torrent_file->ssl_cert();
|
2013-07-28 17:06:28 +02:00
|
|
|
if (!cert.empty())
|
|
|
|
{
|
|
|
|
m_ssl_torrent = true;
|
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
|
|
|
init_ssl(cert);
|
2011-08-28 23:06:15 +02:00
|
|
|
#endif
|
2013-07-28 17:06:28 +02:00
|
|
|
}
|
2011-08-28 23:06:15 +02:00
|
|
|
|
2016-06-08 00:44:39 +02:00
|
|
|
m_block_size_shift = root2((std::min)(block_size(), m_torrent_file->piece_length()));
|
2008-06-18 14:34:39 +02:00
|
|
|
|
2009-02-23 02:21:19 +01:00
|
|
|
if (m_torrent_file->num_pieces() > piece_picker::max_pieces)
|
2008-06-19 13:28:34 +02:00
|
|
|
{
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(errors::too_many_pieces_in_torrent, torrent_status::error_file_none);
|
2008-06-19 13:28:34 +02:00
|
|
|
pause();
|
2010-02-22 02:51:25 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-03-02 08:08:29 +01:00
|
|
|
if (m_torrent_file->num_pieces() == 0)
|
|
|
|
{
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(errors::torrent_invalid_length, torrent_status::error_file_none);
|
2010-03-02 08:08:29 +01:00
|
|
|
pause();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-14 00:07:02 +01:00
|
|
|
// --- MAPPED FILES ---
|
2016-12-22 16:42:33 +01:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
2016-02-14 00:07:02 +01:00
|
|
|
if (m_add_torrent_params)
|
|
|
|
{
|
2016-06-26 18:13:08 +02:00
|
|
|
for (auto const& f : m_add_torrent_params->renamed_files)
|
2016-02-14 00:07:02 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
if (f.first < file_index_t(0) || f.first >= fs.end_file()) continue;
|
|
|
|
m_torrent_file->rename_file(file_index_t(f.first), f.second);
|
2016-02-14 00:07:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
construct_storage();
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2015-07-25 07:34:53 +02:00
|
|
|
if (m_share_mode && valid_metadata())
|
|
|
|
{
|
|
|
|
// in share mode, all pieces have their priorities initialized to 0
|
|
|
|
m_file_priority.clear();
|
|
|
|
m_file_priority.resize(m_torrent_file->num_files(), 0);
|
|
|
|
}
|
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
// it's important to initialize the peers early, because this is what will
|
|
|
|
// fix up their have-bitmasks to have the correct size
|
|
|
|
// TODO: 2 add a unit test where we don't have metadata, connect to a peer
|
|
|
|
// that sends a bitfield that's too large, then we get the metadata
|
|
|
|
if (!m_connections_initialized)
|
|
|
|
{
|
|
|
|
m_connections_initialized = true;
|
|
|
|
// all peer connections have to initialize themselves now that the metadata
|
|
|
|
// is available
|
|
|
|
// copy the peer list since peers may disconnect and invalidate
|
|
|
|
// m_connections as we initialize them
|
|
|
|
std::vector<peer_connection*> peers = m_connections;
|
2016-06-26 18:13:08 +02:00
|
|
|
for (auto* pc : peers)
|
2015-08-29 16:21:15 +02:00
|
|
|
{
|
|
|
|
if (pc->is_disconnecting()) continue;
|
|
|
|
pc->on_metadata_impl();
|
|
|
|
if (pc->is_disconnecting()) continue;
|
|
|
|
pc->init();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-14 04:19:55 +02:00
|
|
|
// if we've already loaded file priorities, don't load piece priorities,
|
|
|
|
// they will interfere.
|
2016-02-16 07:43:06 +01:00
|
|
|
if (m_add_torrent_params && m_file_priority.empty())
|
2014-11-08 17:58:18 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t idx(0);
|
|
|
|
for (auto prio : m_add_torrent_params->piece_priorities)
|
2014-11-08 17:58:18 +01:00
|
|
|
{
|
2016-12-24 02:00:24 +01:00
|
|
|
if (has_picker() || prio != default_piece_priority)
|
2016-12-22 16:42:33 +01:00
|
|
|
{
|
|
|
|
need_picker();
|
|
|
|
m_picker->set_piece_priority(idx, prio);
|
|
|
|
}
|
|
|
|
++idx;
|
2014-11-08 17:58:18 +01:00
|
|
|
}
|
2016-02-12 23:50:29 +01:00
|
|
|
update_gauge();
|
2014-11-08 17:58:18 +01:00
|
|
|
}
|
|
|
|
|
2015-08-02 21:18:21 +02:00
|
|
|
// in case file priorities were passed in via the add_torrent_params
|
|
|
|
// and also in the case of share mode, we need to update the priorities
|
|
|
|
if (!m_file_priority.empty() && std::find(m_file_priority.begin()
|
2016-11-15 18:03:11 +01:00
|
|
|
, m_file_priority.end(), 0) != m_file_priority.end())
|
2015-08-02 21:18:21 +02:00
|
|
|
{
|
|
|
|
update_piece_priorities();
|
|
|
|
}
|
|
|
|
|
2009-02-03 08:46:24 +01:00
|
|
|
if (m_seed_mode)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
m_have_all = true;
|
2016-10-06 06:08:14 +02:00
|
|
|
auto self = shared_from_this();
|
|
|
|
m_ses.get_io_service().post([self] { self->wrap(&torrent::files_checked); });
|
2016-06-26 18:13:08 +02:00
|
|
|
TORRENT_ASSERT(m_outstanding_check_files == false);
|
2016-02-12 23:50:29 +01:00
|
|
|
m_add_torrent_params.reset();
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
2015-07-27 09:01:19 +02:00
|
|
|
update_state_list();
|
2009-02-03 08:46:24 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-21 19:12:37 +02:00
|
|
|
set_state(torrent_status::checking_resume_data);
|
|
|
|
|
2013-09-14 12:08:31 +02:00
|
|
|
int num_pad_files = 0;
|
2010-02-14 02:39:55 +01:00
|
|
|
TORRENT_ASSERT(block_size() > 0);
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < fs.end_file(); ++i)
|
2008-10-03 07:49:41 +02:00
|
|
|
{
|
2013-09-14 12:08:31 +02:00
|
|
|
if (fs.pad_file_at(i)) ++num_pad_files;
|
|
|
|
|
2013-08-12 09:30:57 +02:00
|
|
|
if (!fs.pad_file_at(i) || fs.file_size(i) == 0) continue;
|
2016-06-18 20:01:38 +02:00
|
|
|
m_padding += std::uint32_t(fs.file_size(i));
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// TODO: instead of creating the picker up front here,
|
|
|
|
// maybe this whole section should move to need_picker()
|
|
|
|
need_picker();
|
|
|
|
|
2016-12-05 14:39:53 +01:00
|
|
|
peer_request pr = m_torrent_file->map_file(i, 0, int(fs.file_size(i)));
|
2016-12-11 02:51:55 +01:00
|
|
|
int off = pr.start & (block_size() - 1);
|
2010-02-14 02:39:55 +01:00
|
|
|
if (off != 0) { pr.length -= block_size() - off; pr.start += block_size() - off; }
|
2016-12-11 02:51:55 +01:00
|
|
|
TORRENT_ASSERT((pr.start & (block_size() - 1)) == 0);
|
2010-02-14 02:39:55 +01:00
|
|
|
|
|
|
|
int block = block_size();
|
|
|
|
int blocks_per_piece = m_torrent_file->piece_length() / block;
|
2010-12-13 17:47:12 +01:00
|
|
|
piece_block pb(pr.piece, pr.start / block);
|
2010-02-14 02:39:55 +01:00
|
|
|
for (; pr.length >= block; pr.length -= block, ++pb.block_index)
|
2008-10-03 07:49:41 +02:00
|
|
|
{
|
2016-11-15 18:03:11 +01:00
|
|
|
if (pb.block_index == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
|
2016-07-09 22:26:26 +02:00
|
|
|
m_picker->mark_as_finished(pb, nullptr);
|
2008-10-03 07:49:41 +02:00
|
|
|
}
|
2010-12-13 17:47:12 +01:00
|
|
|
// ugly edge case where padfiles are not used they way they're
|
|
|
|
// supposed to be. i.e. added back-to back or at the end
|
2016-06-13 13:47:16 +02:00
|
|
|
if (pb.block_index == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
|
2016-12-22 16:42:33 +01:00
|
|
|
if (pr.length > 0 && ((next(i) != fs.end_file() && fs.pad_file_at(next(i)))
|
|
|
|
|| next(i) == fs.end_file()))
|
2010-12-13 17:47:12 +01:00
|
|
|
{
|
2016-07-09 22:26:26 +02:00
|
|
|
m_picker->mark_as_finished(pb, nullptr);
|
2010-12-13 17:47:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_padding > 0)
|
|
|
|
{
|
|
|
|
// if we marked an entire piece as finished, we actually
|
|
|
|
// need to consider it finished
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
std::vector<piece_picker::downloading_piece> dq
|
2010-12-13 17:47:12 +01:00
|
|
|
= m_picker->get_download_queue();
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
std::vector<piece_index_t> have_pieces;
|
2010-12-13 17:47:12 +01:00
|
|
|
|
2016-11-15 18:03:11 +01:00
|
|
|
for (auto const& p : dq)
|
2010-12-13 17:47:12 +01:00
|
|
|
{
|
2016-11-15 18:03:11 +01:00
|
|
|
int num_blocks = m_picker->blocks_in_piece(p.index);
|
|
|
|
if (p.finished < num_blocks) continue;
|
|
|
|
have_pieces.push_back(p.index);
|
2010-12-13 17:47:12 +01:00
|
|
|
}
|
|
|
|
|
2016-12-23 12:07:37 +01:00
|
|
|
for (auto i : have_pieces)
|
2010-12-13 17:47:12 +01:00
|
|
|
{
|
2016-11-15 18:03:11 +01:00
|
|
|
picker().piece_passed(i);
|
|
|
|
TORRENT_ASSERT(picker().have_piece(i));
|
|
|
|
we_have(i);
|
2010-12-13 17:47:12 +01:00
|
|
|
}
|
2008-10-03 07:49:41 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (num_pad_files > 0)
|
|
|
|
m_picker->set_num_pad_files(num_pad_files);
|
2013-09-14 12:08:31 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
aux::vector<std::string, file_index_t> links;
|
2015-03-21 01:12:40 +01:00
|
|
|
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
if (!m_torrent_file->similar_torrents().empty()
|
|
|
|
|| !m_torrent_file->collections().empty())
|
|
|
|
{
|
|
|
|
resolve_links res(m_torrent_file);
|
|
|
|
|
2016-11-15 18:03:11 +01:00
|
|
|
for (auto const& ih : m_torrent_file->similar_torrents())
|
2015-03-21 01:12:40 +01:00
|
|
|
{
|
2016-11-15 18:03:11 +01:00
|
|
|
std::shared_ptr<torrent> t = m_ses.find_torrent(ih).lock();
|
2015-03-21 01:12:40 +01:00
|
|
|
if (!t) continue;
|
|
|
|
|
|
|
|
// Only attempt to reuse files from torrents that are seeding.
|
|
|
|
// TODO: this could be optimized by looking up which files are
|
|
|
|
// complete and just look at those
|
|
|
|
if (!t->is_seed()) continue;
|
|
|
|
|
|
|
|
res.match(t->get_torrent_copy(), t->save_path());
|
|
|
|
}
|
2016-11-15 18:03:11 +01:00
|
|
|
for (auto const& c : m_torrent_file->collections())
|
2015-03-21 01:12:40 +01:00
|
|
|
{
|
2016-11-15 18:03:11 +01:00
|
|
|
std::vector<std::shared_ptr<torrent>> ts = m_ses.find_collection(c);
|
2015-03-21 01:12:40 +01:00
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
for (std::vector<std::shared_ptr<torrent>>::iterator k = ts.begin()
|
2015-08-16 18:17:23 +02:00
|
|
|
, end2(ts.end()); k != end2; ++k)
|
2015-03-21 01:12:40 +01:00
|
|
|
{
|
|
|
|
// Only attempt to reuse files from torrents that are seeding.
|
|
|
|
// TODO: this could be optimized by looking up which files are
|
|
|
|
// complete and just look at those
|
|
|
|
if (!(*k)->is_seed()) continue;
|
|
|
|
|
|
|
|
res.match((*k)->get_torrent_copy(), (*k)->save_path());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<resolve_links::link_t> const& l = res.get_links();
|
|
|
|
if (!l.empty())
|
|
|
|
{
|
|
|
|
for (std::vector<resolve_links::link_t>::const_iterator i = l.begin()
|
|
|
|
, end(l.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
if (!i->ti) continue;
|
|
|
|
|
|
|
|
torrent_info const& ti = *i->ti;
|
|
|
|
std::string const& save_path = i->save_path;
|
2015-08-20 01:33:20 +02:00
|
|
|
links.push_back(combine_path(save_path
|
2015-03-21 01:12:40 +01:00
|
|
|
, ti.files().file_path(i->file_idx)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
TORRENT_ASSERT(m_outstanding_check_files == false);
|
|
|
|
m_outstanding_check_files = true;
|
|
|
|
#endif
|
2016-10-19 07:18:05 +02:00
|
|
|
m_ses.disk_thread().async_check_files(
|
2016-12-31 18:35:10 +01:00
|
|
|
m_storage, m_add_torrent_params ? m_add_torrent_params.get() : nullptr
|
2016-10-19 07:18:05 +02:00
|
|
|
, links, std::bind(&torrent::on_resume_data_checked
|
2016-11-23 01:03:27 +01:00
|
|
|
, shared_from_this(), _1, _2));
|
2016-02-15 00:17:32 +01:00
|
|
|
// async_check_files will gut links
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-02-15 00:17:32 +01:00
|
|
|
debug_log("init, async_check_files");
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
update_want_peers();
|
|
|
|
|
|
|
|
maybe_done_flushing();
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
|
2010-11-29 02:33:05 +01:00
|
|
|
bt_peer_connection* torrent::find_introducer(tcp::endpoint const& ep) const
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto pe : m_connections)
|
2010-11-29 02:33:05 +01:00
|
|
|
{
|
2016-09-24 19:47:17 +02:00
|
|
|
if (pe->type() != connection_type::bittorrent) continue;
|
2016-06-01 07:05:17 +02:00
|
|
|
bt_peer_connection* p = static_cast<bt_peer_connection*>(pe);
|
2010-11-29 02:33:05 +01:00
|
|
|
if (!p->supports_holepunch()) continue;
|
2016-08-16 03:05:39 +02:00
|
|
|
if (p->was_introduced_by(ep)) return p;
|
2010-11-29 02:33:05 +01:00
|
|
|
}
|
2015-08-22 00:28:12 +02:00
|
|
|
#else
|
|
|
|
TORRENT_UNUSED(ep);
|
2010-11-29 02:33:05 +01:00
|
|
|
#endif
|
2016-06-01 07:05:17 +02:00
|
|
|
return nullptr;
|
2010-11-29 02:33:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bt_peer_connection* torrent::find_peer(tcp::endpoint const& ep) const
|
|
|
|
{
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto p : m_connections)
|
2010-11-29 02:33:05 +01:00
|
|
|
{
|
2016-09-24 19:47:17 +02:00
|
|
|
if (p->type() != connection_type::bittorrent) continue;
|
2015-08-11 02:03:24 +02:00
|
|
|
if (p->remote() == ep) return static_cast<bt_peer_connection*>(p);
|
2010-11-29 02:33:05 +01:00
|
|
|
}
|
2016-06-20 17:32:06 +02:00
|
|
|
return nullptr;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
peer_connection* torrent::find_peer(sha1_hash const& pid)
|
|
|
|
{
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto p : m_connections)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (p->pid() == pid) return p;
|
|
|
|
}
|
2016-07-09 22:26:26 +02:00
|
|
|
return nullptr;
|
2010-11-29 02:33:05 +01:00
|
|
|
}
|
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
void torrent::on_resume_data_checked(status_t const status
|
2016-11-23 01:03:27 +01:00
|
|
|
, storage_error const& error) try
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// hold a reference until this function returns
|
|
|
|
|
2016-06-26 18:13:08 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
TORRENT_ASSERT(m_outstanding_check_files);
|
|
|
|
m_outstanding_check_files = false;
|
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// when applying some of the resume data to the torrent, we will
|
|
|
|
// trigger calls that set m_need_save_resume_data, even though we're
|
|
|
|
// just applying the state of the resume data we loaded with. We don't
|
|
|
|
// want anything in this function to affect the state of
|
|
|
|
// m_need_save_resume_data, so we save it in a local variable and reset
|
|
|
|
// it at the end of the function.
|
2016-06-26 18:13:08 +02:00
|
|
|
bool const need_save_resume_data = m_need_save_resume_data;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
if (status == status_t::fatal_disk_error)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2016-06-26 18:13:08 +02:00
|
|
|
TORRENT_ASSERT(m_outstanding_check_files == false);
|
2016-06-10 07:38:32 +02:00
|
|
|
m_add_torrent_params.reset();
|
2016-11-23 01:03:27 +01:00
|
|
|
handle_disk_error("check_resume_data", error);
|
2013-01-06 05:53:26 +01:00
|
|
|
auto_managed(false);
|
|
|
|
pause();
|
2014-07-06 21:18:00 +02:00
|
|
|
set_state(torrent_status::checking_files);
|
|
|
|
if (should_check_files()) start_checking();
|
2008-03-08 07:06:31 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-02 07:16:08 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2011-12-19 06:55:38 +01:00
|
|
|
state_updated();
|
|
|
|
|
2016-02-12 23:50:29 +01:00
|
|
|
if (m_add_torrent_params)
|
|
|
|
{
|
|
|
|
// --- PEERS ---
|
|
|
|
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto const& p : m_add_torrent_params->peers)
|
2016-02-12 23:50:29 +01:00
|
|
|
{
|
2016-06-01 07:05:17 +02:00
|
|
|
add_peer(p , peer_info::resume_data);
|
2016-02-12 23:50:29 +01:00
|
|
|
}
|
|
|
|
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto const& p : m_add_torrent_params->banned_peers)
|
2016-02-12 23:50:29 +01:00
|
|
|
{
|
2016-06-01 07:05:17 +02:00
|
|
|
torrent_peer* peer = add_peer(p, peer_info::resume_data);
|
2016-02-12 23:50:29 +01:00
|
|
|
if (peer) ban_peer(peer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_add_torrent_params->peers.empty()
|
|
|
|
|| !m_add_torrent_params->banned_peers.empty())
|
|
|
|
{
|
|
|
|
update_want_peers();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (m_peer_list && m_peer_list->num_peers() > 0)
|
|
|
|
debug_log("resume added peers (%d)", m_peer_list->num_peers());
|
|
|
|
#endif
|
|
|
|
}
|
2016-02-11 19:58:18 +01:00
|
|
|
|
2010-08-23 07:51:12 +02:00
|
|
|
// only report this error if the user actually provided resume data
|
2016-02-16 07:43:06 +01:00
|
|
|
// (i.e. m_add_torrent_params->have_pieces)
|
2016-11-26 07:51:47 +01:00
|
|
|
if ((error || status != status_t::no_error)
|
2016-02-16 07:43:06 +01:00
|
|
|
&& m_add_torrent_params
|
|
|
|
&& !m_add_torrent_params->have_pieces.empty()
|
2014-07-06 21:18:00 +02:00
|
|
|
&& m_ses.alerts().should_post<fastresume_rejected_alert>())
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2016-02-12 23:50:29 +01:00
|
|
|
m_ses.alerts().emplace_alert<fastresume_rejected_alert>(get_handle()
|
2016-11-23 01:03:27 +01:00
|
|
|
, error.ec
|
2016-12-22 16:42:33 +01:00
|
|
|
, resolve_filename(error.file())
|
2016-11-23 01:03:27 +01:00
|
|
|
, error.operation_str());
|
2009-02-03 18:35:41 +01:00
|
|
|
}
|
2010-08-23 07:51:12 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
2012-09-28 01:04:51 +02:00
|
|
|
{
|
2016-11-26 07:51:47 +01:00
|
|
|
if (status != status_t::no_error)
|
2016-09-14 04:46:07 +02:00
|
|
|
{
|
|
|
|
debug_log("fastresume data rejected: ret: %d (%d) %s"
|
2016-11-26 07:51:47 +01:00
|
|
|
, static_cast<int>(status), error.ec.value(), error.ec.message().c_str());
|
2016-09-14 04:46:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug_log("fastresume data accepted");
|
|
|
|
}
|
2016-02-20 07:59:24 +01:00
|
|
|
}
|
2012-09-28 01:04:51 +02:00
|
|
|
#endif
|
2016-02-20 07:59:24 +01:00
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
bool should_start_full_check = status != status_t::no_error;
|
2016-02-20 07:59:24 +01:00
|
|
|
|
|
|
|
// if we got a partial pieces bitfield, it means we were in the middle of
|
|
|
|
// checking this torrent. pick it up where we left off
|
|
|
|
if (!should_start_full_check
|
|
|
|
&& m_add_torrent_params
|
|
|
|
&& m_add_torrent_params->have_pieces.size() > 0
|
|
|
|
&& m_add_torrent_params->have_pieces.size() < m_torrent_file->num_pieces())
|
|
|
|
{
|
|
|
|
m_checking_piece = m_num_checked_pieces
|
2016-12-22 16:42:33 +01:00
|
|
|
= m_add_torrent_params->have_pieces.end_index();
|
2016-02-20 07:59:24 +01:00
|
|
|
should_start_full_check = true;
|
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2010-08-23 07:51:12 +02:00
|
|
|
// if ret != 0, it means we need a full check. We don't necessarily need
|
|
|
|
// that when the resume data check fails. For instance, if the resume data
|
|
|
|
// is incorrect, but we don't have any files, we skip the check and initialize
|
|
|
|
// the storage to not have anything.
|
2016-11-26 07:51:47 +01:00
|
|
|
if (status == status_t::no_error)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
|
|
|
// there are either no files for this torrent
|
|
|
|
// or the resume_data was accepted
|
|
|
|
|
2016-11-23 01:03:27 +01:00
|
|
|
if (!error && m_add_torrent_params)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2016-02-12 23:50:29 +01:00
|
|
|
// --- PIECES ---
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
int const num_pieces = std::min(m_add_torrent_params->have_pieces.size()
|
2016-02-12 23:50:29 +01:00
|
|
|
, torrent_file().num_pieces());
|
2016-12-22 16:42:33 +01:00
|
|
|
for (piece_index_t i = piece_index_t(0); i < piece_index_t(num_pieces); ++i)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2016-02-12 23:50:29 +01:00
|
|
|
if (m_add_torrent_params->have_pieces[i] == false) continue;
|
2016-09-23 22:49:39 +02:00
|
|
|
need_picker();
|
|
|
|
m_picker->we_have(i);
|
|
|
|
inc_stats_counter(counters::num_piece_passed);
|
|
|
|
update_gauge();
|
|
|
|
we_have(i);
|
2016-02-12 23:50:29 +01:00
|
|
|
}
|
2015-07-27 09:01:19 +02:00
|
|
|
|
2016-02-12 23:50:29 +01:00
|
|
|
if (m_seed_mode)
|
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
int const num_pieces2 = std::min(m_add_torrent_params->verified_pieces.size()
|
2016-02-12 23:50:29 +01:00
|
|
|
, torrent_file().num_pieces());
|
2016-12-22 16:42:33 +01:00
|
|
|
for (piece_index_t i = piece_index_t(0);
|
|
|
|
i < piece_index_t(num_pieces2); ++i)
|
2016-02-12 23:50:29 +01:00
|
|
|
{
|
|
|
|
if (m_add_torrent_params->verified_pieces[i] == false) continue;
|
|
|
|
m_verified.set_bit(i);
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-12 23:50:29 +01:00
|
|
|
// --- UNFINISHED PIECES ---
|
|
|
|
|
2016-06-08 00:44:39 +02:00
|
|
|
int const num_blocks_per_piece = torrent_file().piece_length() / block_size();
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2016-09-23 22:51:20 +02:00
|
|
|
for (auto const& p : m_add_torrent_params->unfinished_pieces)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t const piece = p.first;
|
2016-09-23 22:51:20 +02:00
|
|
|
bitfield const& blocks = p.second;
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
if (piece < piece_index_t(0) || piece >= torrent_file().end_piece())
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-21 19:12:37 +02:00
|
|
|
|
2016-02-12 23:50:29 +01:00
|
|
|
// being in seed mode and missing a piece is not compatible.
|
|
|
|
// Leave seed mode if that happens
|
|
|
|
if (m_seed_mode) leave_seed_mode(true);
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2016-02-12 23:50:29 +01:00
|
|
|
if (has_picker() && m_picker->have_piece(piece))
|
|
|
|
{
|
|
|
|
m_picker->we_dont_have(piece);
|
|
|
|
update_gauge();
|
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2016-02-12 23:50:29 +01:00
|
|
|
need_picker();
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-02-12 23:50:29 +01:00
|
|
|
const int num_bits = (std::min)(num_blocks_per_piece, blocks.size());
|
|
|
|
for (int k = 0; k < num_bits; ++k)
|
|
|
|
{
|
|
|
|
if (blocks.get_bit(k))
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-07-09 22:26:26 +02:00
|
|
|
m_picker->mark_as_finished(piece_block(piece, k), nullptr);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
2016-02-12 23:50:29 +01:00
|
|
|
if (m_picker->is_piece_finished(piece))
|
|
|
|
{
|
|
|
|
verify_piece(piece);
|
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-21 06:38:34 +01:00
|
|
|
|
|
|
|
if (should_start_full_check)
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
|
|
|
// either the fastresume data was rejected or there are
|
|
|
|
// some files
|
2014-07-06 21:18:00 +02:00
|
|
|
set_state(torrent_status::checking_files);
|
2015-09-20 03:06:56 +02:00
|
|
|
if (should_check_files()) start_checking();
|
2008-07-02 23:23:02 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// start the checking right away (potentially)
|
|
|
|
m_ses.trigger_auto_manage();
|
|
|
|
}
|
2016-02-21 06:38:34 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
files_checked();
|
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
maybe_done_flushing();
|
2016-06-26 18:13:08 +02:00
|
|
|
TORRENT_ASSERT(m_outstanding_check_files == false);
|
2016-02-12 23:50:29 +01:00
|
|
|
m_add_torrent_params.reset();
|
2009-02-08 02:29:09 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// restore m_need_save_resume_data to its state when we entered this
|
|
|
|
// function.
|
|
|
|
m_need_save_resume_data = need_save_resume_data;
|
2009-02-08 02:29:09 +01:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2009-02-08 02:29:09 +01:00
|
|
|
|
2008-06-07 18:24:56 +02:00
|
|
|
void torrent::force_recheck()
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2009-11-29 18:57:42 +01:00
|
|
|
if (!valid_metadata()) return;
|
|
|
|
|
2008-11-19 01:46:48 +01:00
|
|
|
// if the torrent is already queued to check its files
|
|
|
|
// don't do anything
|
|
|
|
if (should_check_files()
|
|
|
|
|| m_state == torrent_status::checking_resume_data)
|
2008-06-17 16:56:22 +02:00
|
|
|
return;
|
|
|
|
|
2009-02-23 02:21:19 +01:00
|
|
|
clear_error();
|
2008-11-19 01:46:48 +01:00
|
|
|
|
2015-02-15 06:17:09 +01:00
|
|
|
disconnect_all(errors::stopping_torrent, op_bittorrent);
|
2010-02-18 04:53:16 +01:00
|
|
|
stop_announcing();
|
2008-06-07 18:24:56 +02:00
|
|
|
|
2015-07-21 19:12:37 +02:00
|
|
|
// we're checking everything anyway, no point in assuming we are a seed
|
|
|
|
// now.
|
|
|
|
leave_seed_mode(true);
|
|
|
|
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_release_files(m_storage);
|
2010-02-23 17:26:24 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// forget that we have any pieces
|
|
|
|
m_have_all = false;
|
|
|
|
|
|
|
|
// removing the piece picker will clear the user priorities
|
|
|
|
// instead, just clear which pieces we have
|
|
|
|
if (m_picker)
|
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
int const blocks_per_piece = (m_torrent_file->piece_length() + block_size() - 1) / block_size();
|
|
|
|
int const blocks_in_last_piece = ((m_torrent_file->total_size() % m_torrent_file->piece_length())
|
2014-07-06 21:18:00 +02:00
|
|
|
+ block_size() - 1) / block_size();
|
|
|
|
m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces());
|
|
|
|
}
|
|
|
|
|
|
|
|
// file progress is allocated lazily, the first time the client
|
|
|
|
// asks for it
|
2015-06-29 04:47:11 +02:00
|
|
|
m_file_progress.clear();
|
2013-03-16 23:16:58 +01:00
|
|
|
|
2008-06-07 18:24:56 +02:00
|
|
|
// assume that we don't have anything
|
|
|
|
m_files_checked = false;
|
2008-06-17 16:56:22 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
update_want_tick();
|
|
|
|
set_state(torrent_status::checking_resume_data);
|
2009-05-23 17:58:32 +02:00
|
|
|
|
2009-06-10 10:30:55 +02:00
|
|
|
if (m_auto_managed && !is_finished())
|
2008-06-17 16:56:22 +02:00
|
|
|
set_queue_position((std::numeric_limits<int>::max)());
|
2008-06-07 18:24:56 +02:00
|
|
|
|
2016-06-26 18:13:08 +02:00
|
|
|
TORRENT_ASSERT(m_outstanding_check_files == false);
|
2016-02-12 23:50:29 +01:00
|
|
|
m_add_torrent_params.reset();
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
aux::vector<std::string, file_index_t> links;
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_check_files(m_storage, nullptr
|
2016-05-25 06:31:52 +02:00
|
|
|
, links, std::bind(&torrent::on_force_recheck
|
2016-11-23 01:03:27 +01:00
|
|
|
, shared_from_this(), _1, _2));
|
2008-06-07 18:24:56 +02:00
|
|
|
}
|
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
void torrent::on_force_recheck(status_t const status, storage_error const& error) try
|
2008-06-07 18:24:56 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// hold a reference until this function returns
|
2011-12-24 21:12:34 +01:00
|
|
|
state_updated();
|
|
|
|
|
2016-03-02 07:16:08 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2016-11-23 01:03:27 +01:00
|
|
|
if (error)
|
2008-06-07 18:24:56 +02:00
|
|
|
{
|
2016-11-23 01:03:27 +01:00
|
|
|
handle_disk_error("force_recheck", error);
|
2008-06-07 18:24:56 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-11-26 07:51:47 +01:00
|
|
|
if (status == status_t::no_error)
|
2009-04-10 09:22:27 +02:00
|
|
|
{
|
|
|
|
// if there are no files, just start
|
2010-07-14 06:16:38 +02:00
|
|
|
files_checked();
|
2009-04-10 09:22:27 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-20 07:59:24 +01:00
|
|
|
m_progress_ppm = 0;
|
2016-12-22 16:42:33 +01:00
|
|
|
m_checking_piece = piece_index_t(0);
|
|
|
|
m_num_checked_pieces = piece_index_t(0);
|
2016-02-20 07:59:24 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
set_state(torrent_status::checking_files);
|
|
|
|
if (m_auto_managed) pause(true);
|
|
|
|
if (should_check_files()) start_checking();
|
|
|
|
else m_ses.trigger_auto_manage();
|
2009-04-10 09:22:27 +02:00
|
|
|
}
|
2008-06-07 18:24:56 +02:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2008-06-07 18:24:56 +02:00
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
void torrent::start_checking()
|
|
|
|
{
|
2008-11-19 01:46:48 +01:00
|
|
|
TORRENT_ASSERT(should_check_files());
|
2009-01-17 20:00:09 +01:00
|
|
|
|
2015-05-16 08:33:37 +02:00
|
|
|
int num_outstanding = settings().get_int(settings_pack::checking_mem_usage) * block_size()
|
2014-07-06 21:18:00 +02:00
|
|
|
/ m_torrent_file->piece_length();
|
2016-03-06 22:31:18 +01:00
|
|
|
// if we only keep a single read operation in-flight at a time, we suffer
|
|
|
|
// significant performance degradation. Always keep at least two jobs
|
|
|
|
// outstanding
|
|
|
|
if (num_outstanding < 2) num_outstanding = 2;
|
2011-12-25 13:16:04 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// we might already have some outstanding jobs, if we were paused and
|
|
|
|
// resumed quickly, before the outstanding jobs completed
|
2016-12-22 16:42:33 +01:00
|
|
|
if (m_checking_piece >= m_torrent_file->end_piece())
|
2008-06-09 06:46:34 +02:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
debug_log("start_checking, checking_piece >= num_pieces. %d >= %d"
|
2016-12-22 16:42:33 +01:00
|
|
|
, static_cast<int>(m_checking_piece), m_torrent_file->num_pieces());
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2008-06-09 06:46:34 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// subtract the number of pieces we already have outstanding
|
2016-12-22 16:42:33 +01:00
|
|
|
num_outstanding -= (static_cast<int>(m_checking_piece)
|
|
|
|
- static_cast<int>(m_num_checked_pieces));
|
2014-07-06 21:18:00 +02:00
|
|
|
if (num_outstanding < 0) num_outstanding = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < num_outstanding; ++i)
|
2011-07-24 18:18:54 +02:00
|
|
|
{
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_hash(m_storage, m_checking_piece
|
2016-11-26 07:51:47 +01:00
|
|
|
, disk_interface::sequential_access | disk_interface::volatile_read
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_piece_hashed
|
2016-11-26 07:51:47 +01:00
|
|
|
, shared_from_this(), _1, _2, _3), reinterpret_cast<void*>(1));
|
2016-12-22 16:42:33 +01:00
|
|
|
++m_checking_piece;
|
|
|
|
if (m_checking_piece >= m_torrent_file->end_piece()) break;
|
2011-07-24 18:18:54 +02:00
|
|
|
}
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-12-22 16:42:33 +01:00
|
|
|
debug_log("start_checking, m_checking_piece: %d"
|
|
|
|
, static_cast<int>(m_checking_piece));
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
2015-06-30 02:40:32 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// This is only used for checking of torrents. i.e. force-recheck or initial checking
|
|
|
|
// of existing files
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::on_piece_hashed(piece_index_t const piece
|
2016-11-21 05:58:48 +01:00
|
|
|
, sha1_hash const& piece_hash, storage_error const& error) try
|
2004-02-26 01:27:06 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
2010-07-17 09:13:14 +02:00
|
|
|
|
2016-03-02 07:16:08 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
state_updated();
|
2004-02-26 01:27:06 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
++m_num_checked_pieces;
|
2007-04-11 19:22:19 +02:00
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
if (error)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-11-21 05:58:48 +01:00
|
|
|
if (error.ec == boost::system::errc::no_such_file_or_directory
|
|
|
|
|| error.ec == boost::asio::error::eof
|
2014-07-20 11:27:02 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2016-11-21 05:58:48 +01:00
|
|
|
|| error.ec == error_code(ERROR_HANDLE_EOF, system_category())
|
2014-07-20 11:27:02 +02:00
|
|
|
#endif
|
|
|
|
)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(error.file() >= file_index_t(0));
|
2014-10-13 01:49:51 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// skip this file by updating m_checking_piece to the first piece following it
|
|
|
|
file_storage const& st = m_torrent_file->files();
|
2016-12-22 16:42:33 +01:00
|
|
|
std::uint64_t file_size = st.file_size(error.file());
|
|
|
|
piece_index_t last = st.map_file(error.file(), file_size, 0).piece;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_checking_piece < last)
|
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
int diff = static_cast<int>(last) - static_cast<int>(m_checking_piece);
|
|
|
|
m_num_checked_pieces = piece_index_t(static_cast<int>(m_num_checked_pieces) + diff);
|
|
|
|
m_checking_piece = last;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
m_checking_piece = piece_index_t{0};
|
|
|
|
m_num_checked_pieces = piece_index_t{0};
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<file_error_alert>())
|
2016-11-21 05:58:48 +01:00
|
|
|
m_ses.alerts().emplace_alert<file_error_alert>(error.ec,
|
2016-12-22 16:42:33 +01:00
|
|
|
resolve_filename(error.file()), error.operation_str(), get_handle());
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
2016-11-21 05:58:48 +01:00
|
|
|
{
|
|
|
|
debug_log("on_piece_hashed, fatal disk error: (%d) %s", error.ec.value()
|
|
|
|
, error.ec.message().c_str());
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
auto_managed(false);
|
|
|
|
pause();
|
2016-12-22 16:42:33 +01:00
|
|
|
set_error(error.ec, error.file());
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// recalculate auto-managed torrents sooner
|
|
|
|
// in order to start checking the next torrent
|
|
|
|
m_ses.trigger_auto_manage();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2010-02-05 09:23:17 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
m_progress_ppm = std::uint32_t(std::int64_t(static_cast<int>(m_num_checked_pieces))
|
|
|
|
* 1000000 / torrent_file().num_pieces());
|
2010-03-29 02:34:04 +02:00
|
|
|
|
2015-05-16 08:33:37 +02:00
|
|
|
if (settings().get_bool(settings_pack::disable_hash_checks)
|
2016-11-21 05:58:48 +01:00
|
|
|
|| piece_hash == m_torrent_file->hash_for_piece(piece))
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (has_picker() || !m_have_all)
|
|
|
|
{
|
|
|
|
need_picker();
|
2016-11-21 05:58:48 +01:00
|
|
|
m_picker->we_have(piece);
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
}
|
2016-11-21 05:58:48 +01:00
|
|
|
we_have(piece);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if the hash failed, remove it from the cache
|
|
|
|
if (m_storage)
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().clear_piece(m_storage, piece);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
if (m_num_checked_pieces < m_torrent_file->end_piece())
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
// we're not done yet, issue another job
|
2016-12-22 16:42:33 +01:00
|
|
|
if (m_checking_piece >= m_torrent_file->end_piece())
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
// actually, we already have outstanding jobs for
|
|
|
|
// the remaining pieces. We just need to wait for them
|
|
|
|
// to finish
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we paused the checking
|
|
|
|
if (!should_check_files())
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
debug_log("on_piece_hashed, checking paused");
|
|
|
|
#endif
|
2015-09-20 03:06:56 +02:00
|
|
|
if (m_checking_piece == m_num_checked_pieces)
|
|
|
|
{
|
|
|
|
// we are paused, and we just completed the last outstanding job.
|
|
|
|
// now we can be considered paused
|
|
|
|
if (alerts().should_post<torrent_paused_alert>())
|
|
|
|
alerts().emplace_alert<torrent_paused_alert>(get_handle());
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_hash(m_storage, m_checking_piece
|
2016-11-26 07:51:47 +01:00
|
|
|
, disk_interface::sequential_access | disk_interface::volatile_read
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_piece_hashed
|
2016-11-26 07:51:47 +01:00
|
|
|
, shared_from_this(), _1, _2, _3), reinterpret_cast<void*>(1));
|
2016-12-22 16:42:33 +01:00
|
|
|
++m_checking_piece;
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-12-22 16:42:33 +01:00
|
|
|
debug_log("on_piece_hashed, m_checking_piece: %d"
|
|
|
|
, static_cast<int>(m_checking_piece));
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
debug_log("on_piece_hashed, completed");
|
|
|
|
#endif
|
2015-09-20 03:06:56 +02:00
|
|
|
if (m_auto_managed)
|
|
|
|
{
|
|
|
|
// if we're auto managed. assume we need to be paused until the auto
|
|
|
|
// managed logic runs again (which is triggered further down)
|
2016-03-07 03:42:18 +01:00
|
|
|
// setting flags to 0 prevents the disk cache from being evicted as a
|
|
|
|
// result of this
|
2016-06-01 07:05:32 +02:00
|
|
|
set_paused(true, 0);
|
2015-09-20 03:06:56 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-09-20 03:06:56 +02:00
|
|
|
// we're done checking! (this should cause a call to trigger_auto_manage)
|
|
|
|
files_checked();
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// reset the checking state
|
2016-12-22 16:42:33 +01:00
|
|
|
m_checking_piece = piece_index_t(0);
|
|
|
|
m_num_checked_pieces = piece_index_t(0);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-01-01 21:25:39 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::use_interface(std::string net_interfaces)
|
|
|
|
{
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<settings_pack> p = std::make_shared<settings_pack>();
|
2014-07-06 21:18:00 +02:00
|
|
|
p->set_str(settings_pack::outgoing_interfaces, net_interfaces);
|
|
|
|
m_ses.apply_settings_pack(p);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
void torrent::on_tracker_announce(error_code const& ec) try
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-06 06:08:14 +02:00
|
|
|
COMPLETE_ASYNC("tracker::on_tracker_announce");
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-10-06 06:08:14 +02:00
|
|
|
TORRENT_ASSERT(m_waiting_tracker > 0);
|
|
|
|
--m_waiting_tracker;
|
|
|
|
if (ec) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_abort) return;
|
|
|
|
announce_with_tracker();
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
void torrent::lsd_announce()
|
|
|
|
{
|
|
|
|
if (m_abort) return;
|
|
|
|
|
|
|
|
// if the files haven't been checked yet, we're
|
|
|
|
// not ready for peers. Except, if we don't have metadata,
|
|
|
|
// we need peers to download from
|
|
|
|
if (!m_files_checked && valid_metadata()) return;
|
|
|
|
|
|
|
|
if (!m_announce_to_lsd) return;
|
|
|
|
|
|
|
|
// private torrents are never announced on LSD
|
|
|
|
if (m_torrent_file->is_valid() && m_torrent_file->priv()) return;
|
|
|
|
|
|
|
|
// i2p torrents are also never announced on LSD
|
|
|
|
// unless we allow mixed swarms
|
|
|
|
if (m_torrent_file->is_valid()
|
|
|
|
&& (torrent_file().is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (is_paused()) return;
|
|
|
|
|
|
|
|
if (!m_ses.has_lsd()) return;
|
|
|
|
|
|
|
|
// TODO: this pattern is repeated in a few places. Factor this into
|
|
|
|
// a function and generalize the concept of a torrent having a
|
|
|
|
// dedicated listen port
|
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
|
|
|
int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port();
|
2011-09-12 05:51:49 +02:00
|
|
|
#else
|
|
|
|
int port = m_ses.listen_port();
|
|
|
|
#endif
|
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
// announce with the local discovery service
|
2011-09-12 05:51:49 +02:00
|
|
|
m_ses.announce_lsd(m_torrent_file->info_hash(), port
|
2015-05-16 08:33:37 +02:00
|
|
|
, settings().get_bool(settings_pack::broadcast_lsd) && m_lsd_seq == 0);
|
2011-02-16 11:16:52 +01:00
|
|
|
++m_lsd_seq;
|
2007-04-04 04:06:07 +02:00
|
|
|
}
|
|
|
|
|
2006-08-01 17:27:08 +02:00
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
|
|
|
|
2010-02-14 02:39:55 +01:00
|
|
|
void torrent::dht_announce()
|
2010-02-05 09:23:17 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_ses.dht())
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
debug_log("DHT: no dht initialized");
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!should_announce_dht())
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
if (!m_ses.announce_dht())
|
|
|
|
debug_log("DHT: no listen sockets");
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-09-14 04:46:07 +02:00
|
|
|
if (m_torrent_file->is_valid() && !m_files_checked)
|
|
|
|
debug_log("DHT: files not checked, skipping DHT announce");
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-09-14 04:46:07 +02:00
|
|
|
if (!m_announce_to_dht)
|
|
|
|
debug_log("DHT: queueing disabled DHT announce");
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-09-14 04:46:07 +02:00
|
|
|
if (m_paused)
|
|
|
|
debug_log("DHT: torrent paused, no DHT announce");
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2016-09-14 04:46:07 +02:00
|
|
|
// deprecated in 1.2
|
|
|
|
if (!m_torrent_file->is_valid() && !m_url.empty())
|
|
|
|
debug_log("DHT: no info-hash, waiting for \"%s\"", m_url.c_str());
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-09-14 04:46:07 +02:00
|
|
|
if (m_torrent_file->is_valid() && m_torrent_file->priv())
|
|
|
|
debug_log("DHT: private torrent, no DHT announce");
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-09-14 04:46:07 +02:00
|
|
|
if (settings().get_bool(settings_pack::use_dht_as_fallback))
|
|
|
|
{
|
|
|
|
int verified_trackers = 0;
|
|
|
|
for (std::vector<announce_entry>::const_iterator i = m_trackers.begin()
|
|
|
|
, end(m_trackers.end()); i != end; ++i)
|
|
|
|
if (i->verified) ++verified_trackers;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-09-14 04:46:07 +02:00
|
|
|
if (verified_trackers > 0)
|
|
|
|
debug_log("DHT: only using DHT as fallback, and there are %d working trackers", verified_trackers);
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2010-02-05 09:23:17 +01:00
|
|
|
|
2016-06-01 07:05:32 +02:00
|
|
|
TORRENT_ASSERT(!m_paused);
|
2010-03-29 02:34:04 +02:00
|
|
|
|
2011-09-12 05:51:49 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2012-01-14 17:04:25 +01:00
|
|
|
int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port();
|
2011-09-12 05:51:49 +02:00
|
|
|
#else
|
|
|
|
int port = m_ses.listen_port();
|
|
|
|
#endif
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
debug_log("START DHT announce");
|
2015-03-12 05:34:54 +01:00
|
|
|
m_dht_start_time = clock_type::now();
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2014-01-20 07:35:06 +01:00
|
|
|
|
|
|
|
// if we're a seed, we tell the DHT for better scrape stats
|
|
|
|
int flags = is_seed() ? dht::dht_tracker::flag_seed : 0;
|
|
|
|
// if we allow incoming uTP connections, set the implied_port
|
|
|
|
// argument in the announce, this will make the DHT node use
|
|
|
|
// our source port in the packet as our listen port, which is
|
|
|
|
// likely more accurate when behind a NAT
|
2014-07-06 21:18:00 +02:00
|
|
|
if (settings().get_bool(settings_pack::enable_incoming_utp))
|
|
|
|
flags |= dht::dht_tracker::flag_implied_port;
|
2014-01-20 07:35:06 +01:00
|
|
|
|
2016-08-31 14:27:36 +02:00
|
|
|
std::weak_ptr<torrent> self(shared_from_this());
|
2014-07-06 21:18:00 +02:00
|
|
|
m_ses.dht()->announce(m_torrent_file->info_hash()
|
2014-01-20 07:35:06 +01:00
|
|
|
, port, flags
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_dht_announce_response_disp, self, _1));
|
2010-02-14 02:39:55 +01:00
|
|
|
}
|
|
|
|
|
2016-08-31 14:27:36 +02:00
|
|
|
void torrent::on_dht_announce_response_disp(std::weak_ptr<torrent> t
|
2006-08-29 03:15:24 +02:00
|
|
|
, std::vector<tcp::endpoint> const& peers)
|
|
|
|
{
|
2016-08-31 14:27:36 +02:00
|
|
|
std::shared_ptr<torrent> tor = t.lock();
|
2006-08-29 03:15:24 +02:00
|
|
|
if (!tor) return;
|
|
|
|
tor->on_dht_announce_response(peers);
|
|
|
|
}
|
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
void torrent::on_dht_announce_response(std::vector<tcp::endpoint> const& peers) try
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
debug_log("END DHT announce (%d ms) (%d peers)"
|
2015-03-12 05:34:54 +01:00
|
|
|
, int(total_milliseconds(clock_type::now() - m_dht_start_time))
|
2014-07-06 21:18:00 +02:00
|
|
|
, int(peers.size()));
|
|
|
|
#endif
|
|
|
|
|
2016-03-02 07:16:08 +01:00
|
|
|
if (m_abort) return;
|
2007-02-01 08:33:04 +01:00
|
|
|
if (peers.empty()) return;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<dht_reply_alert>())
|
2007-02-01 08:33:04 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<dht_reply_alert>(
|
2016-12-05 14:39:53 +01:00
|
|
|
get_handle(), int(peers.size()));
|
2007-02-01 08:33:04 +01:00
|
|
|
}
|
2009-08-20 05:19:12 +02:00
|
|
|
|
|
|
|
if (torrent_file().priv() || (torrent_file().is_i2p()
|
2014-07-06 21:18:00 +02:00
|
|
|
&& !settings().get_bool(settings_pack::allow_i2p_mixed))) return;
|
2009-08-20 05:19:12 +02:00
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
std::for_each(peers.begin(), peers.end(), std::bind(
|
2014-07-06 21:18:00 +02:00
|
|
|
&torrent::add_peer, this, _1, peer_info::dht, 0));
|
2012-11-03 04:50:12 +01:00
|
|
|
|
|
|
|
do_connect_boost();
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
update_want_peers();
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2006-08-01 17:27:08 +02:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
void torrent::announce_with_tracker(std::uint8_t e)
|
2008-07-12 10:25:19 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-07-12 10:25:19 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2012-09-28 01:50:15 +02:00
|
|
|
if (m_trackers.empty())
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-04-03 22:15:48 +02:00
|
|
|
debug_log("*** announce: no trackers");
|
2012-09-28 01:50:15 +02:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2008-08-11 16:31:58 +02:00
|
|
|
if (m_abort) e = tracker_request::stopped;
|
|
|
|
|
2010-03-29 02:34:04 +02:00
|
|
|
// if we're not announcing to trackers, only allow
|
|
|
|
// stopping
|
2012-09-28 01:50:15 +02:00
|
|
|
if (e != tracker_request::stopped && !m_announce_to_trackers)
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-04-03 22:15:48 +02:00
|
|
|
debug_log("*** announce: event != stopped && !m_announce_to_trackers");
|
2012-09-28 01:50:15 +02:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2010-03-29 02:34:04 +02:00
|
|
|
|
2013-11-09 07:51:11 +01:00
|
|
|
// if we're not allowing peers, there's no point in announcing
|
2016-06-01 07:05:32 +02:00
|
|
|
if (e != tracker_request::stopped && m_paused)
|
2013-11-09 07:51:11 +01:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-06-01 07:05:32 +02:00
|
|
|
debug_log("*** announce: event != stopped && m_paused");
|
2013-11-09 07:51:11 +01:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-01 07:05:32 +02:00
|
|
|
TORRENT_ASSERT(!m_paused || e == tracker_request::stopped);
|
2010-03-29 02:34:04 +02:00
|
|
|
|
2010-09-24 17:52:40 +02:00
|
|
|
if (e == tracker_request::none && is_finished() && !is_seed())
|
2010-09-06 06:02:15 +02:00
|
|
|
e = tracker_request::paused;
|
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
tracker_request req;
|
2015-05-16 08:33:37 +02:00
|
|
|
if (settings().get_bool(settings_pack::apply_ip_filter_to_trackers)
|
|
|
|
&& m_apply_ip_filter)
|
|
|
|
req.filter = m_ip_filter;
|
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
req.info_hash = m_torrent_file->info_hash();
|
|
|
|
req.pid = m_ses.get_peer_id();
|
2010-10-26 18:39:20 +02:00
|
|
|
req.downloaded = m_stat.total_payload_download() - m_total_failed_bytes;
|
2008-07-12 10:25:19 +02:00
|
|
|
req.uploaded = m_stat.total_payload_upload();
|
2010-02-18 07:45:07 +01:00
|
|
|
req.corrupt = m_total_failed_bytes;
|
2010-10-26 18:39:20 +02:00
|
|
|
req.left = bytes_left();
|
2008-07-12 10:25:19 +02:00
|
|
|
if (req.left == -1) req.left = 16*1024;
|
2011-08-28 23:06:15 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
|
|
|
// if this torrent contains an SSL certificate, make sure
|
|
|
|
// any SSL tracker presents a certificate signed by it
|
|
|
|
req.ssl_ctx = m_ssl_ctx.get();
|
|
|
|
#endif
|
2010-02-18 07:45:07 +01:00
|
|
|
|
2010-10-26 18:39:20 +02:00
|
|
|
// exclude redundant bytes if we should
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!settings().get_bool(settings_pack::report_true_downloaded))
|
2010-10-26 18:39:20 +02:00
|
|
|
req.downloaded -= m_total_redundant_bytes;
|
|
|
|
if (req.downloaded < 0) req.downloaded = 0;
|
2010-02-18 07:45:07 +01:00
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
req.event = e;
|
2016-04-07 06:06:04 +02:00
|
|
|
|
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
// since sending our IPv6 address to the tracker may be sensitive. Only
|
|
|
|
// do that if we're not in anonymous mode and if it's a private torrent
|
|
|
|
if (!settings().get_bool(settings_pack::anonymous_mode)
|
|
|
|
&& m_torrent_file
|
|
|
|
&& m_torrent_file->priv())
|
|
|
|
{
|
|
|
|
tcp::endpoint ep;
|
|
|
|
ep = m_ses.get_ipv6_interface();
|
|
|
|
if (ep != tcp::endpoint()) req.ipv6 = ep.address().to_v6();
|
|
|
|
}
|
|
|
|
#endif
|
2008-07-12 10:25:19 +02:00
|
|
|
|
|
|
|
// if we are aborting. we don't want any new peers
|
|
|
|
req.num_want = (req.event == tracker_request::stopped)
|
2016-05-05 23:09:11 +02:00
|
|
|
? 0 : settings().get_int(settings_pack::num_want);
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2016-12-10 20:15:25 +01:00
|
|
|
time_point const now = clock_type::now();
|
2008-11-29 09:38:40 +01:00
|
|
|
|
2009-06-28 22:21:55 +02:00
|
|
|
// the tier is kept as INT_MAX until we find the first
|
|
|
|
// tracker that works, then it's set to that tracker's
|
|
|
|
// tier.
|
2008-11-29 09:38:40 +01:00
|
|
|
int tier = INT_MAX;
|
2009-06-28 22:21:55 +02:00
|
|
|
|
|
|
|
// have we sent an announce in this tier yet?
|
|
|
|
bool sent_announce = false;
|
|
|
|
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto& ae : m_trackers)
|
2008-07-12 10:25:19 +02:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("*** tracker: \"%s\" "
|
|
|
|
"[ tiers: %d trackers: %d"
|
|
|
|
" i->tier: %d tier: %d"
|
|
|
|
" working: %d fails: %d limit: %d upd: %d"
|
|
|
|
" can: %d sent: %d ]"
|
|
|
|
, ae.url.c_str(), settings().get_bool(settings_pack::announce_to_all_tiers)
|
|
|
|
, settings().get_bool(settings_pack::announce_to_all_trackers)
|
|
|
|
, ae.tier, tier, ae.is_working(), ae.fails, ae.fail_limit
|
|
|
|
, ae.updating, ae.can_announce(now, is_seed()), sent_announce);
|
|
|
|
}
|
2011-08-18 01:01:35 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
if (settings().get_bool(settings_pack::announce_to_all_tiers)
|
|
|
|
&& !settings().get_bool(settings_pack::announce_to_all_trackers)
|
2009-06-28 22:21:55 +02:00
|
|
|
&& sent_announce
|
|
|
|
&& ae.tier <= tier
|
|
|
|
&& tier != INT_MAX)
|
|
|
|
continue;
|
|
|
|
|
2016-01-17 03:24:04 +01:00
|
|
|
// if trackerid is not specified for tracker use default one, probably set explicitly
|
|
|
|
req.trackerid = ae.trackerid.empty() ? m_trackerid : ae.trackerid;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (ae.tier > tier && sent_announce
|
|
|
|
&& !settings().get_bool(settings_pack::announce_to_all_tiers)) break;
|
2009-06-28 22:21:55 +02:00
|
|
|
if (ae.is_working()) { tier = ae.tier; sent_announce = false; }
|
2010-09-01 05:14:12 +02:00
|
|
|
if (!ae.can_announce(now, is_seed()))
|
2008-11-29 09:38:40 +01:00
|
|
|
{
|
2011-09-12 07:21:16 +02:00
|
|
|
// this counts
|
|
|
|
if (ae.is_working()) sent_announce = true;
|
2008-11-29 09:38:40 +01:00
|
|
|
continue;
|
|
|
|
}
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2008-11-29 09:38:40 +01:00
|
|
|
req.url = ae.url;
|
|
|
|
req.event = e;
|
|
|
|
if (req.event == tracker_request::none)
|
|
|
|
{
|
|
|
|
if (!ae.start_sent) req.event = tracker_request::started;
|
2010-02-18 04:53:16 +01:00
|
|
|
else if (!ae.complete_sent && is_seed()) req.event = tracker_request::completed;
|
2008-11-29 09:38:40 +01:00
|
|
|
}
|
|
|
|
|
2016-01-17 03:24:04 +01:00
|
|
|
req.triggered_manually = ae.triggered_manually;
|
|
|
|
ae.triggered_manually = false;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (settings().get_bool(settings_pack::force_proxy))
|
2010-04-13 06:30:34 +02:00
|
|
|
{
|
2013-02-19 07:48:53 +01:00
|
|
|
// in force_proxy mode we don't talk directly to trackers
|
2013-06-12 09:57:13 +02:00
|
|
|
// we only allow trackers if there is a proxy and issue
|
|
|
|
// a warning if there isn't one
|
2010-04-13 06:30:34 +02:00
|
|
|
std::string protocol = req.url.substr(0, req.url.find(':'));
|
2015-05-16 08:33:37 +02:00
|
|
|
int proxy_type = settings().get_int(settings_pack::proxy_type);
|
2015-08-14 05:06:59 +02:00
|
|
|
|
2013-06-12 09:57:13 +02:00
|
|
|
// http can run over any proxy, so as long as one is used
|
|
|
|
// it's OK. If no proxy is configured, skip this tracker
|
2010-04-13 06:30:34 +02:00
|
|
|
if ((protocol == "http" || protocol == "https")
|
2014-07-06 21:18:00 +02:00
|
|
|
&& proxy_type == settings_pack::none)
|
2010-04-13 06:30:34 +02:00
|
|
|
{
|
2010-05-03 10:24:30 +02:00
|
|
|
ae.next_announce = now + minutes(10);
|
2016-01-17 03:24:04 +01:00
|
|
|
if (m_ses.alerts().should_post<anonymous_mode_alert>()
|
|
|
|
|| req.triggered_manually)
|
2010-04-13 06:30:34 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<anonymous_mode_alert>(get_handle()
|
|
|
|
, anonymous_mode_alert::tracker_not_anonymous, req.url);
|
2010-04-13 06:30:34 +02:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-06-12 09:57:13 +02:00
|
|
|
// for UDP, only socks5 and i2p proxies will work.
|
|
|
|
// if we're not using one of those proxues with a UDP
|
|
|
|
// tracker, skip it
|
2010-04-13 06:30:34 +02:00
|
|
|
if (protocol == "udp"
|
2014-07-06 21:18:00 +02:00
|
|
|
&& proxy_type != settings_pack::socks5
|
|
|
|
&& proxy_type != settings_pack::socks5_pw
|
|
|
|
&& proxy_type != settings_pack::i2p_proxy)
|
2010-04-13 06:30:34 +02:00
|
|
|
{
|
2010-05-03 10:24:30 +02:00
|
|
|
ae.next_announce = now + minutes(10);
|
2016-01-17 03:24:04 +01:00
|
|
|
if (m_ses.alerts().should_post<anonymous_mode_alert>()
|
|
|
|
|| req.triggered_manually)
|
2010-04-13 06:30:34 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<anonymous_mode_alert>(get_handle()
|
|
|
|
, anonymous_mode_alert::tracker_not_anonymous, req.url);
|
2010-04-13 06:30:34 +02:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2015-02-01 15:30:43 +01:00
|
|
|
|
2015-12-13 21:14:19 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2015-02-01 15:30:43 +01:00
|
|
|
req.auth = tracker_login();
|
2015-12-13 21:14:19 +01:00
|
|
|
#endif
|
2015-02-01 15:30:43 +01:00
|
|
|
req.key = tracker_key();
|
|
|
|
|
2016-12-05 20:59:39 +01:00
|
|
|
#if TORRENT_USE_I2P
|
2015-08-28 13:20:21 +02:00
|
|
|
if (is_i2p())
|
|
|
|
{
|
|
|
|
req.kind |= tracker_request::i2p;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:50:15 +02:00
|
|
|
debug_log("==> TRACKER REQUEST \"%s\" event: %s abort: %d"
|
2012-09-28 01:04:51 +02:00
|
|
|
, req.url.c_str()
|
2016-12-15 20:20:08 +01:00
|
|
|
, (req.event == tracker_request::stopped ? "stopped"
|
|
|
|
: req.event == tracker_request::started ? "started" : "")
|
2012-09-28 01:04:51 +02:00
|
|
|
, m_abort);
|
|
|
|
|
2015-05-19 06:59:31 +02:00
|
|
|
// if we're not logging session logs, don't bother creating an
|
|
|
|
// observer object just for logging
|
2016-09-14 04:46:07 +02:00
|
|
|
if (m_abort && m_ses.should_log())
|
2008-11-29 09:38:40 +01:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
auto tl = std::make_shared<aux::tracker_logger>(m_ses);
|
2015-02-01 15:30:43 +01:00
|
|
|
m_ses.queue_tracker_request(req, tl);
|
2008-11-29 09:38:40 +01:00
|
|
|
}
|
|
|
|
else
|
2008-07-12 10:25:19 +02:00
|
|
|
#endif
|
2012-09-28 01:04:51 +02:00
|
|
|
{
|
2015-02-01 15:30:43 +01:00
|
|
|
m_ses.queue_tracker_request(req, shared_from_this());
|
2012-09-28 01:04:51 +02:00
|
|
|
}
|
|
|
|
|
2008-11-29 09:38:40 +01:00
|
|
|
ae.updating = true;
|
2010-07-18 01:32:17 +02:00
|
|
|
ae.next_announce = now + seconds(20);
|
|
|
|
ae.min_announce = now + seconds(10);
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<tracker_announce_alert>())
|
2008-11-29 09:38:40 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<tracker_announce_alert>(
|
|
|
|
get_handle(), req.url, req.event);
|
2008-11-29 09:38:40 +01:00
|
|
|
}
|
|
|
|
|
2009-06-28 22:21:55 +02:00
|
|
|
sent_announce = true;
|
|
|
|
if (ae.is_working()
|
2014-07-06 21:18:00 +02:00
|
|
|
&& !settings().get_bool(settings_pack::announce_to_all_trackers)
|
|
|
|
&& !settings().get_bool(settings_pack::announce_to_all_tiers))
|
2009-06-28 22:21:55 +02:00
|
|
|
break;
|
2008-07-12 10:25:19 +02:00
|
|
|
}
|
2010-05-03 10:24:30 +02:00
|
|
|
update_tracker_timer(now);
|
2008-07-12 10:25:19 +02:00
|
|
|
}
|
|
|
|
|
2016-01-25 06:51:20 +01:00
|
|
|
void torrent::scrape_tracker(int idx, bool user_triggered)
|
2007-11-20 23:46:27 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-11-10 23:08:32 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2014-08-27 08:36:28 +02:00
|
|
|
m_last_scrape = m_ses.session_time();
|
2016-11-10 23:08:32 +01:00
|
|
|
#endif
|
2009-12-02 05:05:24 +01:00
|
|
|
|
2007-11-20 23:46:27 +01:00
|
|
|
if (m_trackers.empty()) return;
|
|
|
|
|
2016-01-25 06:51:20 +01:00
|
|
|
if (idx < 0 || idx >= int(m_trackers.size())) idx = m_last_working_tracker;
|
|
|
|
if (idx < 0) idx = 0;
|
2015-05-19 06:59:31 +02:00
|
|
|
|
2007-11-20 23:46:27 +01:00
|
|
|
tracker_request req;
|
2015-05-16 08:33:37 +02:00
|
|
|
if (settings().get_bool(settings_pack::apply_ip_filter_to_trackers)
|
|
|
|
&& m_apply_ip_filter)
|
|
|
|
req.filter = m_ip_filter;
|
|
|
|
|
2007-11-20 23:46:27 +01:00
|
|
|
req.info_hash = m_torrent_file->info_hash();
|
2015-06-27 23:30:00 +02:00
|
|
|
req.kind |= tracker_request::scrape_request;
|
2016-01-25 06:51:20 +01:00
|
|
|
req.url = m_trackers[idx].url;
|
2015-12-13 21:14:19 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2015-02-01 15:30:43 +01:00
|
|
|
req.auth = tracker_login();
|
2015-12-13 21:14:19 +01:00
|
|
|
#endif
|
2015-02-01 15:30:43 +01:00
|
|
|
req.key = tracker_key();
|
2016-01-17 03:24:04 +01:00
|
|
|
req.triggered_manually = user_triggered;
|
2015-02-01 15:30:43 +01:00
|
|
|
m_ses.queue_tracker_request(req, shared_from_this());
|
2007-11-20 23:46:27 +01:00
|
|
|
}
|
|
|
|
|
2008-04-23 03:54:21 +02:00
|
|
|
void torrent::tracker_warning(tracker_request const& req, std::string const& msg)
|
2005-08-11 01:32:39 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-03-31 00:00:26 +02:00
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-10-07 21:36:40 +02:00
|
|
|
announce_entry* ae = find_tracker(req.url);
|
2015-11-25 05:38:19 +01:00
|
|
|
if (ae)
|
|
|
|
{
|
|
|
|
ae->message = msg;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<tracker_warning_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<tracker_warning_alert>(get_handle(), req.url, msg);
|
2005-08-11 01:32:39 +02:00
|
|
|
}
|
2015-06-22 04:44:01 +02:00
|
|
|
|
|
|
|
void torrent::tracker_scrape_response(tracker_request const& req
|
|
|
|
, int complete, int incomplete, int downloaded, int /* downloaders */)
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2015-06-22 04:44:01 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2015-06-27 23:30:00 +02:00
|
|
|
TORRENT_ASSERT(0 != (req.kind & tracker_request::scrape_request));
|
2015-06-22 04:44:01 +02:00
|
|
|
|
2016-10-07 21:36:40 +02:00
|
|
|
announce_entry* ae = find_tracker(req.url);
|
2013-02-04 08:17:31 +01:00
|
|
|
if (ae)
|
|
|
|
{
|
|
|
|
if (incomplete >= 0) ae->scrape_incomplete = incomplete;
|
|
|
|
if (complete >= 0) ae->scrape_complete = complete;
|
|
|
|
if (downloaded >= 0) ae->scrape_downloaded = downloaded;
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2013-02-04 08:17:31 +01:00
|
|
|
update_scrape_state();
|
|
|
|
}
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2016-01-17 03:24:04 +01:00
|
|
|
// if this was triggered manually we need to post this unconditionally,
|
|
|
|
// since the client expects a response from its action, regardless of
|
|
|
|
// whether all tracker events have been enabled by the alert mask
|
|
|
|
if (m_ses.alerts().should_post<scrape_reply_alert>()
|
|
|
|
|| req.triggered_manually)
|
2011-11-15 03:34:00 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<scrape_reply_alert>(
|
|
|
|
get_handle(), incomplete, complete, req.url);
|
2011-11-15 03:34:00 +01:00
|
|
|
}
|
|
|
|
}
|
2013-02-04 08:17:31 +01:00
|
|
|
|
|
|
|
void torrent::update_scrape_state()
|
|
|
|
{
|
|
|
|
// loop over all trackers and find the largest numbers for each scrape field
|
|
|
|
// then update the torrent-wide understanding of number of downloaders and seeds
|
|
|
|
int complete = -1;
|
|
|
|
int incomplete = -1;
|
|
|
|
int downloaded = -1;
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const& t : m_trackers)
|
2013-02-04 08:17:31 +01:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
complete = (std::max)(t.scrape_complete, complete);
|
|
|
|
incomplete = (std::max)(t.scrape_incomplete, incomplete);
|
|
|
|
downloaded = (std::max)(t.scrape_downloaded, downloaded);
|
2013-02-04 08:17:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((complete >= 0 && m_complete != complete)
|
|
|
|
|| (incomplete >= 0 && m_incomplete != incomplete)
|
|
|
|
|| (downloaded >= 0 && m_downloaded != downloaded))
|
|
|
|
state_updated();
|
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
if (m_complete != complete
|
|
|
|
|| m_incomplete != incomplete
|
|
|
|
|| m_downloaded != downloaded)
|
|
|
|
{
|
|
|
|
m_complete = complete;
|
|
|
|
m_incomplete = incomplete;
|
|
|
|
m_downloaded = downloaded;
|
2014-07-13 00:03:29 +02:00
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
update_auto_sequential();
|
2014-09-24 02:02:00 +02:00
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
// these numbers are cached in the resume data
|
|
|
|
set_need_save_resume();
|
|
|
|
}
|
2013-02-04 08:17:31 +01:00
|
|
|
}
|
2015-06-30 02:40:32 +02:00
|
|
|
|
2004-01-21 14:16:11 +01:00
|
|
|
void torrent::tracker_response(
|
2007-11-21 00:19:30 +01:00
|
|
|
tracker_request const& r
|
2009-05-15 23:23:41 +02:00
|
|
|
, address const& tracker_ip // this is the IP we connected to
|
|
|
|
, std::list<address> const& tracker_ips // these are all the IPs it resolved to
|
2014-09-28 08:36:03 +02:00
|
|
|
, struct tracker_response const& resp)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2007-03-31 00:00:26 +02:00
|
|
|
INVARIANT_CHECK;
|
2015-06-27 23:30:00 +02:00
|
|
|
TORRENT_ASSERT(0 == (r.kind & tracker_request::scrape_request));
|
2007-03-31 00:00:26 +02:00
|
|
|
|
2015-11-07 23:18:45 +01:00
|
|
|
// if the tracker told us what our external IP address is, record it with
|
|
|
|
// out external IP counter (and pass along the IP of the tracker to know
|
|
|
|
// who to attribute this vote to)
|
|
|
|
if (resp.external_ip != address() && !is_any(tracker_ip))
|
2014-09-28 08:36:03 +02:00
|
|
|
m_ses.set_external_address(resp.external_ip
|
2015-11-07 23:18:45 +01:00
|
|
|
, aux::session_interface::source_tracker, tracker_ip);
|
2008-03-29 23:45:55 +01:00
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point now = aux::time_now();
|
2008-08-02 00:34:37 +02:00
|
|
|
|
2014-09-28 08:36:03 +02:00
|
|
|
int interval = resp.interval;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (interval < settings().get_int(settings_pack::min_announce_interval))
|
|
|
|
interval = settings().get_int(settings_pack::min_announce_interval);
|
2004-01-25 13:37:15 +01:00
|
|
|
|
2016-10-07 21:36:40 +02:00
|
|
|
announce_entry* ae = find_tracker(r.url);
|
2008-11-29 09:38:40 +01:00
|
|
|
if (ae)
|
2008-11-27 21:48:29 +01:00
|
|
|
{
|
2014-09-28 08:36:03 +02:00
|
|
|
if (resp.incomplete >= 0) ae->scrape_incomplete = resp.incomplete;
|
|
|
|
if (resp.complete >= 0) ae->scrape_complete = resp.complete;
|
|
|
|
if (resp.downloaded >= 0) ae->scrape_downloaded = resp.downloaded;
|
2008-11-29 09:38:40 +01:00
|
|
|
if (!ae->start_sent && r.event == tracker_request::started)
|
|
|
|
ae->start_sent = true;
|
|
|
|
if (!ae->complete_sent && r.event == tracker_request::completed)
|
|
|
|
ae->complete_sent = true;
|
|
|
|
ae->verified = true;
|
|
|
|
ae->updating = false;
|
|
|
|
ae->fails = 0;
|
|
|
|
ae->next_announce = now + seconds(interval);
|
2014-09-28 08:36:03 +02:00
|
|
|
ae->min_announce = now + seconds(resp.min_interval);
|
2016-12-05 14:39:53 +01:00
|
|
|
int tracker_index = int(ae - &m_trackers[0]);
|
2016-11-25 17:17:25 +01:00
|
|
|
m_last_working_tracker = std::int8_t(prioritize_tracker(tracker_index));
|
2010-11-18 06:51:52 +01:00
|
|
|
|
2014-09-28 08:36:03 +02:00
|
|
|
if ((!resp.trackerid.empty()) && (ae->trackerid != resp.trackerid))
|
2010-11-18 06:51:52 +01:00
|
|
|
{
|
2014-09-28 08:36:03 +02:00
|
|
|
ae->trackerid = resp.trackerid;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<trackerid_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<trackerid_alert>(get_handle()
|
|
|
|
, r.url, resp.trackerid);
|
2010-11-18 06:51:52 +01:00
|
|
|
}
|
2013-02-04 08:17:31 +01:00
|
|
|
|
|
|
|
update_scrape_state();
|
2008-11-27 21:48:29 +01:00
|
|
|
}
|
2010-05-03 10:24:30 +02:00
|
|
|
update_tracker_timer(now);
|
2003-10-23 18:55:52 +02:00
|
|
|
|
2016-11-10 23:08:32 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2014-09-28 08:36:03 +02:00
|
|
|
if (resp.complete >= 0 && resp.incomplete >= 0)
|
2014-08-27 08:36:28 +02:00
|
|
|
m_last_scrape = m_ses.session_time();
|
2016-11-10 23:08:32 +01:00
|
|
|
#endif
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2016-09-14 04:46:07 +02:00
|
|
|
std::string resolved_to;
|
|
|
|
for (std::list<address>::const_iterator i = tracker_ips.begin()
|
|
|
|
, end(tracker_ips.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
resolved_to += i->to_string();
|
|
|
|
resolved_to += ", ";
|
|
|
|
}
|
|
|
|
debug_log("TRACKER RESPONSE\n"
|
|
|
|
"interval: %d\n"
|
|
|
|
"external ip: %s\n"
|
|
|
|
"resolved to: %s\n"
|
|
|
|
"we connected to: %s\n"
|
|
|
|
"peers:"
|
|
|
|
, interval
|
|
|
|
, print_address(resp.external_ip).c_str()
|
|
|
|
, resolved_to.c_str()
|
|
|
|
, print_address(tracker_ip).c_str());
|
|
|
|
|
|
|
|
for (std::vector<peer_entry>::const_iterator i = resp.peers.begin();
|
|
|
|
i != resp.peers.end(); ++i)
|
|
|
|
{
|
|
|
|
debug_log(" %16s %5d %s %s", i->hostname.c_str(), i->port
|
|
|
|
, i->pid.is_all_zeros()?"":aux::to_hex(i->pid).c_str()
|
|
|
|
, identify_client(i->pid).c_str());
|
|
|
|
}
|
|
|
|
for (std::vector<ipv4_peer_entry>::const_iterator i = resp.peers4.begin();
|
|
|
|
i != resp.peers4.end(); ++i)
|
|
|
|
{
|
|
|
|
debug_log(" %s:%d", print_address(address_v4(i->ip)).c_str(), i->port);
|
|
|
|
}
|
2014-09-28 08:36:03 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
2016-09-14 04:46:07 +02:00
|
|
|
for (std::vector<ipv6_peer_entry>::const_iterator i = resp.peers6.begin();
|
|
|
|
i != resp.peers6.end(); ++i)
|
|
|
|
{
|
|
|
|
debug_log(" [%s]:%d", print_address(address_v6(i->ip)).c_str(), i->port);
|
|
|
|
}
|
2004-01-21 14:16:11 +01:00
|
|
|
#endif
|
2016-09-14 04:46:07 +02:00
|
|
|
}
|
2014-09-28 08:36:03 +02:00
|
|
|
#endif
|
|
|
|
|
2004-01-21 14:16:11 +01:00
|
|
|
// for each of the peers we got from the tracker
|
2014-09-28 08:36:03 +02:00
|
|
|
for (std::vector<peer_entry>::const_iterator i = resp.peers.begin();
|
|
|
|
i != resp.peers.end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-21 14:16:11 +01:00
|
|
|
// don't make connections to ourself
|
2006-04-25 23:04:48 +02:00
|
|
|
if (i->pid == m_ses.get_peer_id())
|
2004-01-21 14:16:11 +01:00
|
|
|
continue;
|
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_I2P
|
2016-08-26 18:36:09 +02:00
|
|
|
if (r.i2pconn && string_ends_with(i->hostname, ".i2p"))
|
2014-09-28 08:36:03 +02:00
|
|
|
{
|
2016-08-26 18:36:09 +02:00
|
|
|
// this is an i2p name, we need to use the SAM connection
|
2014-09-28 08:36:03 +02:00
|
|
|
// to do the name lookup
|
2016-08-26 18:36:09 +02:00
|
|
|
if (string_ends_with(i->hostname, ".b32.i2p"))
|
2015-06-28 00:11:50 +02:00
|
|
|
{
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("torrent::on_i2p_resolve");
|
2015-06-28 00:11:50 +02:00
|
|
|
r.i2pconn->async_name_lookup(i->hostname.c_str()
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_i2p_resolve
|
2015-06-28 00:11:50 +02:00
|
|
|
, shared_from_this(), _1, _2));
|
|
|
|
}
|
2015-08-28 03:48:52 +02:00
|
|
|
else
|
|
|
|
{
|
2015-06-28 00:11:50 +02:00
|
|
|
torrent_state st = get_peer_list_state();
|
|
|
|
need_peer_list();
|
2015-08-28 03:48:52 +02:00
|
|
|
if (m_peer_list->add_i2p_peer(i->hostname.c_str (), peer_info::tracker, 0, &st))
|
|
|
|
state_updated();
|
|
|
|
peers_erased(st.erased);
|
2015-06-28 00:11:50 +02:00
|
|
|
}
|
2008-04-07 04:51:21 +02:00
|
|
|
}
|
|
|
|
else
|
2014-09-28 08:36:03 +02:00
|
|
|
#endif
|
2008-04-07 04:51:21 +02:00
|
|
|
{
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("torrent::on_peer_name_lookup");
|
2014-12-17 03:44:27 +01:00
|
|
|
m_ses.async_resolve(i->hostname, resolver_interface::abort_on_shutdown
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_peer_name_lookup
|
2014-09-28 08:36:03 +02:00
|
|
|
, shared_from_this(), _1, _2, i->port));
|
2007-03-07 19:50:38 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2014-09-28 08:36:03 +02:00
|
|
|
|
|
|
|
// there are 2 reasons to allow local IPs to be returned from a
|
|
|
|
// non-local tracker
|
|
|
|
// 1. retrackers are popular in russia, where an ISP runs a tracker within
|
|
|
|
// the AS (but not on the local network) giving out peers only from the
|
|
|
|
// local network
|
|
|
|
// 2. it might make sense to have a tracker extension in the future where
|
|
|
|
// trackers records a peer's internal and external IP, and match up
|
|
|
|
// peers on the same local network
|
|
|
|
|
|
|
|
bool need_update = false;
|
|
|
|
for (std::vector<ipv4_peer_entry>::const_iterator i = resp.peers4.begin();
|
|
|
|
i != resp.peers4.end(); ++i)
|
|
|
|
{
|
|
|
|
tcp::endpoint a(address_v4(i->ip), i->port);
|
2016-06-20 17:32:06 +02:00
|
|
|
need_update |= bool(add_peer(a, peer_info::tracker) != nullptr);
|
2014-09-28 08:36:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
for (std::vector<ipv6_peer_entry>::const_iterator i = resp.peers6.begin();
|
|
|
|
i != resp.peers6.end(); ++i)
|
|
|
|
{
|
|
|
|
tcp::endpoint a(address_v6(i->ip), i->port);
|
2016-06-20 17:32:06 +02:00
|
|
|
need_update |= bool(add_peer(a, peer_info::tracker) != nullptr);
|
2014-09-28 08:36:03 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (need_update) state_updated();
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2016-01-17 03:24:04 +01:00
|
|
|
// post unconditionally if the announce was triggered manually
|
|
|
|
if (m_ses.alerts().should_post<tracker_reply_alert>()
|
|
|
|
|| r.triggered_manually)
|
2005-03-10 10:59:12 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<tracker_reply_alert>(
|
2016-12-05 14:39:53 +01:00
|
|
|
get_handle(), int(resp.peers.size() + resp.peers4.size())
|
2014-09-28 08:36:03 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
2016-12-05 14:39:53 +01:00
|
|
|
+ int(resp.peers6.size())
|
2014-09-28 08:36:03 +02:00
|
|
|
#endif
|
2015-04-03 22:15:48 +02:00
|
|
|
, r.url);
|
2005-03-10 10:59:12 +01:00
|
|
|
}
|
2009-05-15 23:23:41 +02:00
|
|
|
|
|
|
|
// we're listening on an interface type that was not used
|
|
|
|
// when talking to the tracker. If there is a matching interface
|
|
|
|
// type in the tracker IP list, make another tracker request
|
|
|
|
// using that interface
|
|
|
|
// in order to avoid triggering this case over and over, don't
|
|
|
|
// do it if the bind IP for the tracker request that just completed
|
|
|
|
// matches one of the listen interfaces, since that means this
|
|
|
|
// announce was the second one
|
|
|
|
|
2016-02-07 08:09:19 +01:00
|
|
|
// TODO: 3 instead of announcing once per IP version, announce once per
|
|
|
|
// listen interface (i.e. m_listen_sockets)
|
2014-07-06 21:18:00 +02:00
|
|
|
if (((!is_any(m_ses.get_ipv6_interface().address()) && tracker_ip.is_v4())
|
|
|
|
|| (!is_any(m_ses.get_ipv4_interface().address()) && tracker_ip.is_v6()))
|
|
|
|
&& r.bind_ip != m_ses.get_ipv4_interface().address()
|
2015-10-27 05:21:07 +01:00
|
|
|
&& r.bind_ip != m_ses.get_ipv6_interface().address())
|
2009-05-15 23:23:41 +02:00
|
|
|
{
|
2016-05-25 06:31:52 +02:00
|
|
|
auto i = std::find_if(tracker_ips.begin(), tracker_ips.end()
|
|
|
|
, [&] (address const& a) { return a.is_v4() != tracker_ip.is_v4(); });
|
2009-05-15 23:23:41 +02:00
|
|
|
if (i != tracker_ips.end())
|
|
|
|
{
|
2010-08-03 11:08:37 +02:00
|
|
|
// the tracker did resolve to a different type of address, so announce
|
2009-05-15 23:23:41 +02:00
|
|
|
// to that as well
|
|
|
|
|
2016-04-24 21:26:28 +02:00
|
|
|
// TODO 3: there's a bug when removing a torrent or shutting down the session,
|
2015-10-27 05:21:07 +01:00
|
|
|
// where the second announce is skipped (in this case, the one to the IPv6
|
|
|
|
// name). This should be fixed by generalizing the tracker list structure to
|
|
|
|
// separate the IPv6 and IPv4 addresses as conceptually separate trackers,
|
|
|
|
// and they should be announced to in parallel
|
|
|
|
|
|
|
|
tracker_request req = r;
|
2009-05-15 23:23:41 +02:00
|
|
|
// tell the tracker to bind to the opposite protocol type
|
2015-10-27 05:21:07 +01:00
|
|
|
req.bind_ip = tracker_ip.is_v4()
|
|
|
|
? m_ses.get_ipv6_interface().address()
|
|
|
|
: m_ses.get_ipv4_interface().address();
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("announce again using %s as the bind interface"
|
|
|
|
, print_address(req.bind_ip).c_str());
|
|
|
|
}
|
2009-05-15 23:23:41 +02:00
|
|
|
#endif
|
2015-10-27 05:21:07 +01:00
|
|
|
m_ses.queue_tracker_request(req, shared_from_this());
|
2009-05-15 23:23:41 +02:00
|
|
|
}
|
|
|
|
}
|
2010-12-17 04:10:56 +01:00
|
|
|
|
2012-11-03 04:50:12 +01:00
|
|
|
do_connect_boost();
|
2010-12-17 04:10:56 +01:00
|
|
|
|
2012-11-03 04:50:12 +01:00
|
|
|
state_updated();
|
|
|
|
}
|
|
|
|
|
2014-09-24 02:02:00 +02:00
|
|
|
void torrent::update_auto_sequential()
|
|
|
|
{
|
2015-05-16 08:33:37 +02:00
|
|
|
if (!settings().get_bool(settings_pack::auto_sequential))
|
2014-09-24 02:02:00 +02:00
|
|
|
{
|
|
|
|
m_auto_sequential = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (int(m_connections.size()) - m_num_connecting < 10)
|
|
|
|
{
|
|
|
|
// there are too few peers. Be conservative and don't assume it's
|
|
|
|
// well seeded until we can connect to more peers
|
|
|
|
m_auto_sequential = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there are at least 10 seeds, and there are 10 times more
|
|
|
|
// seeds than downloaders, enter sequential download mode
|
|
|
|
// (for performance)
|
|
|
|
int downloaders = num_downloaders();
|
|
|
|
int seeds = num_seeds();
|
|
|
|
m_auto_sequential = downloaders * 10 <= seeds
|
|
|
|
&& seeds > 9;
|
|
|
|
}
|
|
|
|
|
2012-11-03 04:50:12 +01:00
|
|
|
void torrent::do_connect_boost()
|
|
|
|
{
|
|
|
|
if (!m_need_connect_boost) return;
|
|
|
|
|
|
|
|
// this is the first tracker response for this torrent
|
|
|
|
// instead of waiting one second for session_impl::on_tick()
|
|
|
|
// to be called, connect to a few peers immediately
|
2014-10-03 22:56:57 +02:00
|
|
|
int conns = (std::min)(
|
2015-05-16 08:33:37 +02:00
|
|
|
settings().get_int(settings_pack::torrent_connect_boost)
|
|
|
|
, settings().get_int(settings_pack::connections_limit) - m_ses.num_connections());
|
2012-11-03 04:50:12 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (conns > 0) m_need_connect_boost = false;
|
|
|
|
|
|
|
|
// if we don't know of any peers
|
2014-10-26 08:34:31 +01:00
|
|
|
if (!m_peer_list) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
while (want_peers() && conns > 0)
|
2012-11-03 04:50:12 +01:00
|
|
|
{
|
|
|
|
--conns;
|
2015-06-30 01:56:39 +02:00
|
|
|
torrent_state st = get_peer_list_state();
|
2014-10-26 08:34:31 +01:00
|
|
|
torrent_peer* p = m_peer_list->connect_one_peer(m_ses.session_time(), &st);
|
2014-07-06 21:18:00 +02:00
|
|
|
peers_erased(st.erased);
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::connection_attempt_loops, st.loop_counter);
|
2016-06-20 17:32:06 +02:00
|
|
|
if (p == nullptr)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
update_want_peers();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
external_ip const& external = m_ses.external_address();
|
|
|
|
debug_log(" *** FOUND CONNECTION CANDIDATE ["
|
|
|
|
" ip: %s rank: %u external: %s t: %d ]"
|
|
|
|
, print_endpoint(p->ip()).c_str()
|
|
|
|
, p->rank(external, m_ses.listen_port())
|
|
|
|
, print_address(external.external_address(p->address())).c_str()
|
|
|
|
, int(m_ses.session_time() - p->last_connected));
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!connect_to_peer(p))
|
|
|
|
{
|
2014-10-26 08:34:31 +01:00
|
|
|
m_peer_list->inc_failcount(p);
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// increase m_ses.m_boost_connections for each connection
|
|
|
|
// attempt. This will be deducted from the connect speed
|
|
|
|
// the next time session_impl::on_tick() is triggered
|
|
|
|
m_ses.inc_boost_connections();
|
|
|
|
update_want_peers();
|
|
|
|
}
|
2010-12-17 04:10:56 +01:00
|
|
|
}
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (want_peers()) m_ses.prioritize_connections(shared_from_this());
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point torrent::next_announce() const
|
2009-12-13 17:36:45 +01:00
|
|
|
{
|
2016-10-06 06:08:14 +02:00
|
|
|
return m_waiting_tracker ? m_tracker_timer.expires_at() : min_time();
|
2009-12-13 17:36:45 +01:00
|
|
|
}
|
|
|
|
|
2016-01-17 03:24:04 +01:00
|
|
|
// this is the entry point for the client to force a re-announce. It's
|
|
|
|
// considered a client-initiated announce (as opposed to the regular ones,
|
|
|
|
// issued by libtorrent)
|
2016-09-14 04:46:07 +02:00
|
|
|
void torrent::force_tracker_request(time_point const t, int const tracker_idx)
|
2009-12-13 17:36:45 +01:00
|
|
|
{
|
|
|
|
if (is_paused()) return;
|
2014-01-02 03:16:31 +01:00
|
|
|
if (tracker_idx == -1)
|
|
|
|
{
|
2016-09-14 04:46:07 +02:00
|
|
|
for (auto& e : m_trackers)
|
2016-01-17 03:24:04 +01:00
|
|
|
{
|
2016-09-14 04:46:07 +02:00
|
|
|
e.next_announce = std::max(t, e.min_announce) + seconds(1);
|
|
|
|
e.triggered_manually = true;
|
2016-01-17 03:24:04 +01:00
|
|
|
}
|
2014-01-02 03:16:31 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(tracker_idx >= 0 && tracker_idx < int(m_trackers.size()));
|
|
|
|
if (tracker_idx < 0 || tracker_idx >= int(m_trackers.size()))
|
|
|
|
return;
|
|
|
|
announce_entry& e = m_trackers[tracker_idx];
|
2016-09-14 04:46:07 +02:00
|
|
|
e.next_announce = std::max(t, e.min_announce) + seconds(1);
|
2016-01-17 03:24:04 +01:00
|
|
|
e.triggered_manually = true;
|
2014-01-02 03:16:31 +01:00
|
|
|
}
|
2015-03-12 05:34:54 +01:00
|
|
|
update_tracker_timer(clock_type::now());
|
2009-12-13 17:36:45 +01:00
|
|
|
}
|
|
|
|
|
2015-12-13 21:14:19 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2009-12-13 17:36:45 +01:00
|
|
|
void torrent::set_tracker_login(
|
|
|
|
std::string const& name
|
|
|
|
, std::string const& pw)
|
|
|
|
{
|
|
|
|
m_username = name;
|
|
|
|
m_password = pw;
|
|
|
|
}
|
2015-12-13 21:14:19 +01:00
|
|
|
#endif
|
2009-12-13 17:36:45 +01:00
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_I2P
|
2016-10-23 04:00:47 +02:00
|
|
|
void torrent::on_i2p_resolve(error_code const& ec, char const* dest) try
|
2009-08-20 05:19:12 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2009-08-20 05:19:12 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-04-23 23:29:25 +02:00
|
|
|
COMPLETE_ASYNC("torrent::on_i2p_resolve");
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (ec && should_log())
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("i2p_resolve error: %s", ec.message().c_str());
|
2009-12-07 06:03:23 +01:00
|
|
|
#endif
|
2016-03-02 07:16:08 +01:00
|
|
|
if (ec || m_abort || m_ses.is_aborted()) return;
|
2009-08-20 05:19:12 +02:00
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
need_peer_list();
|
|
|
|
torrent_state st = get_peer_list_state();
|
2014-10-26 08:34:31 +01:00
|
|
|
if (m_peer_list->add_i2p_peer(dest, peer_info::tracker, 0, &st))
|
2014-07-06 21:18:00 +02:00
|
|
|
state_updated();
|
|
|
|
peers_erased(st.erased);
|
2009-08-20 05:19:12 +02:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2009-08-20 05:19:12 +02:00
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::on_peer_name_lookup(error_code const& e
|
2016-11-25 17:17:25 +01:00
|
|
|
, std::vector<address> const& host_list, int const port) try
|
2007-03-07 19:50:38 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-03-07 19:50:38 +01:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-04-23 23:29:25 +02:00
|
|
|
COMPLETE_ASYNC("torrent::on_peer_name_lookup");
|
2010-11-28 02:47:30 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (e && should_log())
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("peer name lookup error: %s", e.message().c_str());
|
2009-12-07 06:03:23 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-03-02 07:16:08 +01:00
|
|
|
if (e || m_abort || host_list.empty() || m_ses.is_aborted()) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2014-09-28 04:20:13 +02:00
|
|
|
// TODO: add one peer per IP the hostname resolves to
|
2016-11-25 17:17:25 +01:00
|
|
|
tcp::endpoint host(host_list.front(), std::uint16_t(port));
|
2007-03-07 19:50:38 +01:00
|
|
|
|
2015-05-16 08:33:37 +02:00
|
|
|
if (m_ip_filter && m_ip_filter->access(host.address()) & ip_filter::blocked)
|
2007-03-07 19:50:38 +01:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
debug_log("blocked ip from tracker: %s", host.address().to_string(ec).c_str());
|
|
|
|
}
|
2007-03-07 19:50:38 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<peer_blocked_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-05-23 01:56:14 +02:00
|
|
|
, host, peer_blocked_alert::ip_filter);
|
2007-03-07 19:50:38 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (add_peer(host, peer_info::tracker))
|
|
|
|
state_updated();
|
|
|
|
update_want_peers();
|
2007-03-07 19:50:38 +01:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2007-03-07 19:50:38 +01:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t 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;
|
2007-09-01 05:00:31 +02:00
|
|
|
return m_torrent_file->total_size()
|
2006-09-24 02:48:31 +02:00
|
|
|
- quantized_bytes_done();
|
|
|
|
}
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t torrent::quantized_bytes_done() const
|
2006-09-24 02:48:31 +02:00
|
|
|
{
|
2006-12-11 13:48:33 +01:00
|
|
|
// INVARIANT_CHECK;
|
2006-09-24 02:48:31 +02:00
|
|
|
|
|
|
|
if (!valid_metadata()) return 0;
|
|
|
|
|
2007-09-01 05:00:31 +02:00
|
|
|
if (m_torrent_file->num_pieces() == 0)
|
2006-09-24 02:48:31 +02:00
|
|
|
return 0;
|
2006-12-04 13:20:34 +01:00
|
|
|
|
2014-03-11 22:26:01 +01:00
|
|
|
// if any piece hash fails, we'll be taken out of seed mode
|
|
|
|
// and m_seed_mode will be false
|
|
|
|
if (m_seed_mode) return m_torrent_file->total_size();
|
|
|
|
|
2015-07-21 19:12:37 +02:00
|
|
|
if (!has_picker()) return m_have_all ? m_torrent_file->total_size() : 0;
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t const last_piece = prev(m_torrent_file->end_piece());
|
2006-09-24 02:48:31 +02:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t total_done
|
|
|
|
= std::uint64_t(m_picker->num_passed()) * m_torrent_file->piece_length();
|
2006-09-24 02:48:31 +02:00
|
|
|
|
|
|
|
// 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
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_picker->has_piece_passed(last_piece))
|
2006-09-24 02:48:31 +02:00
|
|
|
{
|
2007-09-01 05:00:31 +02:00
|
|
|
int corr = m_torrent_file->piece_size(last_piece)
|
|
|
|
- m_torrent_file->piece_length();
|
2006-09-24 02:48:31 +02:00
|
|
|
total_done += corr;
|
|
|
|
}
|
|
|
|
return total_done;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
// returns the number of bytes we are interested
|
2010-02-14 02:39:55 +01:00
|
|
|
// in for the given block. This returns block_size()
|
2009-01-14 08:41:25 +01:00
|
|
|
// for all blocks except the last one (if it's smaller
|
2010-02-14 02:39:55 +01:00
|
|
|
// than block_size()) and blocks that overlap a padding
|
2009-01-14 08:41:25 +01:00
|
|
|
// file
|
|
|
|
int torrent::block_bytes_wanted(piece_block const& p) const
|
|
|
|
{
|
|
|
|
file_storage const& fs = m_torrent_file->files();
|
|
|
|
int piece_size = m_torrent_file->piece_size(p.piece_index);
|
2010-02-14 02:39:55 +01:00
|
|
|
int offset = p.block_index * block_size();
|
2016-06-13 13:47:16 +02:00
|
|
|
if (m_padding == 0) return (std::min)(piece_size - offset, block_size());
|
2009-01-14 08:41:25 +01:00
|
|
|
|
|
|
|
std::vector<file_slice> files = fs.map_block(
|
2016-06-13 13:47:16 +02:00
|
|
|
p.piece_index, offset, (std::min)(piece_size - offset, block_size()));
|
2009-01-14 08:41:25 +01:00
|
|
|
int ret = 0;
|
|
|
|
for (std::vector<file_slice>::iterator i = files.begin()
|
|
|
|
, end(files.end()); i != end; ++i)
|
|
|
|
{
|
2012-09-30 22:55:12 +02:00
|
|
|
if (fs.pad_file_at(i->file_index)) continue;
|
2009-01-14 08:41:25 +01:00
|
|
|
ret += i->size;
|
|
|
|
}
|
2016-06-13 13:47:16 +02:00
|
|
|
TORRENT_ASSERT(ret <= (std::min)(piece_size - offset, block_size()));
|
2009-01-14 08:41:25 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fills in total_wanted, total_wanted_done and total_done
|
2010-03-04 20:15:23 +01:00
|
|
|
void torrent::bytes_done(torrent_status& st, bool accurate) const
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2006-05-28 21:03:54 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
st.total_done = 0;
|
|
|
|
st.total_wanted_done = 0;
|
|
|
|
st.total_wanted = m_torrent_file->total_size();
|
|
|
|
|
2009-01-19 10:52:08 +01:00
|
|
|
TORRENT_ASSERT(st.total_wanted >= m_padding);
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_wanted >= 0);
|
|
|
|
|
2007-09-01 05:00:31 +02:00
|
|
|
if (!valid_metadata() || m_torrent_file->num_pieces() == 0)
|
2009-01-14 08:41:25 +01:00
|
|
|
return;
|
2007-03-17 00:28:26 +01:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
TORRENT_ASSERT(st.total_wanted >= std::int64_t(m_torrent_file->piece_length())
|
2009-01-24 19:11:06 +01:00
|
|
|
* (m_torrent_file->num_pieces() - 1));
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t const last_piece = prev(m_torrent_file->end_piece());
|
|
|
|
int const piece_size = m_torrent_file->piece_length();
|
2004-01-15 17:45:34 +01:00
|
|
|
|
2014-03-13 09:00:53 +01:00
|
|
|
// if any piece hash fails, we'll be taken out of seed mode
|
|
|
|
// and m_seed_mode will be false
|
|
|
|
if (m_seed_mode || is_seed())
|
2009-01-14 08:41:25 +01:00
|
|
|
{
|
|
|
|
st.total_done = m_torrent_file->total_size() - m_padding;
|
|
|
|
st.total_wanted_done = st.total_done;
|
|
|
|
st.total_wanted = st.total_done;
|
|
|
|
return;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
else if (!has_picker())
|
|
|
|
{
|
|
|
|
st.total_done = 0;
|
|
|
|
st.total_wanted_done = 0;
|
|
|
|
st.total_wanted = m_torrent_file->total_size() - m_padding;
|
|
|
|
return;
|
|
|
|
}
|
2006-12-04 13:20:34 +01:00
|
|
|
|
2008-06-07 16:03:21 +02:00
|
|
|
TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered());
|
2016-06-18 20:01:38 +02:00
|
|
|
st.total_wanted_done = std::int64_t(num_passed() - m_picker->num_have_filtered())
|
2007-11-27 00:08:59 +01:00
|
|
|
* piece_size;
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_wanted_done >= 0);
|
2015-07-06 01:32:13 +02:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
st.total_done = std::int64_t(num_passed()) * piece_size;
|
2014-07-06 21:18:00 +02:00
|
|
|
// if num_passed() == num_pieces(), we should be a seed, and taken the
|
|
|
|
// branch above
|
|
|
|
TORRENT_ASSERT(num_passed() <= m_torrent_file->num_pieces());
|
2004-01-15 17:45:34 +01:00
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
int num_filtered_pieces = m_picker->num_filtered()
|
|
|
|
+ m_picker->num_have_filtered();
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t const last_piece_index = m_torrent_file->last_piece();
|
2009-01-14 08:41:25 +01:00
|
|
|
if (m_picker->piece_priority(last_piece_index) == 0)
|
|
|
|
{
|
|
|
|
st.total_wanted -= m_torrent_file->piece_size(last_piece_index);
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(st.total_wanted >= 0);
|
2009-01-14 08:41:25 +01:00
|
|
|
--num_filtered_pieces;
|
|
|
|
}
|
2016-06-18 20:01:38 +02:00
|
|
|
st.total_wanted -= std::int64_t(num_filtered_pieces) * piece_size;
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(st.total_wanted >= 0);
|
2015-07-06 01:32:13 +02:00
|
|
|
|
2004-01-15 17:45:34 +01:00
|
|
|
// 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
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_picker->has_piece_passed(last_piece))
|
2003-12-07 06:53:04 +01:00
|
|
|
{
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_done >= piece_size);
|
2007-09-01 05:00:31 +02:00
|
|
|
int corr = m_torrent_file->piece_size(last_piece)
|
2007-11-27 00:08:59 +01:00
|
|
|
- piece_size;
|
|
|
|
TORRENT_ASSERT(corr <= 0);
|
|
|
|
TORRENT_ASSERT(corr > -piece_size);
|
2009-01-14 08:41:25 +01:00
|
|
|
st.total_done += corr;
|
2007-03-15 23:03:56 +01:00
|
|
|
if (m_picker->piece_priority(last_piece) != 0)
|
2007-11-27 00:08:59 +01:00
|
|
|
{
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_wanted_done >= piece_size);
|
|
|
|
st.total_wanted_done += corr;
|
2007-11-27 00:08:59 +01:00
|
|
|
}
|
2003-12-07 06:53:04 +01:00
|
|
|
}
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_wanted >= st.total_wanted_done);
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2014-01-21 02:52:01 +01:00
|
|
|
// this is expensive, we might not want to do it all the time
|
|
|
|
if (!accurate) return;
|
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
// subtract padding files
|
2014-01-21 02:52:01 +01:00
|
|
|
if (m_padding > 0)
|
2009-01-14 08:41:25 +01:00
|
|
|
{
|
|
|
|
file_storage const& files = m_torrent_file->files();
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < files.end_file(); ++i)
|
2009-01-14 08:41:25 +01:00
|
|
|
{
|
2013-08-12 09:30:57 +02:00
|
|
|
if (!files.pad_file_at(i)) continue;
|
2016-12-05 14:39:53 +01:00
|
|
|
peer_request p = files.map_file(i, 0, int(files.file_size(i)));
|
2016-12-22 16:42:33 +01:00
|
|
|
for (piece_index_t j = p.piece; p.length > 0; ++j)
|
2009-01-14 08:41:25 +01:00
|
|
|
{
|
2012-02-17 08:19:54 +01:00
|
|
|
int deduction = (std::min)(p.length, piece_size - p.start);
|
2016-12-22 16:42:33 +01:00
|
|
|
bool const done = m_picker->has_piece_passed(j);
|
|
|
|
bool const wanted = m_picker->piece_priority(j) > 0;
|
2009-01-15 19:52:41 +01:00
|
|
|
if (done) st.total_done -= deduction;
|
|
|
|
if (wanted) st.total_wanted -= deduction;
|
|
|
|
if (wanted && done) st.total_wanted_done -= deduction;
|
2012-02-17 08:19:54 +01:00
|
|
|
TORRENT_ASSERT(st.total_done >= 0);
|
|
|
|
TORRENT_ASSERT(st.total_wanted >= 0);
|
|
|
|
TORRENT_ASSERT(st.total_wanted_done >= 0);
|
|
|
|
p.length -= piece_size - p.start;
|
|
|
|
p.start = 0;
|
|
|
|
++p.piece;
|
2009-01-14 08:41:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(!accurate || st.total_done <= m_torrent_file->total_size() - m_padding);
|
2009-01-15 19:52:41 +01:00
|
|
|
TORRENT_ASSERT(st.total_wanted_done >= 0);
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_done >= st.total_wanted_done);
|
2006-12-11 13:48:33 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
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
|
|
|
|
2010-02-14 02:39:55 +01:00
|
|
|
const int blocks_per_piece = (piece_size + block_size() - 1) / block_size();
|
2004-01-15 17:45:34 +01:00
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
// look at all unfinished pieces and add the completed
|
|
|
|
// blocks to our 'done' counter
|
2016-12-22 16:42:33 +01:00
|
|
|
for (auto i = 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;
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t const index = i->index;
|
2009-01-14 08:41:25 +01:00
|
|
|
// completed pieces are already accounted for
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_picker->has_piece_passed(index)) continue;
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(i->finished <= m_picker->blocks_in_piece(index));
|
2006-12-14 17:12:31 +01:00
|
|
|
|
2014-01-21 02:52:01 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(std::count_if(std::next(i), dl_queue.end()
|
|
|
|
, [index](piece_picker::downloading_piece const& p) { return p.index == index; }) == 0);
|
2006-12-14 17:12:31 +01:00
|
|
|
#endif
|
2004-01-15 17:45:34 +01:00
|
|
|
|
2015-02-14 22:32:41 +01:00
|
|
|
piece_picker::block_info* info = m_picker->blocks_for_piece(*i);
|
2004-01-15 17:45:34 +01:00
|
|
|
for (int j = 0; j < blocks_per_piece; ++j)
|
|
|
|
{
|
2009-09-09 18:34:50 +02:00
|
|
|
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(m_picker->is_finished(piece_block(index, j))
|
2015-02-14 22:32:41 +01:00
|
|
|
== (info[j].state == piece_picker::block_info::state_finished));
|
2009-09-09 18:34:50 +02:00
|
|
|
#endif
|
2015-02-14 22:32:41 +01:00
|
|
|
if (info[j].state == piece_picker::block_info::state_finished)
|
2009-01-14 08:41:25 +01:00
|
|
|
{
|
|
|
|
corr += block_bytes_wanted(piece_block(index, j));
|
|
|
|
}
|
2007-11-27 00:08:59 +01:00
|
|
|
TORRENT_ASSERT(corr >= 0);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(index != last_piece || j < m_picker->blocks_in_last_piece()
|
2015-02-14 22:32:41 +01:00
|
|
|
|| info[j].state != piece_picker::block_info::state_finished);
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
st.total_done += corr;
|
|
|
|
if (m_picker->piece_priority(index) > 0)
|
|
|
|
st.total_wanted_done += corr;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_wanted <= m_torrent_file->total_size() - m_padding);
|
|
|
|
TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding);
|
|
|
|
TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding);
|
2009-01-15 19:52:41 +01:00
|
|
|
TORRENT_ASSERT(st.total_wanted_done >= 0);
|
|
|
|
TORRENT_ASSERT(st.total_done >= st.total_wanted_done);
|
2006-12-15 13:29:47 +01:00
|
|
|
|
2004-09-16 03:14:16 +02:00
|
|
|
std::map<piece_block, int> downloading_piece;
|
2016-12-22 16:42:33 +01:00
|
|
|
for (auto pc : *this)
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2016-09-04 18:31:02 +02:00
|
|
|
piece_block_progress p = pc->downloading_piece_progress();
|
|
|
|
if (p.piece_index == piece_block_progress::invalid_index)
|
|
|
|
continue;
|
2004-09-16 03:14:16 +02:00
|
|
|
|
2016-09-04 18:31:02 +02:00
|
|
|
if (m_picker->has_piece_passed(p.piece_index))
|
2009-01-14 08:41:25 +01:00
|
|
|
continue;
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2016-09-04 18:31:02 +02:00
|
|
|
piece_block block(p.piece_index, p.block_index);
|
2009-01-14 08:41:25 +01:00
|
|
|
if (m_picker->is_finished(block))
|
|
|
|
continue;
|
|
|
|
|
2016-09-05 00:24:20 +02:00
|
|
|
auto dp = downloading_piece.find(block);
|
2009-01-14 08:41:25 +01:00
|
|
|
if (dp != downloading_piece.end())
|
|
|
|
{
|
2016-09-04 18:31:02 +02:00
|
|
|
if (dp->second < p.bytes_downloaded)
|
|
|
|
dp->second = p.bytes_downloaded;
|
2009-01-14 08:41:25 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-09-04 18:31:02 +02:00
|
|
|
downloading_piece[block] = p.bytes_downloaded;
|
2009-01-14 08:41:25 +01:00
|
|
|
}
|
2016-09-04 18:31:02 +02:00
|
|
|
TORRENT_ASSERT(p.bytes_downloaded <= p.full_block_bytes);
|
|
|
|
TORRENT_ASSERT(p.full_block_bytes == to_req(piece_block(
|
|
|
|
p.piece_index, p.block_index)).length);
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2016-09-05 00:24:20 +02:00
|
|
|
for (auto const& p : downloading_piece)
|
2005-05-30 19:43:03 +02:00
|
|
|
{
|
2016-09-05 00:24:20 +02:00
|
|
|
int done = (std::min)(block_bytes_wanted(p.first), p.second);
|
2009-01-14 08:41:25 +01:00
|
|
|
st.total_done += done;
|
2016-09-05 00:24:20 +02:00
|
|
|
if (m_picker->piece_priority(p.first.piece_index) != 0)
|
2009-01-14 08:41:25 +01:00
|
|
|
st.total_wanted_done += done;
|
2005-05-30 19:43:03 +02:00
|
|
|
}
|
2006-12-15 13:29:47 +01:00
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding);
|
|
|
|
TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding);
|
2008-06-07 04:58:28 +02:00
|
|
|
|
2016-07-02 01:46:59 +02:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2006-12-18 02:23:30 +01:00
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
if (st.total_done >= m_torrent_file->total_size())
|
2006-12-18 02:23:30 +01:00
|
|
|
{
|
2016-09-05 00:24:20 +02:00
|
|
|
// This happens when a piece has been downloaded completely
|
2007-10-01 04:09:12 +02:00
|
|
|
// but not yet verified against the hash
|
2016-05-17 15:24:06 +02:00
|
|
|
std::fprintf(stderr, "num_have: %d\nunfinished:\n", num_have());
|
2016-09-05 00:24:20 +02:00
|
|
|
for (auto const& dp : dl_queue)
|
2006-12-18 02:23:30 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
std::fprintf(stderr, " %d ", static_cast<int>(dp.index));
|
2016-09-05 00:24:20 +02:00
|
|
|
piece_picker::block_info* info = m_picker->blocks_for_piece(dp);
|
2006-12-18 02:23:30 +01:00
|
|
|
for (int j = 0; j < blocks_per_piece; ++j)
|
|
|
|
{
|
2015-02-14 22:32:41 +01:00
|
|
|
char const* state = info[j].state
|
|
|
|
== piece_picker::block_info::state_finished ? "1" : "0";
|
2009-10-28 20:55:20 +01:00
|
|
|
fputs(state, stderr);
|
2006-12-18 02:23:30 +01:00
|
|
|
}
|
2009-10-28 20:55:20 +01:00
|
|
|
fputs("\n", stderr);
|
2006-12-18 02:23:30 +01:00
|
|
|
}
|
2016-02-20 06:48:17 +01:00
|
|
|
|
2009-10-28 20:55:20 +01:00
|
|
|
fputs("downloading pieces:\n", stderr);
|
2006-12-18 02:23:30 +01:00
|
|
|
|
2016-09-05 00:24:20 +02:00
|
|
|
for (auto const& p : downloading_piece)
|
2006-12-18 02:23:30 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
std::fprintf(stderr, " %d:%d %d\n"
|
|
|
|
, static_cast<int>(p.first.piece_index)
|
|
|
|
, p.first.block_index, p.second);
|
2006-12-18 02:23:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size());
|
|
|
|
TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size());
|
2006-12-18 02:23:30 +01:00
|
|
|
|
|
|
|
#endif
|
2006-12-15 13:29:47 +01:00
|
|
|
|
2009-01-14 08:41:25 +01:00
|
|
|
TORRENT_ASSERT(st.total_done >= st.total_wanted_done);
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::on_piece_verified(piece_index_t const piece
|
2016-11-21 05:58:48 +01:00
|
|
|
, sha1_hash const& piece_hash, storage_error const& error) try
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-03-02 07:16:08 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
bool const passed = settings().get_bool(settings_pack::disable_hash_checks)
|
|
|
|
|| (!error && sha1_hash(piece_hash) == m_torrent_file->hash_for_piece(piece));
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
bool const disk_error = !passed && error;
|
|
|
|
|
|
|
|
if (disk_error) handle_disk_error("piece_verified", error);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("*** PIECE_FINISHED [ p: %d | chk: %s | size: %d ]"
|
2016-12-22 16:42:33 +01:00
|
|
|
, static_cast<int>(piece), passed ? "passed" : disk_error ? "disk failed" : "failed"
|
2016-11-21 05:58:48 +01:00
|
|
|
, m_torrent_file->piece_size(piece));
|
2016-09-14 04:46:07 +02:00
|
|
|
}
|
2007-10-18 02:32:16 +02:00
|
|
|
#endif
|
2008-06-23 20:54:58 +02:00
|
|
|
TORRENT_ASSERT(valid_metadata());
|
2007-06-10 22:46:09 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// if we're a seed we don't have a picker
|
|
|
|
// and we also don't have to do anything because
|
|
|
|
// we already have this piece
|
|
|
|
if (!has_picker() && m_have_all) return;
|
|
|
|
|
|
|
|
need_picker();
|
2012-11-02 05:34:17 +01:00
|
|
|
|
2016-11-21 05:58:48 +01:00
|
|
|
TORRENT_ASSERT(!m_picker->have_piece(piece));
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
|
|
|
|
2009-06-10 10:30:55 +02:00
|
|
|
// even though the piece passed the hash-check
|
|
|
|
// it might still have failed being written to disk
|
|
|
|
// if so, piece_picker::write_failed() has been
|
|
|
|
// called, and the piece is no longer finished.
|
|
|
|
// in this case, we have to ignore the fact that
|
|
|
|
// it passed the check
|
2016-11-21 05:58:48 +01:00
|
|
|
if (!m_picker->is_piece_finished(piece)) return;
|
2009-06-10 10:30:55 +02:00
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
if (disk_error)
|
|
|
|
{
|
|
|
|
update_gauge();
|
|
|
|
}
|
|
|
|
else if (passed)
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
|
|
|
// the following call may cause picker to become invalid
|
|
|
|
// in case we just became a seed
|
2016-11-21 05:58:48 +01:00
|
|
|
piece_passed(piece);
|
2009-02-03 08:46:24 +01:00
|
|
|
// if we're in seed mode, we just acquired this piece
|
|
|
|
// mark it as verified
|
2016-11-21 05:58:48 +01:00
|
|
|
if (m_seed_mode) verified(piece);
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
2016-11-26 07:51:47 +01:00
|
|
|
else
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
2008-06-22 22:28:03 +02:00
|
|
|
// piece_failed() will restore the piece
|
2016-11-21 05:58:48 +01:00
|
|
|
piece_failed(piece);
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2007-06-10 22:46:09 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::add_suggest_piece(piece_index_t const index)
|
2016-06-15 19:29:54 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(settings().get_int(settings_pack::suggest_mode)
|
|
|
|
== settings_pack::suggest_read_cache);
|
|
|
|
|
|
|
|
// when we care about suggest mode, we keep the piece picker
|
|
|
|
// around to track piece availability
|
|
|
|
need_picker();
|
|
|
|
int const peers = std::max(num_peers(), 1);
|
|
|
|
int const availability = m_picker->get_availability(index) * 100 / peers;
|
|
|
|
|
|
|
|
m_suggest_pieces.add_piece(index, availability
|
|
|
|
, settings().get_int(settings_pack::max_suggest_pieces));
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// this is called once we have completely downloaded piece
|
|
|
|
// 'index', its hash has been verified. It's also called
|
|
|
|
// during initial file check when we find a piece whose hash
|
|
|
|
// is correct
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::we_have(piece_index_t const index)
|
2009-07-04 06:58:24 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(!has_picker() || m_picker->has_piece_passed(index));
|
2009-07-04 06:58:24 +02:00
|
|
|
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::num_have_pieces);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// at this point, we have the piece for sure. It has been
|
|
|
|
// successfully written to disk. We may announce it to peers
|
|
|
|
// (unless it has already been announced through predictive_piece_announce
|
|
|
|
// feature).
|
|
|
|
bool announce_piece = true;
|
2016-12-22 16:42:33 +01:00
|
|
|
auto it = std::lower_bound(m_predictive_pieces.begin()
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_predictive_pieces.end(), index);
|
2015-08-17 15:01:43 +02:00
|
|
|
if (it != m_predictive_pieces.end() && *it == index)
|
2009-07-04 06:58:24 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// this means we've already announced the piece
|
|
|
|
announce_piece = false;
|
2015-08-17 15:01:43 +02:00
|
|
|
m_predictive_pieces.erase(it);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// make a copy of the peer list since peers
|
|
|
|
// may disconnect while looping
|
|
|
|
std::vector<peer_connection*> peers = m_connections;
|
|
|
|
|
|
|
|
for (peer_iterator i = peers.begin(); i != peers.end(); ++i)
|
|
|
|
{
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<peer_connection> p = (*i)->self();
|
2009-07-04 06:58:24 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// received_piece will check to see if we're still interested
|
|
|
|
// in this peer, and if neither of us is interested in the other,
|
|
|
|
// disconnect it.
|
|
|
|
p->received_piece(index);
|
|
|
|
if (p->is_disconnecting()) continue;
|
2009-07-04 06:58:24 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// if we're not announcing the piece, it means we
|
|
|
|
// already have, and that we might have received
|
|
|
|
// a request for it, and not sending it because
|
|
|
|
// we were waiting to receive the piece, now that
|
|
|
|
// we have received it, try to send stuff (fill_send_buffer)
|
|
|
|
if (announce_piece) p->announce_piece(index);
|
|
|
|
else p->fill_send_buffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-05-08 00:46:42 +02:00
|
|
|
for (auto& ext : m_extensions)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-06 06:08:14 +02:00
|
|
|
ext->on_piece_pass(index);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// since this piece just passed, we might have
|
|
|
|
// become uninterested in some peers where this
|
|
|
|
// was the last piece we were interested in
|
|
|
|
for (peer_iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end();)
|
|
|
|
{
|
|
|
|
peer_connection* p = *i;
|
|
|
|
// update_interest may disconnect the peer and
|
|
|
|
// invalidate the iterator
|
|
|
|
++i;
|
|
|
|
// if we're not interested already, no need to check
|
|
|
|
if (!p->is_interesting()) continue;
|
|
|
|
// if the peer doesn't have the piece we just got, it
|
|
|
|
// shouldn't affect our interest
|
|
|
|
if (!p->has_piece(index)) continue;
|
|
|
|
p->update_interest();
|
|
|
|
}
|
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2014-07-06 21:18:00 +02:00
|
|
|
state_updated();
|
|
|
|
|
|
|
|
if (m_ses.alerts().should_post<piece_finished_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<piece_finished_alert>(get_handle(), index);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// update m_file_progress (if we have one)
|
2015-06-29 04:47:11 +02:00
|
|
|
m_file_progress.update(m_torrent_file->files(), index
|
|
|
|
, &m_ses.alerts(), get_handle());
|
2009-07-04 06:58:24 +02:00
|
|
|
|
2013-01-28 08:42:51 +01:00
|
|
|
remove_time_critical_piece(index, true);
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (is_finished()
|
|
|
|
&& m_state != torrent_status::finished
|
|
|
|
&& m_state != torrent_status::seeding)
|
|
|
|
{
|
|
|
|
// torrent finished
|
|
|
|
// i.e. all the pieces we're interested in have
|
|
|
|
// been downloaded. Release the files (they will open
|
|
|
|
// in read only mode if needed)
|
|
|
|
finished();
|
|
|
|
// if we just became a seed, picker is now invalid, since it
|
|
|
|
// is deallocated by the torrent once it starts seeding
|
|
|
|
}
|
|
|
|
|
2014-08-27 08:36:28 +02:00
|
|
|
m_last_download = m_ses.session_time();
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (m_share_mode)
|
|
|
|
recalc_share_mode();
|
2009-07-04 06:58:24 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// this is called when the piece hash is checked as correct. Note
|
|
|
|
// that the piece picker and the torrent won't necessarily consider
|
|
|
|
// us to have this piece yet, since it might not have been flushed
|
|
|
|
// to disk yet. Only if we have predictive_piece_announce on will
|
|
|
|
// we announce this piece to peers at this point.
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::piece_passed(piece_index_t const index)
|
2008-06-23 20:54:58 +02:00
|
|
|
{
|
|
|
|
// INVARIANT_CHECK;
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(!m_picker->has_piece_passed(index));
|
2008-06-23 20:54:58 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
debug_log("PIECE_PASSED (%d)", num_passed());
|
2010-04-24 21:24:27 +02:00
|
|
|
#endif
|
2008-06-23 20:54:58 +02:00
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
// std::fprintf(stderr, "torrent::piece_passed piece:%d\n", index);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(index >= piece_index_t(0));
|
|
|
|
TORRENT_ASSERT(index < m_torrent_file->end_piece());
|
2008-06-23 20:54:58 +02:00
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2010-05-17 01:12:37 +02:00
|
|
|
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::num_piece_passed);
|
2009-03-17 10:34:44 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
remove_time_critical_piece(index, true);
|
2008-06-23 20:54:58 +02:00
|
|
|
|
2016-06-15 19:29:54 +02:00
|
|
|
if (settings().get_int(settings_pack::suggest_mode)
|
|
|
|
== settings_pack::suggest_read_cache)
|
|
|
|
{
|
|
|
|
// we just got a new piece. Chances are that it's actually the
|
|
|
|
// rarest piece (since we're likely to download pieces rarest first)
|
|
|
|
// if it's rarer than any other piece that we currently suggest, insert
|
|
|
|
// it in the suggest set and pop the last one out
|
|
|
|
add_suggest_piece(index);
|
|
|
|
}
|
|
|
|
|
2015-08-18 10:25:13 +02:00
|
|
|
std::vector<torrent_peer*> downloaders;
|
2008-06-23 20:54:58 +02:00
|
|
|
m_picker->get_downloaders(downloaders, index);
|
|
|
|
|
|
|
|
// increase the trust point of all peers that sent
|
|
|
|
// parts of this piece.
|
2015-08-18 10:25:13 +02:00
|
|
|
std::set<torrent_peer*> peers;
|
2009-07-04 06:58:24 +02:00
|
|
|
|
2014-10-26 08:34:31 +01:00
|
|
|
// these torrent_peer pointers are owned by m_peer_list and they may be
|
2012-02-16 11:06:21 +01:00
|
|
|
// invalidated if a peer disconnects. We cannot keep them across any
|
|
|
|
// significant operations, but we should use them right away
|
2016-06-20 17:32:06 +02:00
|
|
|
// ignore nullptrs
|
2012-02-16 11:06:21 +01:00
|
|
|
std::remove_copy(downloaders.begin(), downloaders.end()
|
2016-07-09 22:26:26 +02:00
|
|
|
, std::inserter(peers, peers.begin()), static_cast<torrent_peer*>(nullptr));
|
2008-06-23 20:54:58 +02:00
|
|
|
|
2015-08-18 10:25:13 +02:00
|
|
|
for (std::set<torrent_peer*>::iterator i = peers.begin()
|
2008-06-23 20:54:58 +02:00
|
|
|
, end(peers.end()); i != end; ++i)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
torrent_peer* p = static_cast<torrent_peer*>(*i);
|
2016-07-09 22:26:26 +02:00
|
|
|
TORRENT_ASSERT(p != nullptr);
|
|
|
|
if (p == nullptr) continue;
|
2012-02-09 07:24:58 +01:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2008-06-23 20:54:58 +02:00
|
|
|
p->on_parole = false;
|
2009-04-30 07:49:46 +02:00
|
|
|
int trust_points = p->trust_points;
|
|
|
|
++trust_points;
|
|
|
|
if (trust_points > 8) trust_points = 8;
|
|
|
|
p->trust_points = trust_points;
|
2012-02-09 07:24:58 +01:00
|
|
|
if (p->connection)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
peer_connection* peer = static_cast<peer_connection*>(p->connection);
|
|
|
|
TORRENT_ASSERT(peer->m_in_use == 1337);
|
|
|
|
peer->received_valid_data(index);
|
2012-02-09 07:24:58 +01:00
|
|
|
}
|
2008-06-23 20:54:58 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
// announcing a piece may invalidate the torrent_peer pointers
|
2012-02-16 11:06:21 +01:00
|
|
|
// so we can't use them anymore
|
|
|
|
|
|
|
|
downloaders.clear();
|
|
|
|
peers.clear();
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// make the disk cache flush the piece to disk
|
|
|
|
if (m_storage)
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_flush_piece(m_storage, index);
|
2014-07-06 21:18:00 +02:00
|
|
|
m_picker->piece_passed(index);
|
|
|
|
update_gauge();
|
2012-02-16 11:06:21 +01:00
|
|
|
we_have(index);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2012-02-16 11:06:21 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// we believe we will complete this piece very soon
|
|
|
|
// announce it to peers ahead of time to eliminate the
|
|
|
|
// round-trip times involved in announcing it, requesting it
|
|
|
|
// and sending it
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::predicted_have_piece(piece_index_t const index, int milliseconds)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
auto i = std::lower_bound(m_predictive_pieces.begin()
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_predictive_pieces.end(), index);
|
|
|
|
if (i != m_predictive_pieces.end() && *i == index) return;
|
2015-05-18 07:04:55 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
for (peer_iterator p = m_connections.begin()
|
|
|
|
, end(m_connections.end()); p != end; ++p)
|
2008-06-23 20:54:58 +02:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-03 04:53:54 +02:00
|
|
|
(*p)->peer_log(peer_log_alert::outgoing, "PREDICTIVE_HAVE", "piece: %d expected in %d ms"
|
2016-12-22 16:42:33 +01:00
|
|
|
, static_cast<int>(index), milliseconds);
|
2015-08-22 00:28:12 +02:00
|
|
|
#else
|
|
|
|
TORRENT_UNUSED(milliseconds);
|
2008-06-23 20:54:58 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
(*p)->announce_piece(index);
|
2008-06-23 20:54:58 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_predictive_pieces.insert(i, index);
|
2008-06-23 20:54:58 +02:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::piece_failed(piece_index_t const index)
|
2003-12-01 22:27:27 +01:00
|
|
|
{
|
2006-12-14 17:12:31 +01:00
|
|
|
// if the last piece fails the peer connection will still
|
|
|
|
// think that it has received all of it until this function
|
|
|
|
// resets the download queue. So, we cannot do the
|
|
|
|
// invariant check here since it assumes:
|
2007-09-01 05:00:31 +02:00
|
|
|
// (total_done == m_torrent_file->total_size()) => is_seed()
|
2007-10-31 10:48:20 +01:00
|
|
|
INVARIANT_CHECK;
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_picker.get());
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(index >= piece_index_t(0));
|
|
|
|
TORRENT_ASSERT(index < m_torrent_file->end_piece());
|
2004-01-25 13:37:15 +01:00
|
|
|
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::num_piece_failed);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (m_ses.alerts().should_post<hash_failed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<hash_failed_alert>(get_handle(), index);
|
2008-07-06 14:22:56 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
auto it = std::lower_bound(m_predictive_pieces.begin()
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_predictive_pieces.end(), index);
|
2015-08-17 15:01:43 +02:00
|
|
|
if (it != m_predictive_pieces.end() && *it == index)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
for (peer_iterator p = m_connections.begin()
|
|
|
|
, end(m_connections.end()); p != end; ++p)
|
|
|
|
{
|
|
|
|
// send reject messages for
|
|
|
|
// potential outstanding requests to this piece
|
|
|
|
(*p)->reject_piece(index);
|
|
|
|
// let peers that support the dont-have message
|
|
|
|
// know that we don't actually have this piece
|
|
|
|
(*p)->write_dont_have(index);
|
|
|
|
}
|
2015-08-17 15:01:43 +02:00
|
|
|
m_predictive_pieces.erase(it);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2004-04-18 14:28:02 +02:00
|
|
|
// increase the total amount of failed bytes
|
2008-07-11 09:30:04 +02:00
|
|
|
add_failed_bytes(m_torrent_file->piece_size(index));
|
2004-04-18 14:28:02 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-05-08 00:46:42 +02:00
|
|
|
for (auto& ext : m_extensions)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-10-06 06:08:14 +02:00
|
|
|
ext->on_piece_failed(index);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-08-18 10:25:13 +02:00
|
|
|
std::vector<torrent_peer*> downloaders;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_picker)
|
|
|
|
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
|
2015-08-18 10:25:13 +02:00
|
|
|
std::set<torrent_peer*> peers;
|
2004-09-12 12:12:16 +02:00
|
|
|
std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin()));
|
|
|
|
|
2016-07-02 01:46:59 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2015-08-18 10:25:13 +02:00
|
|
|
for (std::vector<torrent_peer*>::iterator i = downloaders.begin()
|
2007-12-18 07:04:54 +01:00
|
|
|
, end(downloaders.end()); i != end; ++i)
|
|
|
|
{
|
2015-08-17 15:01:43 +02:00
|
|
|
torrent_peer* p = static_cast<torrent_peer*>(*i);
|
2007-12-18 07:04:54 +01:00
|
|
|
if (p && p->connection)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
peer_connection* peer = static_cast<peer_connection*>(p->connection);
|
|
|
|
peer->piece_failed = true;
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-09-29 19:46:41 +02:00
|
|
|
// did we receive this piece from a single peer?
|
|
|
|
bool single_peer = peers.size() == 1;
|
|
|
|
|
2015-08-18 10:25:13 +02:00
|
|
|
for (std::set<torrent_peer*>::iterator i = peers.begin()
|
2004-09-12 12:12:16 +02:00
|
|
|
, end(peers.end()); i != end; ++i)
|
2003-12-01 22:27:27 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
torrent_peer* p = static_cast<torrent_peer*>(*i);
|
2016-07-09 22:26:26 +02:00
|
|
|
if (p == nullptr) continue;
|
2012-03-06 08:35:56 +01:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2012-09-29 19:46:41 +02:00
|
|
|
bool allow_disconnect = true;
|
2012-03-06 08:35:56 +01:00
|
|
|
if (p->connection)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
peer_connection* peer = static_cast<peer_connection*>(p->connection);
|
|
|
|
TORRENT_ASSERT(peer->m_in_use == 1337);
|
2012-09-29 19:46:41 +02:00
|
|
|
|
|
|
|
// the peer implementation can ask not to be disconnected.
|
|
|
|
// this is used for web seeds for instance, to instead of
|
|
|
|
// disconnecting, mark the file as not being haved.
|
2014-07-06 21:18:00 +02:00
|
|
|
allow_disconnect = peer->received_invalid_data(index, single_peer);
|
2012-03-06 08:35:56 +01:00
|
|
|
}
|
|
|
|
|
2015-05-16 08:33:37 +02:00
|
|
|
if (settings().get_bool(settings_pack::use_parole_mode))
|
2012-03-06 08:35:56 +01:00
|
|
|
p->on_parole = true;
|
|
|
|
|
|
|
|
int hashfails = p->hashfails;
|
|
|
|
int trust_points = p->trust_points;
|
|
|
|
|
|
|
|
// we decrease more than we increase, to keep the
|
|
|
|
// allowed failed/passed ratio low.
|
|
|
|
trust_points -= 2;
|
|
|
|
++hashfails;
|
|
|
|
if (trust_points < -7) trust_points = -7;
|
|
|
|
p->trust_points = trust_points;
|
|
|
|
if (hashfails > 255) hashfails = 255;
|
2016-11-25 17:17:25 +01:00
|
|
|
p->hashfails = std::uint8_t(hashfails);
|
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.
|
2012-09-29 19:46:41 +02:00
|
|
|
// if we have failed more than 3 pieces from this peer,
|
|
|
|
// don't trust it regardless.
|
2007-07-04 04:16:49 +02:00
|
|
|
if (p->trust_points <= -7
|
2012-09-29 19:46:41 +02:00
|
|
|
|| (single_peer && allow_disconnect))
|
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.
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<peer_ban_alert>())
|
2004-03-21 03:03:37 +01:00
|
|
|
{
|
2016-07-09 22:26:26 +02:00
|
|
|
peer_id pid(nullptr);
|
2008-07-08 11:30:10 +02:00
|
|
|
if (p->connection) pid = p->connection->pid();
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<peer_ban_alert>(
|
|
|
|
get_handle(), p->ip(), pid);
|
2004-03-21 03:03:37 +01:00
|
|
|
}
|
2007-04-13 03:53:25 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// mark the peer as banned
|
|
|
|
ban_peer(p);
|
|
|
|
update_want_peers();
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::banned_for_hash_failure);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (p->connection)
|
|
|
|
{
|
|
|
|
peer_connection* peer = static_cast<peer_connection*>(p->connection);
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("*** BANNING PEER: \"%s\" Too many corrupt pieces"
|
|
|
|
, print_endpoint(p->ip()).c_str());
|
|
|
|
}
|
2015-05-03 04:53:54 +02:00
|
|
|
peer->peer_log(peer_log_alert::info, "BANNING_PEER", "Too many corrupt pieces");
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2015-02-15 06:17:09 +01:00
|
|
|
peer->disconnect(errors::too_many_corrupt_pieces, op_bittorrent);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If m_storage isn't set here, it means we're shutting down
|
|
|
|
if (m_storage)
|
|
|
|
{
|
|
|
|
// it doesn't make much sense to fail to hash a piece
|
|
|
|
// without having a storage associated with the torrent.
|
|
|
|
// restoring the piece in the piece picker without calling
|
|
|
|
// clear piece on the disk thread will make them out of
|
|
|
|
// sync, and if we try to write more blocks to this piece
|
|
|
|
// the disk thread will barf, because it hasn't been cleared
|
|
|
|
TORRENT_ASSERT(m_storage);
|
|
|
|
|
|
|
|
// don't allow picking any blocks from this piece
|
|
|
|
// until we're done synchronizing with the disk threads.
|
|
|
|
m_picker->lock_piece(index);
|
|
|
|
|
|
|
|
// don't do this until after the plugins have had a chance
|
|
|
|
// to read back the blocks that failed, for blame purposes
|
|
|
|
// this way they have a chance to hit the cache
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_clear_piece(m_storage, index
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_piece_sync, shared_from_this(), _1));
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_abort);
|
|
|
|
// it doesn't really matter what we do
|
|
|
|
// here, since we're about to destruct the
|
|
|
|
// torrent anyway.
|
2016-11-23 07:43:57 +01:00
|
|
|
on_piece_sync(index);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2016-07-02 01:46:59 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2015-08-18 10:25:13 +02:00
|
|
|
for (std::vector<torrent_peer*>::iterator i = downloaders.begin()
|
2014-07-06 21:18:00 +02:00
|
|
|
, end(downloaders.end()); i != end; ++i)
|
|
|
|
{
|
2015-08-18 16:42:03 +02:00
|
|
|
torrent_peer* p = *i;
|
2014-07-06 21:18:00 +02:00
|
|
|
if (p && p->connection)
|
|
|
|
{
|
|
|
|
peer_connection* peer = static_cast<peer_connection*>(p->connection);
|
|
|
|
peer->piece_failed = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::peer_is_interesting(peer_connection& c)
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
// no peer should be interesting if we're finished
|
|
|
|
TORRENT_ASSERT(!is_finished());
|
|
|
|
|
|
|
|
if (c.in_handshake()) return;
|
|
|
|
c.send_interested();
|
|
|
|
if (c.has_peer_choked()
|
|
|
|
&& c.allowed_fast().empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (request_a_block(*this, c))
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::interesting_piece_picks);
|
2014-07-06 21:18:00 +02:00
|
|
|
c.send_block_requests();
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::on_piece_sync(piece_index_t const piece) try
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
// the user may have called force_recheck, which clears
|
|
|
|
// the piece picker
|
|
|
|
if (!has_picker()) return;
|
|
|
|
|
|
|
|
// unlock the piece and restore it, as if no block was
|
|
|
|
// ever downloaded for it.
|
2016-11-23 07:43:57 +01:00
|
|
|
m_picker->restore_piece(piece);
|
2014-07-06 21:18:00 +02: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
|
2016-11-23 07:43:57 +01:00
|
|
|
TORRENT_ASSERT(m_picker->have_piece(piece) == false);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// loop over all peers and re-request potential duplicate
|
|
|
|
// blocks to this piece
|
|
|
|
for (std::vector<peer_connection*>::iterator i = m_connections.begin()
|
|
|
|
, end(m_connections.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
peer_connection* p = *i;
|
|
|
|
std::vector<pending_block> const& dq = p->download_queue();
|
|
|
|
std::vector<pending_block> const& rq = p->request_queue();
|
|
|
|
for (std::vector<pending_block>::const_iterator k = dq.begin()
|
2015-08-18 16:42:03 +02:00
|
|
|
, end2(dq.end()); k != end2; ++k)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (k->timed_out || k->not_wanted) continue;
|
2016-12-15 20:20:08 +01:00
|
|
|
if (k->block.piece_index != piece) continue;
|
2014-07-06 21:18:00 +02:00
|
|
|
m_picker->mark_as_downloading(k->block, p->peer_info_struct()
|
2015-01-26 03:04:58 +01:00
|
|
|
, p->picker_options());
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
for (std::vector<pending_block>::const_iterator k = rq.begin()
|
2015-08-18 16:42:03 +02:00
|
|
|
, end2(rq.end()); k != end2; ++k)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
if (k->block.piece_index != piece) continue;
|
2014-07-06 21:18:00 +02:00
|
|
|
m_picker->mark_as_downloading(k->block, p->peer_info_struct()
|
2015-01-26 03:04:58 +01:00
|
|
|
, p->picker_options());
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::peer_has(piece_index_t const index, peer_connection const* peer)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (has_picker())
|
|
|
|
{
|
2015-08-18 10:25:13 +02:00
|
|
|
torrent_peer* pp = peer->peer_info_struct();
|
|
|
|
m_picker->inc_refcount(index, pp);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(is_seed() || !m_have_all);
|
|
|
|
}
|
|
|
|
}
|
2015-08-17 15:01:43 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// when we get a bitfield message, this is called for that piece
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::peer_has(typed_bitfield<piece_index_t> const& bits
|
|
|
|
, peer_connection const* peer)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (has_picker())
|
|
|
|
{
|
2015-08-28 03:48:52 +02:00
|
|
|
TORRENT_ASSERT(bits.size() == torrent_file().num_pieces());
|
2015-08-18 10:25:13 +02:00
|
|
|
torrent_peer* pp = peer->peer_info_struct();
|
|
|
|
m_picker->inc_refcount(bits, pp);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(is_seed() || !m_have_all);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::peer_has_all(peer_connection const* peer)
|
|
|
|
{
|
|
|
|
if (has_picker())
|
|
|
|
{
|
2015-08-18 10:25:13 +02:00
|
|
|
torrent_peer* pp = peer->peer_info_struct();
|
|
|
|
m_picker->inc_refcount_all(pp);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(is_seed() || !m_have_all);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::peer_lost(typed_bitfield<piece_index_t> const& bits
|
|
|
|
, peer_connection const* peer)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (has_picker())
|
|
|
|
{
|
2015-08-28 03:48:52 +02:00
|
|
|
TORRENT_ASSERT(bits.size() == torrent_file().num_pieces());
|
2015-08-18 10:25:13 +02:00
|
|
|
torrent_peer* pp = peer->peer_info_struct();
|
|
|
|
m_picker->dec_refcount(bits, pp);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(is_seed() || !m_have_all);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::peer_lost(piece_index_t const index, peer_connection const* peer)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (m_picker.get())
|
|
|
|
{
|
2015-08-18 10:25:13 +02:00
|
|
|
torrent_peer* pp = peer->peer_info_struct();
|
|
|
|
m_picker->dec_refcount(index, pp);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(is_seed() || !m_have_all);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-05 15:17:17 +01:00
|
|
|
void torrent::abort()
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2009-02-09 03:04:43 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2005-03-05 15:17:17 +01:00
|
|
|
m_abort = true;
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
|
|
|
update_want_tick();
|
2015-07-27 09:01:19 +02:00
|
|
|
update_want_scrape();
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
2016-05-03 07:34:43 +02:00
|
|
|
stop_announcing();
|
2014-10-06 00:30:09 +02:00
|
|
|
|
2016-05-07 20:52:10 +02:00
|
|
|
if (m_peer_class > 0)
|
|
|
|
{
|
|
|
|
m_ses.peer_classes().decref(m_peer_class);
|
|
|
|
m_peer_class = 0;
|
|
|
|
}
|
|
|
|
|
2014-10-06 00:30:09 +02:00
|
|
|
error_code ec;
|
|
|
|
m_inactivity_timer.cancel(ec);
|
2007-08-16 14:41:46 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-03 04:53:54 +02:00
|
|
|
log_to_all_peers("aborting");
|
2007-08-16 14:41:46 +02:00
|
|
|
#endif
|
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
// disconnect all peers and close all
|
|
|
|
// files belonging to the torrents
|
2015-02-15 06:17:09 +01:00
|
|
|
disconnect_all(errors::torrent_aborted, op_bittorrent);
|
2010-02-21 09:52:26 +01:00
|
|
|
|
|
|
|
// post a message to the main thread to destruct
|
|
|
|
// the torrent object from there
|
2016-12-31 18:35:10 +01:00
|
|
|
if (m_storage)
|
2008-11-17 02:19:46 +01:00
|
|
|
{
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_stop_torrent(m_storage
|
2016-11-22 07:48:14 +01:00
|
|
|
, std::bind(&torrent::on_cache_flushed, shared_from_this()));
|
2008-11-17 02:19:46 +01:00
|
|
|
}
|
2011-04-26 18:26:53 +02:00
|
|
|
else
|
|
|
|
{
|
2013-08-12 02:51:49 +02:00
|
|
|
TORRENT_ASSERT(m_abort);
|
2011-04-26 18:26:53 +02:00
|
|
|
if (alerts().should_post<cache_flushed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<cache_flushed_alert>(get_handle());
|
2011-04-26 18:26:53 +02:00
|
|
|
}
|
2015-05-25 23:46:42 +02:00
|
|
|
|
2017-01-02 08:00:20 +01:00
|
|
|
// if this torrent still has connections, they may have buffers to return
|
|
|
|
// to the disk storage, in which case we cannot destroy the disk storage
|
|
|
|
// yet. wait until the last peer connection is removed
|
|
|
|
if (m_connections.empty())
|
|
|
|
{
|
|
|
|
m_storage.reset();
|
|
|
|
}
|
2014-10-05 03:23:22 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// TODO: 2 abort lookups this torrent has made via the
|
|
|
|
// session host resolver interface
|
2009-02-09 03:04:43 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_apply_ip_filter)
|
|
|
|
{
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::non_filter_torrents, -1);
|
2014-07-06 21:18:00 +02:00
|
|
|
m_apply_ip_filter = true;
|
|
|
|
}
|
2009-02-09 07:19:31 +01:00
|
|
|
|
2016-06-01 07:05:32 +02:00
|
|
|
m_paused = false;
|
2014-07-06 21:18:00 +02:00
|
|
|
m_auto_managed = false;
|
2015-07-27 09:01:19 +02:00
|
|
|
update_state_list();
|
2014-07-06 21:18:00 +02:00
|
|
|
for (int i = 0; i < aux::session_interface::num_torrent_lists; ++i)
|
|
|
|
{
|
|
|
|
if (!m_links[i].in_list()) continue;
|
|
|
|
m_links[i].unlink(m_ses.torrent_list(i), i);
|
|
|
|
}
|
|
|
|
// don't re-add this torrent to the state-update list
|
|
|
|
m_state_subscription = false;
|
2005-03-05 15:17:17 +01:00
|
|
|
}
|
2003-12-01 22:27:27 +01:00
|
|
|
|
2016-05-22 01:05:42 +02:00
|
|
|
void torrent::set_super_seeding(bool on)
|
2008-12-08 07:36:22 +01:00
|
|
|
{
|
|
|
|
if (on == m_super_seeding) return;
|
|
|
|
|
|
|
|
m_super_seeding = on;
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2008-12-08 07:36:22 +01:00
|
|
|
|
|
|
|
if (m_super_seeding) return;
|
|
|
|
|
|
|
|
// disable super seeding for all peers
|
2016-12-22 16:42:33 +01:00
|
|
|
for (auto pc : *this)
|
2008-12-08 07:36:22 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
pc->superseed_piece(piece_index_t(-1), piece_index_t(-1));
|
2008-12-08 07:36:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
// TODO: 3 this should return optional<>. piece index -1 should not be
|
|
|
|
// allowed
|
|
|
|
piece_index_t torrent::get_piece_to_super_seed(typed_bitfield<piece_index_t> const& bits)
|
2008-12-08 07:36:22 +01:00
|
|
|
{
|
|
|
|
// return a piece with low availability that is not in
|
|
|
|
// the bitfield and that is not currently being super
|
|
|
|
// seeded by any peer
|
|
|
|
TORRENT_ASSERT(m_super_seeding);
|
2015-05-25 23:46:42 +02:00
|
|
|
|
2008-12-08 07:36:22 +01:00
|
|
|
// do a linear search from the first piece
|
|
|
|
int min_availability = 9999;
|
2016-12-22 16:42:33 +01:00
|
|
|
std::vector<piece_index_t> avail_vec;
|
|
|
|
for (piece_index_t i(0); i < m_torrent_file->end_piece(); ++i)
|
2008-12-08 07:36:22 +01:00
|
|
|
{
|
|
|
|
if (bits[i]) continue;
|
|
|
|
|
|
|
|
int availability = 0;
|
2016-12-22 16:42:33 +01:00
|
|
|
for (auto pc : *this)
|
2008-12-08 07:36:22 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
if (pc->super_seeded_piece(i))
|
2008-12-08 07:36:22 +01:00
|
|
|
{
|
|
|
|
// avoid superseeding the same piece to more than one
|
|
|
|
// peer if we can avoid it. Do this by artificially
|
|
|
|
// increase the availability
|
|
|
|
availability = 999;
|
|
|
|
break;
|
|
|
|
}
|
2016-12-22 16:42:33 +01:00
|
|
|
if (pc->has_piece(i)) ++availability;
|
2008-12-08 07:36:22 +01:00
|
|
|
}
|
|
|
|
if (availability > min_availability) continue;
|
|
|
|
if (availability == min_availability)
|
|
|
|
{
|
|
|
|
avail_vec.push_back(i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
TORRENT_ASSERT(availability < min_availability);
|
|
|
|
min_availability = availability;
|
|
|
|
avail_vec.clear();
|
|
|
|
avail_vec.push_back(i);
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
if (avail_vec.empty()) return piece_index_t(-1);
|
2016-12-05 14:39:53 +01:00
|
|
|
return avail_vec[random(std::uint32_t(avail_vec.size() - 1))];
|
2008-12-08 07:36:22 +01:00
|
|
|
}
|
|
|
|
|
2016-11-23 07:43:57 +01:00
|
|
|
void torrent::on_files_deleted(storage_error const& error) try
|
2007-10-13 05:33:33 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-10-13 05:33:33 +02:00
|
|
|
|
2016-11-23 07:43:57 +01:00
|
|
|
if (error)
|
2007-10-13 05:33:33 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (alerts().should_post<torrent_delete_failed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_delete_failed_alert>(get_handle()
|
2016-11-23 07:43:57 +01:00
|
|
|
, error.ec, m_torrent_file->info_hash());
|
2008-07-06 14:22:56 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_deleted_alert>(get_handle(), m_torrent_file->info_hash());
|
2007-10-13 05:33:33 +02:00
|
|
|
}
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2007-10-13 05:33:33 +02:00
|
|
|
|
2016-11-23 07:43:57 +01:00
|
|
|
void torrent::on_file_renamed(std::string const& filename
|
2016-12-22 16:42:33 +01:00
|
|
|
, file_index_t const file_idx
|
2016-11-23 07:43:57 +01:00
|
|
|
, storage_error const& error) try
|
2008-05-28 10:44:40 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-11-23 07:43:57 +01:00
|
|
|
if (error)
|
2008-05-28 10:44:40 +02:00
|
|
|
{
|
2016-11-23 07:43:57 +01:00
|
|
|
if (alerts().should_post<file_rename_failed_alert>())
|
|
|
|
alerts().emplace_alert<file_rename_failed_alert>(get_handle()
|
|
|
|
, file_idx, error.ec);
|
2009-05-07 08:47:19 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-23 07:43:57 +01:00
|
|
|
if (alerts().should_post<file_renamed_alert>())
|
|
|
|
alerts().emplace_alert<file_renamed_alert>(get_handle()
|
|
|
|
, filename, file_idx);
|
|
|
|
m_torrent_file->rename_file(file_idx, filename);
|
2008-05-28 10:44:40 +02:00
|
|
|
}
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2008-05-28 10:44:40 +02:00
|
|
|
|
2016-11-23 07:43:57 +01:00
|
|
|
void torrent::on_torrent_paused() try
|
2007-06-11 23:24:14 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-06-11 23:24:14 +02:00
|
|
|
|
2008-07-06 14:22:56 +02:00
|
|
|
if (alerts().should_post<torrent_paused_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_paused_alert>(get_handle());
|
2007-06-11 23:24:14 +02:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2007-06-11 23:24:14 +02:00
|
|
|
|
2015-12-13 21:14:19 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
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;
|
|
|
|
}
|
2015-12-13 21:14:19 +01:00
|
|
|
#endif
|
2004-02-22 23:40:45 +01:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint32_t torrent::tracker_key() const
|
2013-08-31 23:06:43 +02:00
|
|
|
{
|
2016-09-02 14:27:38 +02:00
|
|
|
uintptr_t const self = reinterpret_cast<uintptr_t>(this);
|
|
|
|
uintptr_t const ses = reinterpret_cast<uintptr_t>(&m_ses);
|
2016-12-31 18:35:10 +01:00
|
|
|
std::uint32_t const storage = m_storage
|
|
|
|
? static_cast<std::uint32_t>(static_cast<storage_index_t>(m_storage))
|
|
|
|
: 0;
|
2016-07-22 16:29:39 +02:00
|
|
|
sha1_hash const h = hasher(reinterpret_cast<char const*>(&self), sizeof(self))
|
2015-08-16 18:17:23 +02:00
|
|
|
.update(reinterpret_cast<char const*>(&storage), sizeof(storage))
|
|
|
|
.update(reinterpret_cast<char const*>(&ses), sizeof(ses))
|
2013-08-31 23:06:43 +02:00
|
|
|
.final();
|
|
|
|
unsigned char const* ptr = &h[0];
|
|
|
|
return detail::read_uint32(ptr);
|
|
|
|
}
|
|
|
|
|
2016-10-06 06:08:14 +02:00
|
|
|
template <typename Fun, typename... Args>
|
|
|
|
void torrent::wrap(Fun f, Args&&... a)
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
|
|
try
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
(this->*f)(std::forward<Args>(a)...);
|
|
|
|
}
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
|
|
catch (system_error const& e) {
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("EXCEPTION: (%d %s) %s"
|
|
|
|
, e.code().value()
|
|
|
|
, e.code().message().c_str()
|
|
|
|
, e.what());
|
|
|
|
#endif
|
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle()
|
|
|
|
, e.code(), e.what());
|
|
|
|
pause();
|
|
|
|
} catch (std::exception const& e) {
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("EXCEPTION: %s", e.what());
|
|
|
|
#endif
|
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle()
|
|
|
|
, error_code(), e.what());
|
|
|
|
pause();
|
|
|
|
} catch (...) {
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("EXCEPTION: unknown");
|
|
|
|
#endif
|
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle()
|
|
|
|
, error_code(), "unknown error");
|
|
|
|
pause();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
void torrent::cancel_non_critical()
|
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
std::set<piece_index_t> time_critical;
|
2014-07-06 21:18:00 +02:00
|
|
|
for (std::vector<time_critical_piece>::iterator i = m_time_critical_pieces.begin()
|
2014-06-06 03:26:18 +02:00
|
|
|
, end(m_time_critical_pieces.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
time_critical.insert(i->piece);
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
for (std::vector<peer_connection*>::iterator i
|
2014-06-06 03:26:18 +02:00
|
|
|
= m_connections.begin(), end(m_connections.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
// for each peer, go through its download and request queue
|
|
|
|
// and cancel everything, except pieces that are time critical
|
|
|
|
peer_connection* p = *i;
|
|
|
|
|
|
|
|
std::vector<pending_block> dq = p->download_queue();
|
|
|
|
for (std::vector<pending_block>::iterator k = dq.begin()
|
2015-08-18 16:42:03 +02:00
|
|
|
, end2(dq.end()); k != end2; ++k)
|
2014-06-06 03:26:18 +02:00
|
|
|
{
|
|
|
|
if (time_critical.count(k->block.piece_index)) continue;
|
2014-06-06 17:27:44 +02:00
|
|
|
if (k->not_wanted || k->timed_out) continue;
|
2014-06-06 03:26:18 +02:00
|
|
|
p->cancel_request(k->block, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<pending_block> rq = p->request_queue();
|
|
|
|
for (std::vector<pending_block>::const_iterator k = rq.begin()
|
2015-08-18 16:42:03 +02:00
|
|
|
, end2(rq.end()); k != end2; ++k)
|
2014-06-06 03:26:18 +02:00
|
|
|
{
|
|
|
|
if (time_critical.count(k->block.piece_index)) continue;
|
|
|
|
p->cancel_request(k->block, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::set_piece_deadline(piece_index_t piece, int t, int flags)
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
2013-01-28 08:42:51 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2013-01-13 23:35:19 +01:00
|
|
|
if (m_abort)
|
|
|
|
{
|
|
|
|
// failed
|
|
|
|
if (flags & torrent_handle::alert_when_available)
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<read_piece_alert>(
|
2015-11-24 06:50:51 +01:00
|
|
|
get_handle(), piece, error_code(boost::system::errc::operation_canceled, generic_category()));
|
2013-01-13 23:35:19 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point deadline = aux::time_now() + milliseconds(t);
|
2009-03-17 10:34:44 +01:00
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
// if we already have the piece, no need to set the deadline.
|
|
|
|
// however, if the user asked to get the piece data back, we still
|
|
|
|
// need to read it and post it back to the user
|
2014-07-06 21:18:00 +02:00
|
|
|
if (is_seed() || (has_picker() && m_picker->has_piece_passed(piece)))
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
|
|
|
if (flags & torrent_handle::alert_when_available)
|
|
|
|
read_piece(piece);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
// if this is the first time critical piece we add. in order to make it
|
|
|
|
// react quickly, cancel all the currently outstanding requests
|
|
|
|
if (m_time_critical_pieces.empty())
|
|
|
|
{
|
|
|
|
// defer this by posting it to the end of the message queue.
|
2016-10-06 06:08:14 +02:00
|
|
|
// this gives the client a chance to specify multiple time-critical
|
2014-06-06 03:26:18 +02:00
|
|
|
// pieces before libtorrent cancels requests
|
2016-10-06 06:08:14 +02:00
|
|
|
auto self = shared_from_this();
|
|
|
|
m_ses.get_io_service().post([self] { self->wrap(&torrent::cancel_non_critical); });
|
2014-06-06 03:26:18 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
for (std::vector<time_critical_piece>::iterator i = m_time_critical_pieces.begin()
|
2009-03-17 10:34:44 +01:00
|
|
|
, end(m_time_critical_pieces.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
if (i->piece != piece) continue;
|
|
|
|
i->deadline = deadline;
|
|
|
|
i->flags = flags;
|
|
|
|
|
|
|
|
// resort i since deadline might have changed
|
2016-09-07 05:37:36 +02:00
|
|
|
while (std::next(i) != m_time_critical_pieces.end() && i->deadline > std::next(i)->deadline)
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
2016-09-07 05:37:36 +02:00
|
|
|
std::iter_swap(i, std::next(i));
|
2009-03-17 10:34:44 +01:00
|
|
|
++i;
|
|
|
|
}
|
2016-09-07 05:37:36 +02:00
|
|
|
while (i != m_time_critical_pieces.begin() && i->deadline < std::prev(i)->deadline)
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
2016-09-07 05:37:36 +02:00
|
|
|
std::iter_swap(i, std::prev(i));
|
2009-03-17 10:34:44 +01:00
|
|
|
--i;
|
|
|
|
}
|
2013-01-25 07:22:42 +01:00
|
|
|
// just in case this piece had priority 0
|
2014-07-06 21:18:00 +02:00
|
|
|
int prev_prio = m_picker->piece_priority(piece);
|
2014-04-22 06:21:14 +02:00
|
|
|
m_picker->set_piece_priority(piece, 7);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (prev_prio == 0) update_gauge();
|
2009-03-17 10:34:44 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
need_picker();
|
|
|
|
|
2009-03-17 10:34:44 +01:00
|
|
|
time_critical_piece p;
|
|
|
|
p.first_requested = min_time();
|
|
|
|
p.last_requested = min_time();
|
|
|
|
p.flags = flags;
|
|
|
|
p.deadline = deadline;
|
|
|
|
p.peers = 0;
|
|
|
|
p.piece = piece;
|
2015-08-18 16:42:03 +02:00
|
|
|
std::vector<time_critical_piece>::iterator critical_piece_it
|
|
|
|
= std::upper_bound(m_time_critical_pieces.begin()
|
2009-03-17 10:34:44 +01:00
|
|
|
, m_time_critical_pieces.end(), p);
|
2015-08-18 16:42:03 +02:00
|
|
|
m_time_critical_pieces.insert(critical_piece_it, p);
|
2011-08-06 12:57:32 +02:00
|
|
|
|
2013-01-25 07:22:42 +01:00
|
|
|
// just in case this piece had priority 0
|
2014-07-06 21:18:00 +02:00
|
|
|
int prev_prio = m_picker->piece_priority(piece);
|
2014-04-22 06:21:14 +02:00
|
|
|
m_picker->set_piece_priority(piece, 7);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (prev_prio == 0) update_gauge();
|
2013-01-25 07:22:42 +01:00
|
|
|
|
2011-08-06 12:57:32 +02:00
|
|
|
piece_picker::downloading_piece pi;
|
|
|
|
m_picker->piece_info(piece, pi);
|
|
|
|
if (pi.requested == 0) return;
|
|
|
|
// this means we have outstanding requests (or queued
|
|
|
|
// up requests that haven't been sent yet). Promote them
|
|
|
|
// to deadline pieces immediately
|
2015-08-18 10:25:13 +02:00
|
|
|
std::vector<torrent_peer*> downloaders;
|
2011-08-06 12:57:32 +02:00
|
|
|
m_picker->get_downloaders(downloaders, piece);
|
|
|
|
|
|
|
|
int block = 0;
|
2015-08-18 10:25:13 +02:00
|
|
|
for (std::vector<torrent_peer*>::iterator i = downloaders.begin()
|
2011-08-06 12:57:32 +02:00
|
|
|
, end(downloaders.end()); i != end; ++i, ++block)
|
|
|
|
{
|
2015-08-18 16:42:03 +02:00
|
|
|
torrent_peer* tp = *i;
|
2016-07-09 22:26:26 +02:00
|
|
|
if (tp == nullptr || tp->connection == nullptr) continue;
|
2015-08-18 16:42:03 +02:00
|
|
|
peer_connection* peer = static_cast<peer_connection*>(tp->connection);
|
2014-07-06 21:18:00 +02:00
|
|
|
peer->make_time_critical(piece_block(piece, block));
|
2011-08-06 12:57:32 +02:00
|
|
|
}
|
2009-03-17 10:34:44 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::reset_piece_deadline(piece_index_t piece)
|
2011-08-05 08:31:46 +02:00
|
|
|
{
|
|
|
|
remove_time_critical_piece(piece);
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::remove_time_critical_piece(piece_index_t piece, bool finished)
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
for (std::vector<time_critical_piece>::iterator i
|
|
|
|
= m_time_critical_pieces.begin(), end(m_time_critical_pieces.end());
|
|
|
|
i != end; ++i)
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
|
|
|
if (i->piece != piece) continue;
|
|
|
|
if (finished)
|
|
|
|
{
|
|
|
|
if (i->flags & torrent_handle::alert_when_available)
|
|
|
|
{
|
|
|
|
read_piece(i->piece);
|
|
|
|
}
|
|
|
|
|
2012-04-17 05:38:50 +02:00
|
|
|
// if first_requested is min_time(), it wasn't requested as a critical piece
|
|
|
|
// and we shouldn't adjust any average download times
|
|
|
|
if (i->first_requested != min_time())
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
2012-04-17 05:38:50 +02:00
|
|
|
// update the average download time and average
|
|
|
|
// download time deviation
|
2016-12-05 14:39:53 +01:00
|
|
|
int dl_time = int(total_milliseconds(aux::time_now() - i->first_requested));
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2012-04-17 05:38:50 +02:00
|
|
|
if (m_average_piece_time == 0)
|
|
|
|
{
|
|
|
|
m_average_piece_time = dl_time;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int diff = abs(int(dl_time - m_average_piece_time));
|
|
|
|
if (m_piece_time_deviation == 0) m_piece_time_deviation = diff;
|
2014-04-22 06:21:14 +02:00
|
|
|
else m_piece_time_deviation = (m_piece_time_deviation * 9 + diff) / 10;
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
m_average_piece_time = (m_average_piece_time * 9 + dl_time) / 10;
|
2012-04-17 05:38:50 +02:00
|
|
|
}
|
2009-03-17 10:34:44 +01:00
|
|
|
}
|
|
|
|
}
|
2013-01-28 08:27:44 +01:00
|
|
|
else if (i->flags & torrent_handle::alert_when_available)
|
|
|
|
{
|
|
|
|
// post an empty read_piece_alert to indicate it failed
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<read_piece_alert>(
|
2015-11-24 06:50:51 +01:00
|
|
|
get_handle(), piece, error_code(boost::system::errc::operation_canceled, generic_category()));
|
2013-01-28 08:27:44 +01:00
|
|
|
}
|
2014-04-22 06:21:14 +02:00
|
|
|
if (has_picker()) m_picker->set_piece_priority(piece, 1);
|
2009-03-17 10:34:44 +01:00
|
|
|
m_time_critical_pieces.erase(i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 04:23:11 +02:00
|
|
|
void torrent::clear_time_critical()
|
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto i = m_time_critical_pieces.begin(); i != m_time_critical_pieces.end();)
|
2014-05-23 04:23:11 +02:00
|
|
|
{
|
|
|
|
if (i->flags & torrent_handle::alert_when_available)
|
|
|
|
{
|
|
|
|
// post an empty read_piece_alert to indicate it failed
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<read_piece_alert>(
|
2015-11-24 06:50:51 +01:00
|
|
|
get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, generic_category()));
|
2014-05-23 04:23:11 +02:00
|
|
|
}
|
|
|
|
if (has_picker()) m_picker->set_piece_priority(i->piece, 1);
|
|
|
|
i = m_time_critical_pieces.erase(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-17 10:34:44 +01:00
|
|
|
// remove time critical pieces where priority is 0
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::remove_time_critical_pieces(aux::vector<int, piece_index_t> const& priority)
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto i = m_time_critical_pieces.begin(); i != m_time_critical_pieces.end();)
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
|
|
|
if (priority[i->piece] == 0)
|
|
|
|
{
|
2013-01-28 08:27:44 +01:00
|
|
|
if (i->flags & torrent_handle::alert_when_available)
|
|
|
|
{
|
|
|
|
// post an empty read_piece_alert to indicate it failed
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<read_piece_alert>(
|
2015-11-24 06:50:51 +01:00
|
|
|
get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, generic_category()));
|
2013-01-28 08:27:44 +01:00
|
|
|
}
|
2009-03-17 10:34:44 +01:00
|
|
|
i = m_time_critical_pieces.erase(i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::piece_availability(aux::vector<int, piece_index_t>& avail) const
|
2007-05-30 08:52:59 +02:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
2007-03-20 02:59:00 +01:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(valid_metadata());
|
2012-04-25 21:16:22 +02:00
|
|
|
if (!has_picker())
|
2007-05-30 08:52:59 +02:00
|
|
|
{
|
|
|
|
avail.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_picker->get_availability(avail);
|
|
|
|
}
|
2007-03-20 02:59:00 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::set_piece_priority(piece_index_t const index, int priority)
|
2007-03-20 02:59:00 +01:00
|
|
|
{
|
2007-08-09 00:38:30 +02:00
|
|
|
// INVARIANT_CHECK;
|
2007-03-20 02:59:00 +01:00
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
|
|
|
debug_log("*** SET_PIECE_PRIORITY [ idx: %d prio: %d ignored. "
|
2016-12-22 16:42:33 +01:00
|
|
|
"no metadata yet ]", static_cast<int>(index), priority);
|
2015-08-29 16:21:15 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!valid_metadata() || is_seed()) return;
|
2007-03-20 02:59:00 +01:00
|
|
|
|
|
|
|
// this call is only valid on torrents with metadata
|
2016-12-22 16:42:33 +01:00
|
|
|
if (index < piece_index_t(0) || index >= m_torrent_file->end_piece())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2007-03-20 02:59:00 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
need_picker();
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
bool const was_finished = is_finished();
|
2007-08-10 23:13:35 +02:00
|
|
|
bool filter_updated = m_picker->set_piece_priority(index, priority);
|
2008-06-07 16:03:21 +02:00
|
|
|
TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
update_gauge();
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2009-03-17 10:34:44 +01:00
|
|
|
if (filter_updated)
|
|
|
|
{
|
|
|
|
update_peer_interest(was_finished);
|
|
|
|
if (priority == 0) remove_time_critical_piece(index);
|
|
|
|
}
|
|
|
|
|
2007-03-20 02:59:00 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
int torrent::piece_priority(piece_index_t const index) const
|
2007-03-20 02:59:00 +01:00
|
|
|
{
|
2007-08-09 00:38:30 +02:00
|
|
|
// INVARIANT_CHECK;
|
2007-03-20 02:59:00 +01:00
|
|
|
|
2016-12-24 02:00:24 +01:00
|
|
|
if (!has_picker()) return default_piece_priority;
|
2007-03-20 02:59:00 +01:00
|
|
|
|
|
|
|
// this call is only valid on torrents with metadata
|
2015-08-29 16:21:15 +02:00
|
|
|
TORRENT_ASSERT(valid_metadata());
|
2016-12-22 16:42:33 +01:00
|
|
|
if (index < piece_index_t(0) || index >= m_torrent_file->end_piece())
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT_FAIL();
|
|
|
|
return 0;
|
|
|
|
}
|
2007-03-20 02:59:00 +01:00
|
|
|
|
|
|
|
return m_picker->piece_priority(index);
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::prioritize_piece_list(std::vector<std::pair<piece_index_t, int>> const& pieces)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
// this call is only valid on torrents with metadata
|
|
|
|
TORRENT_ASSERT(valid_metadata());
|
|
|
|
if (is_seed()) return;
|
|
|
|
|
|
|
|
need_picker();
|
|
|
|
|
|
|
|
bool filter_updated = false;
|
2016-12-22 16:42:33 +01:00
|
|
|
bool const was_finished = is_finished();
|
2016-09-07 23:51:18 +02:00
|
|
|
for (auto const& p : pieces)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-09-07 23:51:18 +02:00
|
|
|
TORRENT_ASSERT(p.second >= 0);
|
|
|
|
TORRENT_ASSERT(p.second <= 7);
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(p.first >= piece_index_t(0));
|
|
|
|
TORRENT_ASSERT(p.first < m_torrent_file->end_piece());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
if (p.first < piece_index_t(0)
|
|
|
|
|| p.first >= m_torrent_file->end_piece()
|
|
|
|
|| p.second < 0 || p.second > 7)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
continue;
|
2016-12-22 16:42:33 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
filter_updated |= m_picker->set_piece_priority(p.first, p.second);
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered());
|
|
|
|
}
|
|
|
|
update_gauge();
|
|
|
|
if (filter_updated)
|
|
|
|
{
|
|
|
|
// we need to save this new state
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
update_peer_interest(was_finished);
|
|
|
|
}
|
|
|
|
|
|
|
|
state_updated();
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::prioritize_pieces(aux::vector<int, piece_index_t> const& pieces)
|
2007-03-20 02:59:00 +01:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
// this call is only valid on torrents with metadata
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(valid_metadata());
|
2007-03-20 02:59:00 +01:00
|
|
|
if (is_seed()) return;
|
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("*** PRIORITIZE_PIECES [ ignored. no metadata yet ]");
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
need_picker();
|
2007-03-20 02:59:00 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t index(0);
|
2007-08-10 23:13:35 +02:00
|
|
|
bool filter_updated = false;
|
2016-12-22 16:42:33 +01:00
|
|
|
bool const was_finished = is_finished();
|
|
|
|
for (auto prio : pieces)
|
2007-03-20 02:59:00 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(prio >= 0 && prio <= 7);
|
|
|
|
filter_updated |= m_picker->set_piece_priority(index, prio);
|
2008-06-07 16:03:21 +02:00
|
|
|
TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered());
|
2016-12-22 16:42:33 +01:00
|
|
|
++index;
|
2007-03-20 02:59:00 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
2016-03-31 05:35:47 +02:00
|
|
|
update_want_tick();
|
|
|
|
|
2009-03-17 10:34:44 +01:00
|
|
|
if (filter_updated)
|
|
|
|
{
|
2011-06-05 22:48:00 +02:00
|
|
|
// we need to save this new state
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2011-06-05 22:48:00 +02:00
|
|
|
|
2009-03-17 10:34:44 +01:00
|
|
|
update_peer_interest(was_finished);
|
|
|
|
remove_time_critical_pieces(pieces);
|
|
|
|
}
|
2011-11-15 03:34:00 +01:00
|
|
|
|
|
|
|
state_updated();
|
2015-07-27 09:01:19 +02:00
|
|
|
update_state_list();
|
2007-03-20 02:59:00 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::piece_priorities(aux::vector<int, piece_index_t>* pieces) const
|
2007-03-20 02:59:00 +01:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
// this call is only valid on torrents with metadata
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(valid_metadata());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!has_picker())
|
2007-03-20 02:59:00 +01:00
|
|
|
{
|
2011-05-01 19:43:15 +02:00
|
|
|
pieces->clear();
|
2016-12-24 02:00:24 +01:00
|
|
|
pieces->resize(m_torrent_file->num_pieces(), default_piece_priority);
|
2007-03-20 02:59:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_picker.get());
|
2011-05-01 19:43:15 +02:00
|
|
|
m_picker->piece_priorities(*pieces);
|
2007-03-20 02:59:00 +01:00
|
|
|
}
|
|
|
|
|
2016-11-23 07:43:57 +01:00
|
|
|
void torrent::on_file_priority(storage_error const&) {}
|
2014-02-28 11:19:29 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::prioritize_files(aux::vector<int, file_index_t> const& files)
|
2007-03-20 02:59:00 +01:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
// this call is only valid on torrents with metadata
|
|
|
|
if (!valid_metadata() || is_seed()) return;
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
|
|
|
file_index_t const limit = std::min(files.end_index(), fs.end_file());
|
2011-05-01 19:43:15 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
if (m_file_priority.end_index() < limit)
|
2016-12-24 02:00:24 +01:00
|
|
|
m_file_priority.resize(static_cast<int>(limit), default_piece_priority);
|
2011-05-01 19:43:15 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
std::copy(files.begin(), files.begin() + static_cast<int>(limit)
|
|
|
|
, m_file_priority.begin());
|
2011-05-01 19:43:15 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// initialize pad files to priority 0
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < limit; ++i)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (!fs.pad_file_at(i)) continue;
|
|
|
|
m_file_priority[i] = 0;
|
|
|
|
}
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
// storage may be nullptr during construction and shutdown
|
2014-02-28 11:19:29 +01:00
|
|
|
if (m_torrent_file->num_pieces() > 0 && m_storage)
|
|
|
|
{
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_set_file_priority(m_storage
|
2016-11-23 07:43:57 +01:00
|
|
|
, m_file_priority, std::bind(&torrent::on_file_priority, this, _1));
|
2014-02-28 11:19:29 +01:00
|
|
|
}
|
|
|
|
|
2008-07-20 18:00:08 +02:00
|
|
|
update_piece_priorities();
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::set_file_priority(file_index_t const index, int prio)
|
2008-07-20 18:00:08 +02:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
2009-11-26 01:09:49 +01:00
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
if (is_seed()) return;
|
2009-11-26 01:09:49 +01:00
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
// setting file priority on a torrent that doesn't have metadata yet is
|
|
|
|
// similar to having passed in file priorities through add_torrent_params.
|
|
|
|
// we store the priorities in m_file_priority until we get the metadata
|
2016-12-22 16:42:33 +01:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
|
|
|
if (index < file_index_t(0) || (valid_metadata() && index >= fs.end_file()))
|
2015-08-29 16:21:15 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2015-07-21 19:12:37 +02:00
|
|
|
|
2013-05-29 05:30:33 +02:00
|
|
|
if (prio < 0) prio = 0;
|
|
|
|
else if (prio > 7) prio = 7;
|
2016-12-22 16:42:33 +01:00
|
|
|
if (m_file_priority.end_index() <= index)
|
2013-01-28 05:57:35 +01:00
|
|
|
{
|
2016-12-24 02:00:24 +01:00
|
|
|
// any unallocated slot is assumed to have the default priority
|
|
|
|
if (prio == default_piece_priority) return;
|
|
|
|
m_file_priority.resize(static_cast<int>(index) + 1, default_piece_priority);
|
2013-01-28 05:57:35 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2008-07-20 18:00:08 +02:00
|
|
|
if (m_file_priority[index] == prio) return;
|
2016-11-25 17:17:25 +01:00
|
|
|
m_file_priority[index] = std::uint8_t(prio);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
if (!valid_metadata()) return;
|
|
|
|
|
2016-08-15 01:48:31 +02:00
|
|
|
// storage may be nullptr during shutdown
|
2014-02-28 11:19:29 +01:00
|
|
|
if (m_storage)
|
|
|
|
{
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_set_file_priority(m_storage
|
2016-11-23 07:43:57 +01:00
|
|
|
, m_file_priority, std::bind(&torrent::on_file_priority, this, _1));
|
2014-02-28 11:19:29 +01:00
|
|
|
}
|
2008-07-20 18:00:08 +02:00
|
|
|
update_piece_priorities();
|
|
|
|
}
|
2015-07-14 04:19:55 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
int torrent::file_priority(file_index_t const index) const
|
2008-07-20 18:00:08 +02:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT_PRECOND(index >= file_index_t(0));
|
|
|
|
if (index < file_index_t(0)) return 0;
|
2009-11-26 01:09:49 +01:00
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
// if we have metadata, perform additional checks
|
|
|
|
if (valid_metadata())
|
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
|
|
|
TORRENT_ASSERT_PRECOND(index < fs.end_file());
|
|
|
|
if (index >= fs.end_file()) return 0;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-08-29 16:21:15 +02:00
|
|
|
// pad files always have priority 0
|
2016-12-22 16:42:33 +01:00
|
|
|
if (fs.pad_file_at(index)) return 0;
|
2015-08-29 16:21:15 +02:00
|
|
|
}
|
|
|
|
|
2016-12-24 02:00:24 +01:00
|
|
|
// any unallocated slot is assumed to have the default priority
|
|
|
|
if (m_file_priority.end_index() <= index) return default_piece_priority;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2008-07-20 18:00:08 +02:00
|
|
|
return m_file_priority[index];
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::file_priorities(aux::vector<int, file_index_t>* files) const
|
2008-07-20 18:00:08 +02:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2011-05-01 19:43:15 +02:00
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
|
|
|
files->resize(m_file_priority.size());
|
|
|
|
std::copy(m_file_priority.begin(), m_file_priority.end(), files->begin());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
files->clear();
|
2016-12-24 02:00:24 +01:00
|
|
|
files->resize(m_torrent_file->num_files(), default_piece_priority);
|
2014-07-05 16:10:25 +02:00
|
|
|
TORRENT_ASSERT(int(m_file_priority.size()) <= m_torrent_file->num_files());
|
2011-05-01 19:43:15 +02:00
|
|
|
std::copy(m_file_priority.begin(), m_file_priority.end(), files->begin());
|
2008-07-20 18:00:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::update_piece_priorities()
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
if (m_torrent_file->num_pieces() == 0) return;
|
2008-04-13 09:39:37 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool need_update = false;
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t position = 0;
|
2007-03-21 03:09:50 +01:00
|
|
|
// initialize the piece priorities to 0, then only allow
|
|
|
|
// setting higher priorities
|
2016-12-22 16:42:33 +01:00
|
|
|
aux::vector<int, piece_index_t> pieces(m_torrent_file->num_pieces(), 0);
|
2013-08-12 09:30:57 +02:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < fs.end_file(); ++i)
|
2007-03-20 02:59:00 +01:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
std::int64_t const size = m_torrent_file->files().file_size(i);
|
2007-04-18 00:56:14 +02:00
|
|
|
if (size == 0) continue;
|
|
|
|
position += size;
|
2015-08-29 16:21:15 +02:00
|
|
|
|
|
|
|
// pad files always have priority 0
|
2016-12-22 16:42:33 +01:00
|
|
|
int const file_prio
|
|
|
|
= fs.pad_file_at(i) ? 0
|
2016-12-24 02:00:24 +01:00
|
|
|
: i >= m_file_priority.end_index() ? default_piece_priority
|
2016-12-22 16:42:33 +01:00
|
|
|
: m_file_priority[i];
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (file_prio == 0)
|
|
|
|
{
|
2016-12-03 00:13:39 +01:00
|
|
|
// the pieces already start out as priority 0, no need to update
|
|
|
|
// the pieces vector in this case
|
2014-07-06 21:18:00 +02:00
|
|
|
need_update = true;
|
|
|
|
continue;
|
|
|
|
}
|
2008-09-19 21:46:04 +02:00
|
|
|
|
2007-03-21 03:09:50 +01:00
|
|
|
// mark all pieces of the file with this file's priority
|
|
|
|
// but only if the priority is higher than the pieces
|
|
|
|
// already set (to avoid problems with overlapping pieces)
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t start;
|
|
|
|
piece_index_t end;
|
|
|
|
std::tie(start, end) = file_piece_range_inclusive(fs, i);
|
|
|
|
|
2007-03-21 03:09:50 +01:00
|
|
|
// if one piece spans several files, we might
|
|
|
|
// come here several times with the same start_piece, end_piece
|
2016-12-24 02:00:24 +01:00
|
|
|
for (piece_index_t p = start; p < end; ++p)
|
|
|
|
pieces[p] = std::max(pieces[p], file_prio);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-12-03 00:13:39 +01:00
|
|
|
need_update = true;
|
2007-03-20 02:59:00 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
if (need_update) prioritize_pieces(pieces);
|
2007-03-20 02:59:00 +01:00
|
|
|
}
|
|
|
|
|
2008-04-13 09:39:37 +02:00
|
|
|
// this is called when piece priorities have been updated
|
2007-03-21 03:09:50 +01:00
|
|
|
// updates the interested flag in peers
|
2008-04-13 09:39:37 +02:00
|
|
|
void torrent::update_peer_interest(bool was_finished)
|
2007-03-21 03:09:50 +01:00
|
|
|
{
|
2008-09-18 01:14:59 +02:00
|
|
|
for (peer_iterator i = begin(); i != end();)
|
|
|
|
{
|
|
|
|
peer_connection* p = *i;
|
|
|
|
// update_interest may disconnect the peer and
|
|
|
|
// invalidate the iterator
|
|
|
|
++i;
|
2008-09-18 19:53:44 +02:00
|
|
|
p->update_interest();
|
2008-09-18 01:14:59 +02:00
|
|
|
}
|
2008-03-29 19:47:24 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("*** UPDATE_PEER_INTEREST [ finished: %d was_finished %d ]"
|
|
|
|
, is_finished(), was_finished);
|
|
|
|
}
|
2014-01-26 05:45:49 +01:00
|
|
|
#endif
|
|
|
|
|
2008-04-13 09:39:37 +02:00
|
|
|
// the torrent just became finished
|
|
|
|
if (is_finished() && !was_finished)
|
2008-08-18 22:02:50 +02:00
|
|
|
{
|
2008-04-13 09:39:37 +02:00
|
|
|
finished();
|
2008-08-18 22:02:50 +02:00
|
|
|
}
|
2008-05-20 05:21:45 +02:00
|
|
|
else if (!is_finished() && was_finished)
|
2008-08-18 22:02:50 +02:00
|
|
|
{
|
|
|
|
// if we used to be finished, but we aren't anymore
|
|
|
|
// we may need to connect to peers again
|
2008-05-20 05:21:45 +02:00
|
|
|
resume_download();
|
2008-08-18 22:02:50 +02:00
|
|
|
}
|
2007-03-21 03:09:50 +01:00
|
|
|
}
|
2007-03-20 02:59:00 +01:00
|
|
|
|
2004-09-12 12:12:16 +02:00
|
|
|
void torrent::replace_trackers(std::vector<announce_entry> const& urls)
|
|
|
|
{
|
2009-04-26 10:05:42 +02:00
|
|
|
m_trackers.clear();
|
|
|
|
std::remove_copy_if(urls.begin(), urls.end(), back_inserter(m_trackers)
|
2016-12-15 20:20:08 +01:00
|
|
|
, [](announce_entry const& e) { return e.url.empty(); });
|
2009-04-26 10:05:42 +02:00
|
|
|
|
2004-09-12 12:12:16 +02:00
|
|
|
m_last_working_tracker = -1;
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto& t : m_trackers)
|
2014-08-07 07:00:29 +02:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
if (t.source == 0) t.source = announce_entry::source_client;
|
|
|
|
t.complete_sent = is_seed();
|
2014-08-07 07:00:29 +02:00
|
|
|
}
|
2008-11-29 09:38:40 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (settings().get_bool(settings_pack::prefer_udp_trackers))
|
2008-12-08 10:13:21 +01:00
|
|
|
prioritize_udp_trackers();
|
|
|
|
|
2010-06-17 19:01:28 +02:00
|
|
|
if (!m_trackers.empty()) announce_with_tracker();
|
2013-02-20 09:10:31 +01:00
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2004-09-12 12:12:16 +02:00
|
|
|
}
|
|
|
|
|
2008-12-08 10:13:21 +01:00
|
|
|
void torrent::prioritize_udp_trackers()
|
|
|
|
{
|
|
|
|
// look for udp-trackers
|
2016-12-22 16:42:33 +01:00
|
|
|
for (auto i = m_trackers.begin(), end(m_trackers.end()); i != end; ++i)
|
2008-12-08 10:13:21 +01:00
|
|
|
{
|
|
|
|
if (i->url.substr(0, 6) != "udp://") continue;
|
|
|
|
// now, look for trackers with the same hostname
|
|
|
|
// that is has higher priority than this one
|
|
|
|
// if we find one, swap with the udp-tracker
|
2009-06-12 18:40:38 +02:00
|
|
|
error_code ec;
|
2008-12-08 10:13:21 +01:00
|
|
|
std::string udp_hostname;
|
2016-06-20 17:32:06 +02:00
|
|
|
using std::ignore;
|
|
|
|
std::tie(ignore, ignore, udp_hostname, ignore, ignore)
|
2009-06-12 18:40:38 +02:00
|
|
|
= parse_url_components(i->url, ec);
|
2016-12-22 16:42:33 +01:00
|
|
|
for (auto j = m_trackers.begin(); j != i; ++j)
|
2008-12-08 10:13:21 +01:00
|
|
|
{
|
|
|
|
std::string hostname;
|
2016-06-20 17:32:06 +02:00
|
|
|
std::tie(ignore, ignore, hostname, ignore, ignore)
|
2009-06-12 18:40:38 +02:00
|
|
|
= parse_url_components(j->url, ec);
|
2008-12-08 10:13:21 +01:00
|
|
|
if (hostname != udp_hostname) continue;
|
|
|
|
if (j->url.substr(0, 6) == "udp://") continue;
|
|
|
|
using std::swap;
|
|
|
|
using std::iter_swap;
|
|
|
|
swap(i->tier, j->tier);
|
|
|
|
iter_swap(i, j);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-26 02:17:58 +01:00
|
|
|
bool torrent::add_tracker(announce_entry const& url)
|
2008-11-26 02:42:14 +01:00
|
|
|
{
|
2016-10-07 21:36:40 +02:00
|
|
|
if(auto k = find_tracker(url.url))
|
2008-11-29 09:38:40 +01:00
|
|
|
{
|
|
|
|
k->source |= url.source;
|
2014-01-26 02:17:58 +01:00
|
|
|
return false;
|
2008-11-29 09:38:40 +01:00
|
|
|
}
|
2016-10-07 21:36:40 +02:00
|
|
|
auto k = std::upper_bound(m_trackers.begin(), m_trackers.end(), url
|
2016-05-25 06:31:52 +02:00
|
|
|
, [] (announce_entry const& lhs, announce_entry const& rhs)
|
|
|
|
{ return lhs.tier < rhs.tier; });
|
2008-11-26 02:42:14 +01:00
|
|
|
if (k - m_trackers.begin() < m_last_working_tracker) ++m_last_working_tracker;
|
2008-11-29 09:38:40 +01:00
|
|
|
k = m_trackers.insert(k, url);
|
|
|
|
if (k->source == 0) k->source = announce_entry::source_client;
|
2016-06-01 07:05:32 +02:00
|
|
|
if (!m_paused && !m_trackers.empty()) announce_with_tracker();
|
2014-01-26 02:17:58 +01:00
|
|
|
return true;
|
2008-11-26 02:42:14 +01:00
|
|
|
}
|
|
|
|
|
2009-07-23 06:38:52 +02:00
|
|
|
bool torrent::choke_peer(peer_connection& c)
|
2007-08-17 09:37:08 +02:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(!c.is_choked());
|
2009-01-28 07:14:21 +01:00
|
|
|
TORRENT_ASSERT(!c.ignore_unchoke_slots());
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_num_uploads > 0);
|
2009-07-23 06:38:52 +02:00
|
|
|
if (!c.send_choke()) return false;
|
2007-08-17 09:37:08 +02:00
|
|
|
--m_num_uploads;
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2009-07-23 06:38:52 +02:00
|
|
|
return true;
|
2007-08-17 09:37:08 +02:00
|
|
|
}
|
2015-06-10 07:22:01 +02:00
|
|
|
|
2010-10-04 06:06:14 +02:00
|
|
|
bool torrent::unchoke_peer(peer_connection& c, bool optimistic)
|
2007-08-17 09:37:08 +02:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2010-10-30 10:36:18 +02:00
|
|
|
TORRENT_ASSERT(!m_graceful_pause_mode);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(c.is_choked());
|
2009-01-28 07:14:21 +01:00
|
|
|
TORRENT_ASSERT(!c.ignore_unchoke_slots());
|
2010-10-04 06:06:14 +02:00
|
|
|
// when we're unchoking the optimistic slots, we might
|
|
|
|
// exceed the limit temporarily while we're iterating
|
|
|
|
// over the peers
|
|
|
|
if (m_num_uploads >= m_max_uploads && !optimistic) return false;
|
2008-08-29 19:21:56 +02:00
|
|
|
if (!c.send_unchoke()) return false;
|
2007-08-17 09:37:08 +02:00
|
|
|
++m_num_uploads;
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2007-08-17 09:37:08 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-22 05:47:43 +02:00
|
|
|
void torrent::trigger_unchoke()
|
|
|
|
{
|
2016-05-25 06:31:52 +02:00
|
|
|
m_ses.get_io_service().dispatch(std::bind(
|
|
|
|
&aux::session_interface::trigger_unchoke, std::ref(m_ses)));
|
2014-09-22 05:47:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::trigger_optimistic_unchoke()
|
|
|
|
{
|
2016-05-25 06:31:52 +02:00
|
|
|
m_ses.get_io_service().dispatch(std::bind(
|
|
|
|
&aux::session_interface::trigger_optimistic_unchoke, std::ref(m_ses)));
|
2014-09-22 05:47:43 +02:00
|
|
|
}
|
|
|
|
|
2007-07-06 19:15:35 +02:00
|
|
|
void torrent::cancel_block(piece_block block)
|
|
|
|
{
|
2008-03-14 11:17:27 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
for (auto p : m_connections)
|
2007-07-06 19:15:35 +02:00
|
|
|
{
|
2016-05-25 06:31:52 +02:00
|
|
|
p->cancel_request(block);
|
2007-07-06 19:15:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-10 07:52:07 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2015-09-02 07:30:40 +02:00
|
|
|
namespace {
|
|
|
|
std::string password_callback(int length, boost::asio::ssl::context::password_purpose p
|
|
|
|
, std::string pw)
|
|
|
|
{
|
|
|
|
TORRENT_UNUSED(length);
|
2015-08-21 10:05:51 +02:00
|
|
|
|
2015-09-02 07:30:40 +02:00
|
|
|
if (p != boost::asio::ssl::context::for_reading) return "";
|
|
|
|
return pw;
|
|
|
|
}
|
2011-09-12 05:51:49 +02:00
|
|
|
}
|
|
|
|
|
2011-09-10 07:52:07 +02:00
|
|
|
// certificate is a filename to a .pem file which is our
|
2011-09-12 05:51:49 +02:00
|
|
|
// certificate. The certificate must be signed by the root
|
|
|
|
// cert of the torrent file. any peer we connect to or that
|
|
|
|
// connect to use must present a valid certificate signed
|
|
|
|
// by the torrent root cert as well
|
|
|
|
void torrent::set_ssl_cert(std::string const& certificate
|
|
|
|
, std::string const& private_key
|
|
|
|
, std::string const& dh_params
|
|
|
|
, std::string const& passphrase)
|
2011-09-10 07:52:07 +02:00
|
|
|
{
|
2014-01-20 02:01:03 +01:00
|
|
|
if (!m_ssl_ctx)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle()
|
2016-10-02 21:27:50 +02:00
|
|
|
, errors::not_an_ssl_torrent, "");
|
2014-01-20 02:01:03 +01:00
|
|
|
return;
|
|
|
|
}
|
2011-09-12 05:51:49 +02:00
|
|
|
|
|
|
|
using boost::asio::ssl::context;
|
|
|
|
error_code ec;
|
2016-05-25 06:31:52 +02:00
|
|
|
m_ssl_ctx->set_password_callback(std::bind(&password_callback, _1, _2, passphrase), ec);
|
2011-09-12 05:51:49 +02:00
|
|
|
if (ec)
|
2011-09-10 07:52:07 +02:00
|
|
|
{
|
2011-09-12 05:51:49 +02:00
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, "");
|
2011-09-10 07:52:07 +02:00
|
|
|
}
|
|
|
|
m_ssl_ctx->use_certificate_file(certificate, context::pem, ec);
|
2011-09-12 05:51:49 +02:00
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, certificate);
|
2011-09-12 05:51:49 +02:00
|
|
|
}
|
2016-02-08 08:05:00 +01:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
debug_log("*** use certificate file: %s", ec.message().c_str());
|
2016-02-08 08:05:00 +01:00
|
|
|
#endif
|
2011-09-12 05:51:49 +02:00
|
|
|
m_ssl_ctx->use_private_key_file(private_key, context::pem, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, private_key);
|
2011-09-12 05:51:49 +02:00
|
|
|
}
|
2016-02-08 08:05:00 +01:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
debug_log("*** use private key file: %s", ec.message().c_str());
|
2016-02-08 08:05:00 +01:00
|
|
|
#endif
|
2011-09-12 05:51:49 +02:00
|
|
|
m_ssl_ctx->use_tmp_dh_file(dh_params, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, dh_params);
|
2011-09-12 05:51:49 +02:00
|
|
|
}
|
2016-02-08 08:05:00 +01:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
debug_log("*** use DH file: %s", ec.message().c_str());
|
2016-02-08 08:05:00 +01:00
|
|
|
#endif
|
2011-09-10 07:52:07 +02:00
|
|
|
}
|
2014-05-10 23:53:50 +02:00
|
|
|
|
|
|
|
void torrent::set_ssl_cert_buffer(std::string const& certificate
|
|
|
|
, std::string const& private_key
|
|
|
|
, std::string const& dh_params)
|
|
|
|
{
|
|
|
|
if (!m_ssl_ctx) return;
|
|
|
|
|
|
|
|
boost::asio::const_buffer certificate_buf(certificate.c_str(), certificate.size());
|
|
|
|
|
|
|
|
using boost::asio::ssl::context;
|
|
|
|
error_code ec;
|
|
|
|
m_ssl_ctx->use_certificate(certificate_buf, context::pem, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, "[certificate]");
|
2014-05-10 23:53:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
boost::asio::const_buffer private_key_buf(private_key.c_str(), private_key.size());
|
|
|
|
m_ssl_ctx->use_private_key(private_key_buf, context::pem, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, "[private key]");
|
2014-05-10 23:53:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
boost::asio::const_buffer dh_params_buf(dh_params.c_str(), dh_params.size());
|
|
|
|
m_ssl_ctx->use_tmp_dh(dh_params_buf, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, "[dh params]");
|
2014-05-10 23:53:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-10 07:52:07 +02:00
|
|
|
#endif
|
|
|
|
|
2008-04-07 04:51:21 +02:00
|
|
|
void torrent::remove_peer(peer_connection* p)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2016-07-09 22:26:26 +02:00
|
|
|
TORRENT_ASSERT(p != nullptr);
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2004-01-25 13:37:15 +01:00
|
|
|
|
2016-12-15 20:20:08 +01:00
|
|
|
auto const i = sorted_find(m_connections, p);
|
2007-04-12 12:21:55 +02:00
|
|
|
if (i == m_connections.end())
|
|
|
|
{
|
2016-05-02 18:36:21 +02:00
|
|
|
TORRENT_ASSERT_FAIL();
|
2007-04-12 12:21:55 +02:00
|
|
|
return;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2015-08-18 10:25:13 +02:00
|
|
|
torrent_peer* pp = p->peer_info_struct();
|
2005-10-16 23:14:08 +02:00
|
|
|
if (ready_for_connections())
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2016-06-20 17:32:06 +02:00
|
|
|
TORRENT_ASSERT(p->associated_torrent().lock().get() == nullptr
|
2013-01-02 08:48:09 +01:00
|
|
|
|| p->associated_torrent().lock().get() == this);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-04-15 04:14:02 +02:00
|
|
|
if (p->is_seed())
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (has_picker())
|
2007-04-15 04:14:02 +02:00
|
|
|
{
|
2015-08-18 10:25:13 +02:00
|
|
|
m_picker->dec_refcount_all(pp);
|
2007-04-15 04:14:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (has_picker())
|
2007-04-15 04:14:02 +02:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
auto const& pieces = p->get_bitfield();
|
|
|
|
TORRENT_ASSERT(pieces.count() <= int(pieces.size()));
|
2015-08-18 10:25:13 +02:00
|
|
|
m_picker->dec_refcount(pieces, pp);
|
2007-04-15 04:14:02 +02:00
|
|
|
}
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2009-01-28 07:14:21 +01:00
|
|
|
if (!p->is_choked() && !p->ignore_unchoke_slots())
|
2008-10-14 03:00:05 +02:00
|
|
|
{
|
2007-08-17 09:37:08 +02:00
|
|
|
--m_num_uploads;
|
2014-09-22 05:47:43 +02:00
|
|
|
trigger_unchoke();
|
2008-10-14 03:00:05 +02:00
|
|
|
}
|
|
|
|
|
2009-07-23 06:38:52 +02:00
|
|
|
if (pp)
|
2008-10-14 03:00:05 +02:00
|
|
|
{
|
2009-07-23 06:38:52 +02:00
|
|
|
if (pp->optimistically_unchoked)
|
2014-09-22 09:34:10 +02:00
|
|
|
{
|
|
|
|
pp->optimistically_unchoked = false;
|
|
|
|
m_stats_counters.inc_stats_counter(
|
|
|
|
counters::num_peers_up_unchoked_optimistic, -1);
|
2014-09-22 05:47:43 +02:00
|
|
|
trigger_optimistic_unchoke();
|
2014-09-22 09:34:10 +02:00
|
|
|
}
|
2009-07-23 06:38:52 +02:00
|
|
|
|
|
|
|
TORRENT_ASSERT(pp->prev_amount_upload == 0);
|
|
|
|
TORRENT_ASSERT(pp->prev_amount_download == 0);
|
2011-06-01 08:47:57 +02:00
|
|
|
pp->prev_amount_download += p->statistics().total_payload_download() >> 10;
|
|
|
|
pp->prev_amount_upload += p->statistics().total_payload_upload() >> 10;
|
2014-09-24 11:03:57 +02:00
|
|
|
|
|
|
|
if (pp->seed)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_num_seeds > 0);
|
|
|
|
--m_num_seeds;
|
|
|
|
}
|
2008-10-14 03:00:05 +02:00
|
|
|
}
|
2007-08-17 09:37:08 +02:00
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
torrent_state st = get_peer_list_state();
|
2014-10-26 08:34:31 +01:00
|
|
|
if (m_peer_list) m_peer_list->connection_closed(*p, m_ses.session_time(), &st);
|
2014-07-06 21:18:00 +02:00
|
|
|
peers_erased(st.erased);
|
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
p->set_peer_info(nullptr);
|
2007-10-31 10:48:20 +01:00
|
|
|
TORRENT_ASSERT(i != m_connections.end());
|
2003-10-23 01:00:57 +02:00
|
|
|
m_connections.erase(i);
|
2015-11-20 06:51:58 +01:00
|
|
|
|
|
|
|
if (m_graceful_pause_mode && m_connections.empty())
|
|
|
|
{
|
|
|
|
// we're in graceful pause mode and this was the last peer we
|
|
|
|
// disconnected. This will clear the graceful_pause_mode and post the
|
|
|
|
// torrent_paused_alert.
|
|
|
|
TORRENT_ASSERT(is_paused());
|
2015-12-04 07:08:01 +01:00
|
|
|
|
|
|
|
// this will post torrent_paused alert
|
2016-06-01 07:05:32 +02:00
|
|
|
set_paused(true);
|
2015-11-20 06:51:58 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
|
|
|
update_want_tick();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2016-05-22 01:05:42 +02:00
|
|
|
void torrent::remove_web_seed_iter(std::list<web_seed_t>::iterator web)
|
2011-04-29 10:09:03 +02:00
|
|
|
{
|
|
|
|
if (web->resolving)
|
|
|
|
{
|
|
|
|
web->removed = true;
|
|
|
|
}
|
2015-08-23 11:01:55 +02:00
|
|
|
else
|
2014-12-31 10:30:23 +01:00
|
|
|
{
|
2015-08-23 11:01:55 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("removing web seed: \"%s\"", web->url.c_str());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
peer_connection* peer = static_cast<peer_connection*>(web->peer_info.connection);
|
2016-09-07 23:51:18 +02:00
|
|
|
if (peer != nullptr)
|
2015-08-23 11:01:55 +02:00
|
|
|
{
|
|
|
|
// if we have a connection for this web seed, we also need to
|
|
|
|
// disconnect it and clear its reference to the peer_info object
|
|
|
|
// that's part of the web_seed_t we're about to remove
|
|
|
|
TORRENT_ASSERT(peer->m_in_use == 1337);
|
|
|
|
peer->disconnect(boost::asio::error::operation_aborted, op_bittorrent);
|
2016-07-09 22:26:26 +02:00
|
|
|
peer->set_peer_info(nullptr);
|
2015-08-23 11:01:55 +02:00
|
|
|
}
|
|
|
|
if (has_picker()) picker().clear_peer(&web->peer_info);
|
|
|
|
|
|
|
|
m_web_seeds.erase(web);
|
2012-04-14 08:32:35 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_tick();
|
2011-04-29 10:09:03 +02:00
|
|
|
}
|
|
|
|
|
2014-12-31 23:05:34 +01:00
|
|
|
void torrent::connect_to_url_seed(std::list<web_seed_t>::iterator web)
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2010-02-12 07:10:20 +01:00
|
|
|
TORRENT_ASSERT(!web->resolving);
|
|
|
|
if (web->resolving) return;
|
|
|
|
|
2011-04-30 22:05:23 +02:00
|
|
|
if (int(m_connections.size()) >= m_max_connections
|
2015-05-16 08:33:37 +02:00
|
|
|
|| m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit))
|
2011-04-30 22:05:23 +02:00
|
|
|
return;
|
|
|
|
|
2008-04-09 09:51:41 +02:00
|
|
|
std::string protocol;
|
|
|
|
std::string auth;
|
|
|
|
std::string hostname;
|
|
|
|
int port;
|
|
|
|
std::string path;
|
2009-06-12 18:40:38 +02:00
|
|
|
error_code ec;
|
2016-06-20 17:32:06 +02:00
|
|
|
std::tie(protocol, auth, hostname, port, path)
|
2010-02-12 07:10:20 +01:00
|
|
|
= parse_url_components(web->url, ec);
|
2013-02-26 06:57:29 +01:00
|
|
|
if (port == -1)
|
|
|
|
{
|
|
|
|
port = protocol == "http" ? 80 : 443;
|
|
|
|
}
|
2008-04-09 09:51:41 +02:00
|
|
|
|
2009-06-12 18:40:38 +02:00
|
|
|
if (ec)
|
2008-05-17 16:19:34 +02:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
debug_log("failed to parse web seed url: %s", ec.message().c_str());
|
2008-05-17 16:19:34 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2010-10-17 18:15:32 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
|
|
|
, web->url, ec);
|
2010-10-17 18:15:32 +02:00
|
|
|
}
|
2008-05-17 16:19:34 +02:00
|
|
|
// never try it again
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2008-05-17 16:19:34 +02:00
|
|
|
return;
|
|
|
|
}
|
2012-03-24 02:29:31 +01:00
|
|
|
|
|
|
|
if (web->peer_info.banned)
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("banned web seed: %s", web->url.c_str());
|
2012-03-24 02:29:31 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2012-03-24 02:29:31 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url
|
2016-10-02 21:27:50 +02:00
|
|
|
, libtorrent::errors::peer_banned);
|
2012-03-24 02:29:31 +01:00
|
|
|
}
|
|
|
|
// never try it again
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2012-03-24 02:29:31 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-08-23 11:01:55 +02:00
|
|
|
|
2008-04-09 09:51:41 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
|
|
|
if (protocol != "http" && protocol != "https")
|
|
|
|
#else
|
|
|
|
if (protocol != "http")
|
|
|
|
#endif
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2008-04-09 09:51:41 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url, errors::unsupported_url_protocol);
|
2008-04-09 09:51:41 +02:00
|
|
|
}
|
|
|
|
// never try it again
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2008-04-09 09:51:41 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hostname.empty())
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2008-04-09 09:51:41 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url
|
|
|
|
, errors::invalid_hostname);
|
2008-04-09 09:51:41 +02:00
|
|
|
}
|
|
|
|
// never try it again
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2008-04-09 09:51:41 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (port == 0)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2008-04-09 09:51:41 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url
|
|
|
|
, errors::invalid_port);
|
2008-04-09 09:51:41 +02:00
|
|
|
}
|
|
|
|
// never try it again
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2008-04-09 09:51:41 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-25 17:17:25 +01:00
|
|
|
if (m_ses.get_port_filter().access(std::uint16_t(port)) & port_filter::blocked)
|
2010-08-03 11:08:37 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2010-08-03 11:08:37 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
|
|
|
, web->url, errors::port_blocked);
|
2010-08-03 11:08:37 +02:00
|
|
|
}
|
|
|
|
// never try it again
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2010-08-03 11:08:37 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-28 04:20:13 +02:00
|
|
|
if (!web->endpoints.empty())
|
2010-10-30 09:11:04 +02:00
|
|
|
{
|
2014-09-28 04:20:13 +02:00
|
|
|
connect_web_seed(web, web->endpoints.front());
|
2010-10-30 09:11:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-25 04:18:10 +02:00
|
|
|
aux::proxy_settings const& ps = m_ses.proxy();
|
2015-10-03 17:20:10 +02:00
|
|
|
if ((ps.type == settings_pack::http
|
2014-07-06 21:18:00 +02:00
|
|
|
|| ps.type == settings_pack::http_pw)
|
2015-10-03 17:20:10 +02:00
|
|
|
&& ps.proxy_peer_connections)
|
2007-04-25 20:26:35 +02:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-03-07 20:19:17 +01:00
|
|
|
debug_log("resolving proxy for web seed: %s", web->url.c_str());
|
|
|
|
#endif
|
|
|
|
|
2016-10-06 06:08:14 +02:00
|
|
|
auto self = shared_from_this();
|
|
|
|
std::uint16_t const proxy_port = ps.port;
|
|
|
|
|
2007-04-25 20:26:35 +02:00
|
|
|
// use proxy
|
2011-09-30 11:09:33 +02:00
|
|
|
web->resolving = true;
|
2014-12-17 03:44:27 +01:00
|
|
|
m_ses.async_resolve(ps.hostname, resolver_interface::abort_on_shutdown
|
2016-10-06 06:08:14 +02:00
|
|
|
, [self,web,proxy_port](error_code const& e, std::vector<address> const& addrs)
|
|
|
|
{
|
|
|
|
self->wrap(&torrent::on_proxy_name_lookup, e, addrs, web, proxy_port);
|
|
|
|
});
|
2007-04-25 20:26:35 +02:00
|
|
|
}
|
2010-08-03 11:08:37 +02:00
|
|
|
else if (ps.proxy_hostnames
|
2014-07-06 21:18:00 +02:00
|
|
|
&& (ps.type == settings_pack::socks5
|
2015-10-03 17:20:10 +02:00
|
|
|
|| ps.type == settings_pack::socks5_pw)
|
|
|
|
&& ps.proxy_peer_connections)
|
2010-08-03 11:08:37 +02:00
|
|
|
{
|
2016-11-25 17:17:25 +01:00
|
|
|
connect_web_seed(web, tcp::endpoint(address(), std::uint16_t(port)));
|
2010-08-03 11:08:37 +02:00
|
|
|
}
|
2007-04-25 20:26:35 +02:00
|
|
|
else
|
2006-07-27 20:07:51 +02:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-06-17 05:45:05 +02:00
|
|
|
debug_log("resolving web seed: \"%s\" %s", hostname.c_str(), web->url.c_str());
|
2014-03-07 20:19:17 +01:00
|
|
|
#endif
|
|
|
|
|
2016-10-06 06:08:14 +02:00
|
|
|
auto self = shared_from_this();
|
2010-10-30 21:45:50 +02:00
|
|
|
web->resolving = true;
|
2016-10-06 06:08:14 +02:00
|
|
|
|
2014-12-17 03:44:27 +01:00
|
|
|
m_ses.async_resolve(hostname, resolver_interface::abort_on_shutdown
|
2016-10-06 06:08:14 +02:00
|
|
|
, [self,web,port](error_code const& e, std::vector<address> const& addrs)
|
|
|
|
{
|
|
|
|
self->wrap(&torrent::on_name_lookup, e, addrs, port, web);
|
|
|
|
});
|
2006-07-27 20:07:51 +02:00
|
|
|
}
|
2007-02-12 06:46:29 +01:00
|
|
|
}
|
|
|
|
|
2014-10-05 03:23:22 +02:00
|
|
|
void torrent::on_proxy_name_lookup(error_code const& e
|
|
|
|
, std::vector<address> const& addrs
|
2016-10-23 04:00:47 +02:00
|
|
|
, std::list<web_seed_t>::iterator web, int port) try
|
2007-02-12 06:46:29 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-02-12 06:46:29 +01:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2011-09-30 11:09:33 +02:00
|
|
|
TORRENT_ASSERT(web->resolving == true);
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("completed resolve proxy hostname for: %s", web->url.c_str());
|
2016-09-14 04:46:07 +02:00
|
|
|
if (e && should_log())
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("proxy name lookup error: %s", e.message().c_str());
|
2007-02-12 06:46:29 +01:00
|
|
|
#endif
|
2011-09-30 11:09:33 +02:00
|
|
|
web->resolving = false;
|
|
|
|
|
|
|
|
if (web->removed)
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("removed web seed");
|
2011-09-30 11:09:33 +02:00
|
|
|
#endif
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2011-09-30 11:09:33 +02:00
|
|
|
return;
|
|
|
|
}
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2008-12-20 19:27:09 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2014-10-05 03:23:22 +02:00
|
|
|
if (e || addrs.empty())
|
2007-02-12 06:46:29 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2007-02-12 06:46:29 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
|
|
|
, web->url, e);
|
2007-02-12 06:46:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// the name lookup failed for the http host. Don't try
|
|
|
|
// this host again
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2007-02-12 06:46:29 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_ses.is_aborted()) return;
|
|
|
|
|
2011-04-30 22:05:23 +02:00
|
|
|
if (int(m_connections.size()) >= m_max_connections
|
2015-05-16 08:33:37 +02:00
|
|
|
|| m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit))
|
2011-04-30 22:05:23 +02:00
|
|
|
return;
|
|
|
|
|
2016-11-25 17:17:25 +01:00
|
|
|
tcp::endpoint a(addrs[0], std::uint16_t(port));
|
2007-05-22 22:44:18 +02:00
|
|
|
|
2007-02-12 06:46:29 +01:00
|
|
|
std::string hostname;
|
2009-06-12 18:40:38 +02:00
|
|
|
error_code ec;
|
2013-02-26 06:57:29 +01:00
|
|
|
std::string protocol;
|
2016-12-15 20:20:08 +01:00
|
|
|
std::tie(protocol, std::ignore, hostname, port, std::ignore)
|
2010-02-12 07:10:20 +01:00
|
|
|
= parse_url_components(web->url, ec);
|
2013-02-26 06:57:29 +01:00
|
|
|
if (port == -1) port = protocol == "http" ? 80 : 443;
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2009-06-12 18:40:38 +02:00
|
|
|
if (ec)
|
2008-05-17 16:19:34 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2008-05-17 16:19:34 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
|
|
|
, web->url, ec);
|
2008-05-17 16:19:34 +02:00
|
|
|
}
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2008-05-17 16:19:34 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-16 08:33:37 +02:00
|
|
|
if (m_ip_filter && m_ip_filter->access(a.address()) & ip_filter::blocked)
|
2007-04-17 07:56:43 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<peer_blocked_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-05-23 01:56:14 +02:00
|
|
|
, a, peer_blocked_alert::ip_filter);
|
2007-04-17 07:56:43 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-06 06:08:14 +02:00
|
|
|
auto self = shared_from_this();
|
2010-10-30 21:45:50 +02:00
|
|
|
web->resolving = true;
|
2014-12-17 03:44:27 +01:00
|
|
|
m_ses.async_resolve(hostname, resolver_interface::abort_on_shutdown
|
2016-11-25 17:17:25 +01:00
|
|
|
, [self, web, port](error_code const& err, std::vector<address> const& addr)
|
2016-10-06 06:08:14 +02:00
|
|
|
{
|
|
|
|
self->wrap(&torrent::on_name_lookup, err, addr, port, web);
|
|
|
|
});
|
2007-02-12 06:46:29 +01:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2014-10-05 03:23:22 +02:00
|
|
|
void torrent::on_name_lookup(error_code const& e
|
|
|
|
, std::vector<address> const& addrs
|
|
|
|
, int port
|
2016-10-23 04:00:47 +02:00
|
|
|
, std::list<web_seed_t>::iterator web) try
|
2007-02-12 06:46:29 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-02-12 06:46:29 +01:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2010-10-30 09:11:04 +02:00
|
|
|
TORRENT_ASSERT(web->resolving == true);
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("completed resolve: %s", web->url.c_str());
|
2007-02-12 06:46:29 +01:00
|
|
|
#endif
|
2010-02-12 07:10:20 +01:00
|
|
|
web->resolving = false;
|
2011-04-29 10:09:03 +02:00
|
|
|
if (web->removed)
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("removed web seed");
|
2011-04-29 10:09:03 +02:00
|
|
|
#endif
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(web);
|
2011-04-29 10:09:03 +02:00
|
|
|
return;
|
|
|
|
}
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2008-12-20 19:27:09 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2014-10-05 03:23:22 +02:00
|
|
|
if (e || addrs.empty())
|
2007-02-12 06:46:29 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url, e);
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("*** HOSTNAME LOOKUP FAILED: %s: (%d) %s"
|
|
|
|
, web->url.c_str(), e.value(), e.message().c_str());
|
|
|
|
}
|
2007-02-12 06:46:29 +01:00
|
|
|
#endif
|
|
|
|
|
2009-05-25 09:04:14 +02:00
|
|
|
// unavailable, retry in 30 minutes
|
2015-03-12 05:34:54 +01:00
|
|
|
web->retry = aux::time_now() + minutes(30);
|
2007-02-12 06:46:29 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const& addr : addrs)
|
2014-09-28 04:20:13 +02:00
|
|
|
{
|
|
|
|
// fill in the peer struct's address field
|
2016-12-15 20:20:08 +01:00
|
|
|
web->endpoints.push_back(tcp::endpoint(addr, std::uint16_t(port)));
|
2014-03-07 20:19:17 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
2016-12-15 20:20:08 +01:00
|
|
|
debug_log(" -> %s", print_endpoint(tcp::endpoint(addr, std::uint16_t(port))).c_str());
|
2014-09-28 04:20:13 +02:00
|
|
|
#endif
|
|
|
|
}
|
2014-03-07 20:19:17 +01:00
|
|
|
|
2011-04-30 22:05:23 +02:00
|
|
|
if (int(m_connections.size()) >= m_max_connections
|
2015-05-16 08:33:37 +02:00
|
|
|
|| m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit))
|
2011-04-30 22:05:23 +02:00
|
|
|
return;
|
|
|
|
|
2014-09-28 04:20:13 +02:00
|
|
|
connect_web_seed(web, web->endpoints.front());
|
2010-08-03 11:08:37 +02:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2014-12-31 23:05:34 +01:00
|
|
|
void torrent::connect_web_seed(std::list<web_seed_t>::iterator web, tcp::endpoint a)
|
2010-08-03 11:08:37 +02:00
|
|
|
{
|
2014-09-24 11:03:57 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2015-05-16 08:33:37 +02:00
|
|
|
if (m_ip_filter && m_ip_filter->access(a.address()) & ip_filter::blocked)
|
2007-02-12 06:46:29 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<peer_blocked_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-05-23 01:56:14 +02:00
|
|
|
, a, peer_blocked_alert::ip_filter);
|
2007-02-12 06:46:29 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-05-18 07:04:55 +02:00
|
|
|
|
2010-10-30 09:11:04 +02:00
|
|
|
TORRENT_ASSERT(web->resolving == false);
|
2016-07-09 22:26:26 +02:00
|
|
|
TORRENT_ASSERT(web->peer_info.connection == nullptr);
|
2010-10-30 09:11:04 +02:00
|
|
|
|
2013-10-04 06:20:50 +02:00
|
|
|
if (a.address().is_v4())
|
|
|
|
{
|
|
|
|
web->peer_info.addr = a.address().to_v4();
|
|
|
|
web->peer_info.port = a.port();
|
|
|
|
}
|
2010-02-12 07:10:20 +01:00
|
|
|
|
|
|
|
if (is_paused()) return;
|
|
|
|
if (m_ses.is_aborted()) return;
|
|
|
|
|
2016-10-03 08:08:03 +02:00
|
|
|
// this web seed may have redirected all files to other URLs, leaving it
|
|
|
|
// having no file left, and there's no longer any point in connecting to
|
|
|
|
// it.
|
|
|
|
if (!web->have_files.empty()
|
|
|
|
&& web->have_files.none_set()) return;
|
|
|
|
|
2016-08-31 18:45:45 +02:00
|
|
|
std::shared_ptr<socket_type> s
|
|
|
|
= std::make_shared<socket_type>(m_ses.get_io_service());
|
2008-04-07 04:51:21 +02:00
|
|
|
if (!s) return;
|
2015-05-18 07:04:55 +02:00
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
void* userdata = nullptr;
|
2010-10-12 10:57:43 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2015-10-03 17:20:10 +02:00
|
|
|
const bool ssl = string_begins_no_case("https://", web->url.c_str());
|
2011-09-10 07:52:07 +02:00
|
|
|
if (ssl)
|
|
|
|
{
|
|
|
|
userdata = m_ssl_ctx.get();
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!userdata) userdata = m_ses.ssl_ctx();
|
2011-09-10 07:52:07 +02:00
|
|
|
}
|
2010-10-12 10:57:43 +02:00
|
|
|
#endif
|
2015-08-25 04:18:10 +02:00
|
|
|
bool ret = instantiate_connection(m_ses.get_io_service(), m_ses.proxy()
|
2016-07-09 22:26:26 +02:00
|
|
|
, *s, userdata, nullptr, true, false);
|
2008-08-19 16:53:50 +02:00
|
|
|
(void)ret;
|
2007-10-22 06:17:26 +02:00
|
|
|
TORRENT_ASSERT(ret);
|
|
|
|
|
2012-01-29 03:27:55 +01:00
|
|
|
if (s->get<http_stream>())
|
2007-04-25 20:26:35 +02:00
|
|
|
{
|
|
|
|
// the web seed connection will talk immediately to
|
|
|
|
// the proxy, without requiring CONNECT support
|
2008-10-18 09:12:04 +02:00
|
|
|
s->get<http_stream>()->set_no_connect(true);
|
2007-04-25 20:26:35 +02:00
|
|
|
}
|
2008-02-28 08:34:07 +01:00
|
|
|
|
2012-01-29 03:27:55 +01:00
|
|
|
std::string hostname;
|
|
|
|
error_code ec;
|
2016-07-10 13:34:45 +02:00
|
|
|
using std::ignore;
|
2016-06-20 17:32:06 +02:00
|
|
|
std::tie(ignore, ignore, hostname, ignore, ignore)
|
2012-01-29 03:27:55 +01:00
|
|
|
= parse_url_components(web->url, ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url, ec);
|
2012-01-29 03:27:55 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-06 07:08:05 +02:00
|
|
|
bool const is_ip = is_ip_address(hostname.c_str());
|
|
|
|
if (is_ip) a.address(address::from_string(hostname.c_str(), ec));
|
|
|
|
bool const proxy_hostnames = settings().get_bool(settings_pack::proxy_hostnames)
|
|
|
|
&& !is_ip;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (proxy_hostnames
|
2015-10-03 17:20:10 +02:00
|
|
|
&& (s->get<socks5_stream>()
|
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2016-09-07 23:51:18 +02:00
|
|
|
|| s->get<ssl_stream<socks5_stream>>()
|
2015-10-03 17:20:10 +02:00
|
|
|
#endif
|
|
|
|
))
|
2010-08-03 11:08:37 +02:00
|
|
|
{
|
|
|
|
// we're using a socks proxy and we're resolving
|
|
|
|
// hostnames through it
|
2011-09-10 07:52:07 +02:00
|
|
|
socks5_stream* str =
|
2010-10-12 10:57:43 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2016-09-07 23:51:18 +02:00
|
|
|
ssl ? &s->get<ssl_stream<socks5_stream>>()->next_layer() :
|
2010-10-12 10:57:43 +02:00
|
|
|
#endif
|
2011-09-10 07:52:07 +02:00
|
|
|
s->get<socks5_stream>();
|
2015-10-03 17:20:10 +02:00
|
|
|
TORRENT_ASSERT_VAL(str, s->type_name());
|
2010-08-03 11:08:37 +02:00
|
|
|
|
2010-10-12 10:57:43 +02:00
|
|
|
str->set_dst_name(hostname);
|
2010-08-03 11:08:37 +02:00
|
|
|
}
|
|
|
|
|
2012-04-05 06:17:19 +02:00
|
|
|
setup_ssl_hostname(*s, hostname, ec);
|
2012-01-29 03:27:55 +01:00
|
|
|
if (ec)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<url_seed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url, ec);
|
2012-01-29 03:27:55 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<peer_connection> c;
|
2014-07-14 06:32:41 +02:00
|
|
|
peer_connection_args pack;
|
|
|
|
pack.ses = &m_ses;
|
2015-05-16 08:33:37 +02:00
|
|
|
pack.sett = &settings();
|
2014-07-14 06:32:41 +02:00
|
|
|
pack.stats_counters = &m_ses.stats_counters();
|
|
|
|
pack.disk_thread = &m_ses.disk_thread();
|
|
|
|
pack.ios = &m_ses.get_io_service();
|
|
|
|
pack.tor = shared_from_this();
|
|
|
|
pack.s = s;
|
2014-09-28 07:41:50 +02:00
|
|
|
pack.endp = a;
|
2014-07-14 06:32:41 +02:00
|
|
|
pack.peerinfo = &web->peer_info;
|
2010-02-12 07:10:20 +01:00
|
|
|
if (web->type == web_seed_entry::url_seed)
|
2008-12-30 04:54:07 +01:00
|
|
|
{
|
2016-09-01 03:42:18 +02:00
|
|
|
c = std::make_shared<web_peer_connection>(pack, *web);
|
2008-12-30 04:54:07 +01:00
|
|
|
}
|
2010-02-12 07:10:20 +01:00
|
|
|
else if (web->type == web_seed_entry::http_seed)
|
2008-12-30 04:54:07 +01:00
|
|
|
{
|
2016-09-01 03:42:18 +02:00
|
|
|
c = std::make_shared<http_seed_connection>(pack, *web);
|
2008-12-30 04:54:07 +01:00
|
|
|
}
|
2008-04-07 04:51:21 +02:00
|
|
|
if (!c) return;
|
2010-02-12 07:10:20 +01:00
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2007-02-12 06:46:29 +01:00
|
|
|
c->m_in_constructor = false;
|
|
|
|
#endif
|
|
|
|
|
2007-04-02 22:00:24 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-09-01 21:04:58 +02:00
|
|
|
for (auto const& ext : m_extensions)
|
2007-04-02 22:00:24 +02:00
|
|
|
{
|
2016-08-17 20:30:24 +02:00
|
|
|
std::shared_ptr<peer_plugin>
|
2016-07-10 13:31:58 +02:00
|
|
|
pp(ext->new_connection(peer_connection_handle(c->self())));
|
2007-04-02 22:00:24 +02:00
|
|
|
if (pp) c->add_extension(pp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
TORRENT_ASSERT(!c->m_in_constructor);
|
|
|
|
// add the newly connected peer to this torrent's peer list
|
|
|
|
sorted_insert(m_connections, c.get());
|
|
|
|
update_want_peers();
|
|
|
|
update_want_tick();
|
|
|
|
m_ses.insert_peer(c);
|
2010-02-12 07:10:20 +01:00
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
if (web->peer_info.seed)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_num_seeds < 0xffff);
|
|
|
|
++m_num_seeds;
|
|
|
|
}
|
2014-09-24 11:03:57 +02:00
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
TORRENT_ASSERT(!web->peer_info.connection);
|
|
|
|
web->peer_info.connection = c.get();
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2016-10-23 04:00:47 +02:00
|
|
|
web->peer_info.in_use = true;
|
2012-03-24 02:29:31 +01:00
|
|
|
#endif
|
2010-02-12 07:10:20 +01:00
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
c->add_stat(std::int64_t(web->peer_info.prev_amount_download) << 10
|
|
|
|
, std::int64_t(web->peer_info.prev_amount_upload) << 10);
|
|
|
|
web->peer_info.prev_amount_download = 0;
|
|
|
|
web->peer_info.prev_amount_upload = 0;
|
2015-12-15 00:31:49 +01:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-10-23 04:00:47 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("web seed connection started: [%s] %s"
|
|
|
|
, print_endpoint(a).c_str(), web->url.c_str());
|
|
|
|
}
|
2010-10-17 18:15:32 +02:00
|
|
|
#endif
|
2010-10-30 09:11:04 +02:00
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
c->start();
|
2010-10-30 09:11:04 +02:00
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
if (c->is_disconnecting()) return;
|
2012-10-21 22:24:14 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-10-23 04:00:47 +02:00
|
|
|
debug_log("START queue peer [%p] (%d)", static_cast<void*>(c.get())
|
|
|
|
, num_peers());
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2008-04-07 04:51:21 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2016-08-17 23:26:35 +02:00
|
|
|
std::shared_ptr<const torrent_info> torrent::get_torrent_copy()
|
2012-10-02 18:07:55 +02:00
|
|
|
{
|
2016-08-17 23:26:35 +02:00
|
|
|
if (!m_torrent_file->is_valid()) return std::shared_ptr<const torrent_info>();
|
2014-07-06 21:18:00 +02:00
|
|
|
return m_torrent_file;
|
2012-10-02 18:07:55 +02:00
|
|
|
}
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
void torrent::write_resume_data(entry& ret) const
|
|
|
|
{
|
2009-05-07 00:36:24 +02:00
|
|
|
using namespace libtorrent::detail; // for write_*_endpoint()
|
2008-04-13 20:54:36 +02:00
|
|
|
ret["file-format"] = "libtorrent resume file";
|
|
|
|
ret["file-version"] = 1;
|
2009-06-27 23:47:30 +02:00
|
|
|
ret["libtorrent-version"] = LIBTORRENT_VERSION;
|
2016-02-19 02:52:09 +01:00
|
|
|
ret["allocation"] = m_storage_mode == storage_mode_allocate
|
|
|
|
? "allocate" : "sparse";
|
2008-04-13 20:54:36 +02:00
|
|
|
|
2008-04-16 08:31:05 +02:00
|
|
|
ret["total_uploaded"] = m_total_uploaded;
|
|
|
|
ret["total_downloaded"] = m_total_downloaded;
|
|
|
|
|
2014-08-27 08:36:28 +02:00
|
|
|
ret["active_time"] = active_time();
|
|
|
|
ret["finished_time"] = finished_time();
|
|
|
|
ret["seeding_time"] = seeding_time();
|
2010-03-19 19:39:51 +01:00
|
|
|
ret["last_seen_complete"] = m_last_seen_complete;
|
2008-05-18 21:31:06 +02:00
|
|
|
|
2013-02-04 08:17:31 +01:00
|
|
|
ret["num_complete"] = m_complete;
|
2010-09-06 06:02:15 +02:00
|
|
|
ret["num_incomplete"] = m_incomplete;
|
2013-02-04 08:17:31 +01:00
|
|
|
ret["num_downloaded"] = m_downloaded;
|
2008-09-23 05:52:49 +02:00
|
|
|
|
|
|
|
ret["sequential_download"] = m_sequential_download;
|
2009-03-21 05:33:53 +01:00
|
|
|
|
|
|
|
ret["seed_mode"] = m_seed_mode;
|
2009-11-18 18:40:38 +01:00
|
|
|
ret["super_seeding"] = m_super_seeding;
|
2010-03-09 04:21:35 +01:00
|
|
|
|
|
|
|
ret["added_time"] = m_added_time;
|
|
|
|
ret["completed_time"] = m_completed_time;
|
2010-07-17 09:25:28 +02:00
|
|
|
|
2014-06-13 00:39:41 +02:00
|
|
|
ret["save_path"] = m_save_path;
|
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2011-01-18 04:41:54 +01:00
|
|
|
if (!m_url.empty()) ret["url"] = m_url;
|
|
|
|
if (!m_uuid.empty()) ret["uuid"] = m_uuid;
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2015-05-16 08:33:37 +02:00
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
const sha1_hash& info_hash = torrent_file().info_hash();
|
2015-08-02 05:57:11 +02:00
|
|
|
ret["info-hash"] = info_hash.to_string();
|
2008-04-13 20:54:36 +02:00
|
|
|
|
2011-02-26 08:48:05 +01:00
|
|
|
if (valid_metadata())
|
|
|
|
{
|
|
|
|
if (m_magnet_link || (m_save_resume_flags & torrent_handle::save_info_dict))
|
2016-05-06 03:38:57 +02:00
|
|
|
{
|
|
|
|
boost::shared_array<char> const info = torrent_file().metadata();
|
|
|
|
int const size = torrent_file().metadata_size();
|
|
|
|
ret["info"].preformatted().assign(&info[0], &info[0] + size);
|
|
|
|
}
|
2011-02-26 08:48:05 +01:00
|
|
|
}
|
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
// blocks per piece
|
2016-08-22 17:02:51 +02:00
|
|
|
int num_blocks_per_piece = torrent_file().piece_length() / block_size();
|
2008-04-13 20:54:36 +02:00
|
|
|
ret["blocks per piece"] = num_blocks_per_piece;
|
|
|
|
|
2009-03-13 07:09:39 +01:00
|
|
|
if (m_torrent_file->is_merkle_torrent())
|
|
|
|
{
|
|
|
|
// we need to save the whole merkle hash tree
|
|
|
|
// in order to resume
|
|
|
|
std::string& tree_str = ret["merkle tree"].string();
|
|
|
|
std::vector<sha1_hash> const& tree = m_torrent_file->merkle_tree();
|
|
|
|
tree_str.resize(tree.size() * 20);
|
|
|
|
std::memcpy(&tree_str[0], &tree[0], tree.size() * 20);
|
|
|
|
}
|
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
// if this torrent is a seed, we won't have a piece picker
|
2014-07-06 21:18:00 +02:00
|
|
|
// if we don't have anything, we may also not have a picker
|
|
|
|
// in either case; there will be no half-finished pieces.
|
2012-04-25 21:16:22 +02:00
|
|
|
if (has_picker())
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
std::vector<piece_picker::downloading_piece> q
|
2008-04-13 20:54:36 +02:00
|
|
|
= m_picker->get_download_queue();
|
|
|
|
|
|
|
|
// unfinished pieces
|
|
|
|
ret["unfinished"] = entry::list_type();
|
|
|
|
entry::list_type& up = ret["unfinished"].list();
|
|
|
|
|
|
|
|
// info for each unfinished piece
|
2016-07-02 22:20:12 +02:00
|
|
|
for (piece_picker::downloading_piece const& dp : q)
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2016-07-02 22:20:12 +02:00
|
|
|
if (dp.finished == 0) continue;
|
2008-04-13 20:54:36 +02:00
|
|
|
|
|
|
|
entry piece_struct(entry::dictionary_t);
|
|
|
|
|
|
|
|
// the unfinished piece's index
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_struct["piece"] = static_cast<int>(dp.index);
|
2008-04-13 20:54:36 +02:00
|
|
|
|
|
|
|
std::string bitmask;
|
|
|
|
const int num_bitmask_bytes
|
|
|
|
= (std::max)(num_blocks_per_piece / 8, 1);
|
|
|
|
|
2016-07-02 22:20:12 +02:00
|
|
|
piece_picker::block_info const* info = m_picker->blocks_for_piece(dp);
|
2008-04-13 20:54:36 +02:00
|
|
|
for (int j = 0; j < num_bitmask_bytes; ++j)
|
|
|
|
{
|
|
|
|
unsigned char v = 0;
|
2016-12-15 20:20:08 +01:00
|
|
|
int bits = (std::min)(num_blocks_per_piece - j * 8, 8);
|
2008-04-13 20:54:36 +02:00
|
|
|
for (int k = 0; k < bits; ++k)
|
2016-12-15 20:20:08 +01:00
|
|
|
v |= (info[j * 8 + k].state == piece_picker::block_info::state_finished)
|
2008-04-13 20:54:36 +02:00
|
|
|
? (1 << k) : 0;
|
2009-10-28 20:55:20 +01:00
|
|
|
bitmask.append(1, v);
|
2008-04-13 20:54:36 +02:00
|
|
|
TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1);
|
|
|
|
}
|
|
|
|
piece_struct["bitmask"] = bitmask;
|
|
|
|
// push the struct onto the unfinished-piece list
|
|
|
|
up.push_back(piece_struct);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-22 19:39:18 +02:00
|
|
|
// save trackers
|
2014-07-15 05:23:43 +02:00
|
|
|
entry::list_type& tr_list = ret["trackers"].list();
|
|
|
|
tr_list.push_back(entry::list_type());
|
|
|
|
int tier = 0;
|
2016-07-02 22:20:12 +02:00
|
|
|
for (announce_entry const& tr : m_trackers)
|
2008-09-22 19:39:18 +02:00
|
|
|
{
|
2016-07-02 22:20:12 +02:00
|
|
|
if (tr.tier == tier)
|
2008-09-22 19:39:18 +02:00
|
|
|
{
|
2016-07-02 22:20:12 +02:00
|
|
|
tr_list.back().list().push_back(tr.url);
|
2014-07-15 05:23:43 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tr_list.push_back(entry::list_t);
|
2016-07-02 22:20:12 +02:00
|
|
|
tr_list.back().list().push_back(tr.url);
|
|
|
|
tier = tr.tier;
|
2008-09-22 19:39:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// save web seeds
|
|
|
|
if (!m_web_seeds.empty())
|
|
|
|
{
|
|
|
|
entry::list_type& url_list = ret["url-list"].list();
|
2008-12-30 04:54:07 +01:00
|
|
|
entry::list_type& httpseed_list = ret["httpseeds"].list();
|
2016-07-02 22:20:12 +02:00
|
|
|
for (web_seed_t const& ws : m_web_seeds)
|
2008-12-30 04:54:07 +01:00
|
|
|
{
|
2016-10-03 08:08:03 +02:00
|
|
|
if (ws.removed || ws.ephemeral) continue;
|
2016-07-02 22:20:12 +02:00
|
|
|
if (ws.type == web_seed_entry::url_seed)
|
|
|
|
url_list.push_back(ws.url);
|
|
|
|
else if (ws.type == web_seed_entry::http_seed)
|
|
|
|
httpseed_list.push_back(ws.url);
|
2008-09-22 19:39:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
// write have bitmask
|
2009-03-21 05:33:53 +01:00
|
|
|
// the pieces string has one byte per piece. Each
|
|
|
|
// byte is a bitmask representing different properties
|
|
|
|
// for the piece
|
|
|
|
// bit 0: set if we have the piece
|
|
|
|
// bit 1: set if we have verified the piece (in seed mode)
|
2016-02-20 06:48:17 +01:00
|
|
|
bool const is_checking = state() == torrent_status::checking_files;
|
2008-04-13 20:54:36 +02:00
|
|
|
|
2016-02-20 06:48:17 +01:00
|
|
|
// if we are checking, only save the have_pieces bitfield up to the piece
|
|
|
|
// we have actually checked. This allows us to resume the checking when we
|
|
|
|
// load this torrent up again. If we have not completed checking nor is
|
|
|
|
// currently checking, don't save any pieces from the have_pieces
|
|
|
|
// bitfield.
|
2016-12-22 16:42:33 +01:00
|
|
|
piece_index_t const max_piece
|
2016-02-20 06:48:17 +01:00
|
|
|
= is_checking ? m_num_checked_pieces
|
2016-12-22 16:42:33 +01:00
|
|
|
: m_files_checked ? m_torrent_file->end_piece()
|
|
|
|
: piece_index_t(0);
|
2016-02-20 06:48:17 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
if (max_piece > piece_index_t(0))
|
2009-03-21 05:33:53 +01:00
|
|
|
{
|
2016-02-20 06:48:17 +01:00
|
|
|
entry::string_type& pieces = ret["pieces"].string();
|
2016-12-22 16:42:33 +01:00
|
|
|
pieces.resize(static_cast<int>(max_piece));
|
2016-07-02 22:20:12 +02:00
|
|
|
if (is_seed())
|
2016-02-20 06:48:17 +01:00
|
|
|
{
|
|
|
|
std::memset(&pieces[0], m_have_all, pieces.size());
|
|
|
|
}
|
|
|
|
else if (has_picker())
|
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
for (piece_index_t i(0); i < max_piece; ++i)
|
|
|
|
pieces[static_cast<int>(i)] = m_picker->have_piece(i) ? 1 : 0;
|
2016-02-20 06:48:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_seed_mode)
|
|
|
|
{
|
2016-11-21 07:49:56 +01:00
|
|
|
TORRENT_ASSERT(m_verified.size() == int(pieces.size()));
|
|
|
|
TORRENT_ASSERT(m_verifying.size() == int(pieces.size()));
|
2016-12-22 16:42:33 +01:00
|
|
|
for (piece_index_t i(0); i < max_piece; ++i)
|
|
|
|
pieces[static_cast<int>(i)] |= m_verified[i] ? 2 : 0;
|
2016-02-20 06:48:17 +01:00
|
|
|
}
|
2009-03-21 05:33:53 +01:00
|
|
|
}
|
|
|
|
|
2009-03-22 23:21:48 +01:00
|
|
|
// write renamed files
|
2011-01-31 01:47:09 +01:00
|
|
|
if (&m_torrent_file->files() != &m_torrent_file->orig_files()
|
|
|
|
&& m_torrent_file->files().num_files() == m_torrent_file->orig_files().num_files())
|
2009-03-22 23:21:48 +01:00
|
|
|
{
|
|
|
|
entry::list_type& fl = ret["mapped_files"].list();
|
2013-08-12 09:30:57 +02:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < fs.end_file(); ++i)
|
2009-03-22 23:21:48 +01:00
|
|
|
{
|
2013-08-12 09:30:57 +02:00
|
|
|
fl.push_back(fs.file_path(i));
|
2009-03-22 23:21:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-13 20:54:36 +02:00
|
|
|
// write local peers
|
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
std::back_insert_iterator<entry::string_type> peers(ret["peers"].string());
|
|
|
|
std::back_insert_iterator<entry::string_type> banned_peers(ret["banned_peers"].string());
|
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
std::back_insert_iterator<entry::string_type> peers6(ret["peers6"].string());
|
|
|
|
std::back_insert_iterator<entry::string_type> banned_peers6(ret["banned_peers6"].string());
|
|
|
|
#endif
|
|
|
|
|
2012-07-04 23:33:04 +02:00
|
|
|
int num_saved_peers = 0;
|
|
|
|
|
2014-10-07 10:29:20 +02:00
|
|
|
std::vector<torrent_peer const*> deferred_peers;
|
|
|
|
|
2014-10-26 08:34:31 +01:00
|
|
|
if (m_peer_list)
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto p : *m_peer_list)
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
error_code ec;
|
|
|
|
address addr = p->address();
|
2016-11-15 18:18:11 +01:00
|
|
|
#if TORRENT_USE_I2P
|
2015-07-14 21:12:23 +02:00
|
|
|
if (p->is_i2p_addr)
|
|
|
|
continue;
|
2016-11-15 18:18:11 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
if (p->banned)
|
2010-03-20 23:00:13 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
if (addr.is_v6())
|
|
|
|
{
|
|
|
|
write_address(addr, banned_peers6);
|
|
|
|
write_uint16(p->port, banned_peers6);
|
|
|
|
}
|
|
|
|
else
|
2009-05-07 00:36:24 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
write_address(addr, banned_peers);
|
|
|
|
write_uint16(p->port, banned_peers);
|
|
|
|
}
|
|
|
|
continue;
|
2010-03-20 23:00:13 +01:00
|
|
|
}
|
2008-04-13 20:54:36 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// we cannot save remote connection
|
|
|
|
// since we don't know their listen port
|
|
|
|
// unless they gave us their listen port
|
|
|
|
// through the extension handshake
|
|
|
|
// so, if the peer is not connectable (i.e. we
|
|
|
|
// don't know its listen port) or if it has
|
|
|
|
// been banned, don't save it.
|
|
|
|
if (!p->connectable) continue;
|
|
|
|
|
|
|
|
// don't save peers that don't work
|
2014-10-07 10:29:20 +02:00
|
|
|
if (int(p->failcount) > 0) continue;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2014-10-07 10:29:20 +02:00
|
|
|
// don't save peers that appear to send corrupt data
|
|
|
|
if (int(p->trust_points) < 0) continue;
|
2012-07-04 23:33:04 +02:00
|
|
|
|
2014-10-07 10:29:20 +02:00
|
|
|
if (p->last_connected == 0)
|
|
|
|
{
|
|
|
|
// we haven't connected to this peer. It might still
|
|
|
|
// be useful to save it, but only save it if we
|
|
|
|
// don't have enough peers that we actually did connect to
|
|
|
|
deferred_peers.push_back(p);
|
2014-07-06 21:18:00 +02:00
|
|
|
continue;
|
2014-10-07 10:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
if (addr.is_v6())
|
|
|
|
{
|
|
|
|
write_address(addr, peers6);
|
|
|
|
write_uint16(p->port, peers6);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
write_address(addr, peers);
|
|
|
|
write_uint16(p->port, peers);
|
|
|
|
}
|
|
|
|
++num_saved_peers;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we didn't save 100 peers, fill in with second choice peers
|
|
|
|
if (num_saved_peers < 100)
|
|
|
|
{
|
2016-08-15 01:48:31 +02:00
|
|
|
aux::random_shuffle(deferred_peers.begin(), deferred_peers.end());
|
2014-10-07 10:29:20 +02:00
|
|
|
for (std::vector<torrent_peer const*>::const_iterator i = deferred_peers.begin()
|
|
|
|
, end(deferred_peers.end()); i != end && num_saved_peers < 100; ++i)
|
|
|
|
{
|
|
|
|
torrent_peer const* p = *i;
|
|
|
|
address addr = p->address();
|
2012-07-04 23:33:04 +02:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
2014-07-06 21:18:00 +02:00
|
|
|
if (addr.is_v6())
|
|
|
|
{
|
|
|
|
write_address(addr, peers6);
|
|
|
|
write_uint16(p->port, peers6);
|
|
|
|
}
|
|
|
|
else
|
2009-05-07 00:36:24 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
write_address(addr, peers);
|
|
|
|
write_uint16(p->port, peers);
|
|
|
|
}
|
|
|
|
++num_saved_peers;
|
2010-03-20 23:00:13 +01:00
|
|
|
}
|
2008-04-13 20:54:36 +02:00
|
|
|
}
|
2008-07-19 13:12:40 +02:00
|
|
|
|
|
|
|
ret["upload_rate_limit"] = upload_limit();
|
|
|
|
ret["download_rate_limit"] = download_limit();
|
|
|
|
ret["max_connections"] = max_connections();
|
|
|
|
ret["max_uploads"] = max_uploads();
|
2011-06-05 22:48:00 +02:00
|
|
|
ret["paused"] = is_torrent_paused();
|
2008-07-19 13:12:40 +02:00
|
|
|
ret["auto_managed"] = m_auto_managed;
|
|
|
|
|
2015-07-14 04:19:55 +02:00
|
|
|
// piece priorities and file priorities are mutually exclusive. If there
|
|
|
|
// are file priorities set, don't save piece priorities.
|
|
|
|
if (!m_file_priority.empty())
|
2008-07-19 13:12:40 +02:00
|
|
|
{
|
2015-07-21 19:12:37 +02:00
|
|
|
|
|
|
|
// when in seed mode (i.e. the client promises that we have all files)
|
|
|
|
// it does not make sense to save file priorities.
|
|
|
|
if (!m_seed_mode)
|
|
|
|
{
|
|
|
|
// write file priorities
|
|
|
|
entry::list_type& file_priority = ret["file_priority"].list();
|
|
|
|
file_priority.clear();
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const prio : m_file_priority)
|
|
|
|
file_priority.push_back(prio);
|
2015-07-21 19:12:37 +02:00
|
|
|
}
|
2015-07-14 04:19:55 +02:00
|
|
|
}
|
|
|
|
else if (has_picker())
|
|
|
|
{
|
|
|
|
// write piece priorities
|
|
|
|
// but only if they are not set to the default
|
2014-07-06 21:18:00 +02:00
|
|
|
bool default_prio = true;
|
2016-12-22 16:42:33 +01:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
|
|
|
for (piece_index_t i(0); i < fs.end_piece(); ++i)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-12-24 02:00:24 +01:00
|
|
|
if (m_picker->piece_priority(i) == default_piece_priority) continue;
|
2014-07-06 21:18:00 +02:00
|
|
|
default_prio = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!default_prio)
|
|
|
|
{
|
|
|
|
entry::string_type& piece_priority = ret["piece_priority"].string();
|
|
|
|
piece_priority.resize(m_torrent_file->num_pieces());
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
for (piece_index_t i(0); i < fs.end_piece(); ++i)
|
|
|
|
piece_priority[static_cast<int>(i)] = entry::string_type::value_type(m_picker->piece_priority(i));
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2008-07-19 13:12:40 +02:00
|
|
|
}
|
2008-04-13 20:54:36 +02:00
|
|
|
}
|
|
|
|
|
2016-02-21 05:40:45 +01:00
|
|
|
void torrent::get_full_peer_list(std::vector<peer_list_entry>* v) const
|
2008-04-13 08:32:48 +02:00
|
|
|
{
|
2016-02-21 05:40:45 +01:00
|
|
|
v->clear();
|
2014-10-26 08:34:31 +01:00
|
|
|
if (!m_peer_list) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-02-21 05:40:45 +01:00
|
|
|
v->reserve(m_peer_list->num_peers());
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto p : *m_peer_list)
|
2008-04-13 08:32:48 +02:00
|
|
|
{
|
|
|
|
peer_list_entry e;
|
2016-06-01 07:05:17 +02:00
|
|
|
e.ip = p->ip();
|
|
|
|
e.flags = p->banned ? peer_list_entry::banned : 0;
|
|
|
|
e.failcount = p->failcount;
|
|
|
|
e.source = p->source;
|
2016-02-21 05:40:45 +01:00
|
|
|
v->push_back(e);
|
2008-04-13 08:32:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-21 05:40:45 +01:00
|
|
|
void torrent::get_peer_info(std::vector<peer_info>* v)
|
2007-11-25 19:48:43 +01:00
|
|
|
{
|
2016-02-21 05:40:45 +01:00
|
|
|
v->clear();
|
2007-11-25 19:48:43 +01:00
|
|
|
for (peer_iterator i = begin();
|
|
|
|
i != end(); ++i)
|
|
|
|
{
|
|
|
|
peer_connection* peer = *i;
|
2012-04-12 07:00:20 +02:00
|
|
|
TORRENT_ASSERT(peer->m_in_use == 1337);
|
2007-11-25 19:48:43 +01:00
|
|
|
|
|
|
|
// incoming peers that haven't finished the handshake should
|
|
|
|
// not be included in this list
|
|
|
|
if (peer->associated_torrent().expired()) continue;
|
|
|
|
|
2016-02-21 05:40:45 +01:00
|
|
|
v->push_back(peer_info());
|
|
|
|
peer_info& p = v->back();
|
2015-06-20 22:42:18 +02:00
|
|
|
|
2007-11-25 19:48:43 +01:00
|
|
|
peer->get_peer_info(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-14 22:32:41 +01:00
|
|
|
void torrent::get_download_queue(std::vector<partial_piece_info>* queue) const
|
2007-11-25 19:48:43 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2011-11-20 21:49:18 +01:00
|
|
|
queue->clear();
|
2014-07-06 21:18:00 +02:00
|
|
|
std::vector<block_info>& blk = m_ses.block_info_storage();
|
2009-05-03 11:45:07 +02:00
|
|
|
blk.clear();
|
|
|
|
|
2012-04-25 21:16:22 +02:00
|
|
|
if (!valid_metadata() || !has_picker()) return;
|
2007-11-25 19:48:43 +01:00
|
|
|
piece_picker const& p = picker();
|
2014-07-06 21:18:00 +02:00
|
|
|
std::vector<piece_picker::downloading_piece> q
|
2007-11-25 19:48:43 +01:00
|
|
|
= p.get_download_queue();
|
2014-07-06 21:18:00 +02:00
|
|
|
if (q.empty()) return;
|
2007-11-25 19:48:43 +01:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
const int blocks_per_piece = m_picker->blocks_in_piece(piece_index_t(0));
|
2009-05-03 11:45:07 +02:00
|
|
|
blk.resize(q.size() * blocks_per_piece);
|
2011-11-20 21:49:18 +01:00
|
|
|
// for some weird reason valgrind claims these are uninitialized
|
|
|
|
// unless it's zeroed out here (block_info has a construct that's
|
|
|
|
// supposed to initialize it)
|
2011-11-25 00:01:35 +01:00
|
|
|
if (!blk.empty())
|
2016-12-15 20:20:08 +01:00
|
|
|
std::memset(blk.data(), 0, sizeof(blk[0]) * blk.size());
|
2009-05-03 11:45:07 +02:00
|
|
|
|
|
|
|
int counter = 0;
|
2007-11-25 19:48:43 +01:00
|
|
|
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
2009-05-03 11:45:07 +02:00
|
|
|
= q.begin(); i != q.end(); ++i, ++counter)
|
2007-11-25 19:48:43 +01:00
|
|
|
{
|
|
|
|
partial_piece_info pi;
|
|
|
|
pi.blocks_in_piece = p.blocks_in_piece(i->index);
|
2015-08-16 18:17:23 +02:00
|
|
|
pi.finished = int(i->finished);
|
|
|
|
pi.writing = int(i->writing);
|
|
|
|
pi.requested = int(i->requested);
|
2011-11-20 21:49:18 +01:00
|
|
|
TORRENT_ASSERT(counter * blocks_per_piece + pi.blocks_in_piece <= int(blk.size()));
|
2009-05-03 11:45:07 +02:00
|
|
|
pi.blocks = &blk[counter * blocks_per_piece];
|
2008-04-05 23:18:27 +02:00
|
|
|
int piece_size = int(torrent_file().piece_size(i->index));
|
2015-02-14 22:32:41 +01:00
|
|
|
piece_picker::block_info const* info = m_picker->blocks_for_piece(*i);
|
2009-05-03 11:45:07 +02:00
|
|
|
for (int j = 0; j < pi.blocks_in_piece; ++j)
|
2007-11-25 19:48:43 +01:00
|
|
|
{
|
|
|
|
block_info& bi = pi.blocks[j];
|
2015-02-14 22:32:41 +01:00
|
|
|
bi.state = info[j].state;
|
2010-02-14 02:39:55 +01:00
|
|
|
bi.block_size = j < pi.blocks_in_piece - 1 ? block_size()
|
|
|
|
: piece_size - (j * block_size());
|
2007-11-25 19:48:43 +01:00
|
|
|
bool complete = bi.state == block_info::writing
|
|
|
|
|| bi.state == block_info::finished;
|
2016-07-09 22:26:26 +02:00
|
|
|
if (info[j].peer == nullptr)
|
2007-11-25 19:48:43 +01:00
|
|
|
{
|
2009-05-03 11:45:07 +02:00
|
|
|
bi.set_peer(tcp::endpoint());
|
2007-11-25 19:48:43 +01:00
|
|
|
bi.bytes_progress = complete ? bi.block_size : 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-09-07 23:51:18 +02:00
|
|
|
torrent_peer* tp = info[j].peer;
|
2015-08-17 15:01:43 +02:00
|
|
|
TORRENT_ASSERT(tp->in_use);
|
|
|
|
if (tp->connection)
|
2007-11-25 19:48:43 +01:00
|
|
|
{
|
2015-08-17 15:01:43 +02:00
|
|
|
peer_connection* peer = static_cast<peer_connection*>(tp->connection);
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(peer->m_in_use);
|
|
|
|
bi.set_peer(peer->remote());
|
2007-11-25 19:48:43 +01:00
|
|
|
if (bi.state == block_info::requested)
|
|
|
|
{
|
2016-09-04 18:31:02 +02:00
|
|
|
auto pbp = peer->downloading_piece_progress();
|
2016-12-22 16:42:33 +01:00
|
|
|
if (pbp.piece_index == i->index && pbp.block_index == j)
|
2007-11-25 19:48:43 +01:00
|
|
|
{
|
2016-09-04 18:31:02 +02:00
|
|
|
bi.bytes_progress = pbp.bytes_downloaded;
|
2007-11-25 19:48:43 +01:00
|
|
|
TORRENT_ASSERT(bi.bytes_progress <= bi.block_size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bi.bytes_progress = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bi.bytes_progress = complete ? bi.block_size : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-08-17 15:01:43 +02:00
|
|
|
bi.set_peer(tp->ip());
|
2007-11-25 19:48:43 +01:00
|
|
|
bi.bytes_progress = complete ? bi.block_size : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-14 22:32:41 +01:00
|
|
|
pi.blocks[j].num_peers = info[j].num_peers;
|
2007-11-25 19:48:43 +01:00
|
|
|
}
|
|
|
|
pi.piece_index = i->index;
|
2011-11-20 21:49:18 +01:00
|
|
|
queue->push_back(pi);
|
2007-11-25 19:48:43 +01:00
|
|
|
}
|
2015-05-19 05:13:49 +02:00
|
|
|
|
2007-11-25 19:48:43 +01:00
|
|
|
}
|
2015-05-19 05:13:49 +02:00
|
|
|
|
2016-09-23 22:49:39 +02:00
|
|
|
bool torrent::connect_to_peer(torrent_peer* peerinfo, bool const ignore_limit)
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
2015-05-19 05:13:49 +02:00
|
|
|
TORRENT_UNUSED(ignore_limit);
|
2007-05-02 19:38:37 +02:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(peerinfo);
|
2016-07-09 22:26:26 +02:00
|
|
|
TORRENT_ASSERT(peerinfo->connection == nullptr);
|
2008-03-29 20:39:24 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_abort) return false;
|
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
peerinfo->last_connected = m_ses.session_time();
|
2015-05-19 05:13:49 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!settings().get_bool(settings_pack::allow_multiple_connections_per_ip))
|
2011-03-27 08:07:23 +02:00
|
|
|
{
|
2014-10-26 08:34:31 +01:00
|
|
|
// this asserts that we don't have duplicates in the peer_list's peer list
|
2011-03-27 08:07:23 +02:00
|
|
|
peer_iterator i_ = std::find_if(m_connections.begin(), m_connections.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, [peerinfo] (peer_connection const* p) { return p->remote() == peerinfo->ip(); });
|
2011-04-09 23:04:02 +02:00
|
|
|
#if TORRENT_USE_I2P
|
2011-03-27 08:07:23 +02:00
|
|
|
TORRENT_ASSERT(i_ == m_connections.end()
|
2016-09-24 19:47:17 +02:00
|
|
|
|| (*i_)->type() != connection_type::bittorrent
|
2011-04-09 23:04:02 +02:00
|
|
|
|| peerinfo->is_i2p_addr);
|
|
|
|
#else
|
|
|
|
TORRENT_ASSERT(i_ == m_connections.end()
|
2016-09-24 19:47:17 +02:00
|
|
|
|| (*i_)->type() != connection_type::bittorrent);
|
2009-09-06 02:57:01 +02:00
|
|
|
#endif
|
2011-03-27 08:07:23 +02:00
|
|
|
}
|
2015-05-19 05:13:49 +02:00
|
|
|
#endif // TORRENT_USE_ASSERTS
|
2007-05-04 18:35:29 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(want_peers() || ignore_limit);
|
|
|
|
TORRENT_ASSERT(m_ses.num_connections()
|
2015-05-16 08:33:37 +02:00
|
|
|
< settings().get_int(settings_pack::connections_limit) || ignore_limit);
|
2007-05-02 19:38:37 +02:00
|
|
|
|
2008-07-14 13:15:35 +02:00
|
|
|
tcp::endpoint a(peerinfo->ip());
|
2011-02-27 18:26:57 +01:00
|
|
|
TORRENT_ASSERT(!m_apply_ip_filter
|
2015-05-16 08:33:37 +02:00
|
|
|
|| !m_ip_filter
|
|
|
|
|| (m_ip_filter->access(peerinfo->address()) & ip_filter::blocked) == 0);
|
2006-11-24 15:22:52 +01:00
|
|
|
|
2016-08-31 18:45:45 +02:00
|
|
|
std::shared_ptr<socket_type> s = std::make_shared<socket_type>(m_ses.get_io_service());
|
2007-10-22 06:17:26 +02:00
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_I2P
|
2016-12-22 16:42:33 +01:00
|
|
|
bool const i2p = peerinfo->is_i2p_addr;
|
2009-08-20 05:19:12 +02:00
|
|
|
if (i2p)
|
|
|
|
{
|
2014-09-02 08:28:27 +02:00
|
|
|
if (m_ses.i2p_proxy().hostname.empty())
|
|
|
|
{
|
|
|
|
// we have an i2p torrent, but we're not connected to an i2p
|
|
|
|
// SAM proxy.
|
|
|
|
if (alerts().should_post<i2p_alert>())
|
2016-10-02 21:27:50 +02:00
|
|
|
alerts().emplace_alert<i2p_alert>(errors::no_i2p_router);
|
2014-09-02 08:28:27 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-25 04:18:10 +02:00
|
|
|
// It's not entirely obvious why this peer connection is not marked as
|
|
|
|
// one. The main feature of a peer connection is that whether or not we
|
|
|
|
// proxy it is configurable. When we use i2p, we want to always prox
|
|
|
|
// everything via i2p.
|
2016-12-22 16:42:33 +01:00
|
|
|
bool const ret = instantiate_connection(m_ses.get_io_service()
|
2016-06-20 17:32:06 +02:00
|
|
|
, m_ses.i2p_proxy(), *s, nullptr, nullptr, false, false);
|
2009-08-20 05:19:12 +02:00
|
|
|
(void)ret;
|
|
|
|
TORRENT_ASSERT(ret);
|
2014-07-06 21:18:00 +02:00
|
|
|
s->get<i2p_stream>()->set_destination(static_cast<i2p_peer*>(peerinfo)->destination);
|
2009-08-20 05:19:12 +02:00
|
|
|
s->get<i2p_stream>()->set_command(i2p_stream::cmd_connect);
|
2014-07-06 21:18:00 +02:00
|
|
|
s->get<i2p_stream>()->set_session_id(m_ses.i2p_session());
|
2009-08-20 05:19:12 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
2010-11-29 02:33:05 +01:00
|
|
|
// this is where we determine if we open a regular TCP connection
|
2014-07-06 21:18:00 +02:00
|
|
|
// or a uTP connection. If the utp_socket_manager pointer is not passed in
|
2010-11-29 02:33:05 +01:00
|
|
|
// we'll instantiate a TCP connection
|
2016-07-09 22:26:26 +02:00
|
|
|
utp_socket_manager* sm = nullptr;
|
2010-11-29 02:33:05 +01:00
|
|
|
|
2015-05-16 08:33:37 +02:00
|
|
|
if (settings().get_bool(settings_pack::enable_outgoing_utp)
|
|
|
|
&& (!settings().get_bool(settings_pack::enable_outgoing_tcp)
|
2010-11-29 02:33:05 +01:00
|
|
|
|| peerinfo->supports_utp
|
|
|
|
|| peerinfo->confirmed_supports_utp))
|
2014-07-06 21:18:00 +02:00
|
|
|
sm = m_ses.utp_socket_manager();
|
2010-11-29 02:33:05 +01:00
|
|
|
|
|
|
|
// don't make a TCP connection if it's disabled
|
2016-07-09 22:26:26 +02:00
|
|
|
if (sm == nullptr && !settings().get_bool(settings_pack::enable_outgoing_tcp))
|
2015-07-12 20:08:04 +02:00
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("discarding peer \"%s\": TCP connections disabled "
|
|
|
|
"[ supports-utp: %d ]", peerinfo->to_string().c_str()
|
|
|
|
, peerinfo->supports_utp);
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
2010-11-29 02:33:05 +01:00
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
void* userdata = nullptr;
|
2011-09-12 05:51:49 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2016-02-24 08:07:27 +01:00
|
|
|
if (is_ssl_torrent())
|
2011-09-12 05:51:49 +02:00
|
|
|
{
|
|
|
|
userdata = m_ssl_ctx.get();
|
2016-02-08 08:01:25 +01:00
|
|
|
// if we're creating a uTP socket, since this is SSL now, make sure
|
|
|
|
// to pass in the corresponding utp socket manager
|
|
|
|
if (sm) sm = m_ses.ssl_utp_socket_manager();
|
2011-09-12 05:51:49 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-10-03 22:56:57 +02:00
|
|
|
bool ret = instantiate_connection(m_ses.get_io_service()
|
2015-08-25 04:18:10 +02:00
|
|
|
, m_ses.proxy(), *s, userdata, sm, true, false);
|
2009-08-20 05:19:12 +02:00
|
|
|
(void)ret;
|
|
|
|
TORRENT_ASSERT(ret);
|
2012-01-14 17:04:25 +01:00
|
|
|
|
2016-08-26 06:14:11 +02:00
|
|
|
#if defined TORRENT_USE_OPENSSL
|
2012-01-14 17:04:25 +01:00
|
|
|
if (is_ssl_torrent())
|
|
|
|
{
|
|
|
|
// for ssl sockets, set the hostname
|
2016-07-29 08:36:15 +02:00
|
|
|
std::string host_name = aux::to_hex(m_torrent_file->info_hash());
|
2012-01-14 17:04:25 +01:00
|
|
|
|
2016-08-26 06:14:11 +02:00
|
|
|
#define CASE(t) case socket_type_int_impl<ssl_stream<t>>::value: \
|
|
|
|
s->get<ssl_stream<t>>()->set_host_name(host_name); break;
|
2012-01-14 17:04:25 +01:00
|
|
|
|
|
|
|
switch (s->type())
|
|
|
|
{
|
2015-06-06 19:49:18 +02:00
|
|
|
CASE(tcp::socket)
|
2012-01-14 17:04:25 +01:00
|
|
|
CASE(socks5_stream)
|
|
|
|
CASE(http_stream)
|
|
|
|
CASE(utp_stream)
|
|
|
|
default: break;
|
|
|
|
};
|
|
|
|
}
|
2012-02-25 09:31:25 +01:00
|
|
|
#undef CASE
|
2012-01-14 17:04:25 +01:00
|
|
|
#endif
|
2009-08-20 05:19:12 +02:00
|
|
|
}
|
2007-10-22 06:17:26 +02:00
|
|
|
|
2009-05-01 06:59:15 +02:00
|
|
|
m_ses.setup_socket_buffers(*s);
|
|
|
|
|
2014-07-14 06:32:41 +02:00
|
|
|
peer_connection_args pack;
|
|
|
|
pack.ses = &m_ses;
|
2015-05-16 08:33:37 +02:00
|
|
|
pack.sett = &settings();
|
2014-07-14 06:32:41 +02:00
|
|
|
pack.stats_counters = &m_ses.stats_counters();
|
|
|
|
pack.disk_thread = &m_ses.disk_thread();
|
|
|
|
pack.ios = &m_ses.get_io_service();
|
|
|
|
pack.tor = shared_from_this();
|
|
|
|
pack.s = s;
|
2014-09-28 06:05:44 +02:00
|
|
|
pack.endp = a;
|
2014-07-14 06:32:41 +02:00
|
|
|
pack.peerinfo = peerinfo;
|
|
|
|
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<peer_connection> c = std::make_shared<bt_peer_connection>(
|
|
|
|
pack, m_ses.get_peer_id());
|
2007-06-20 20:41:53 +02:00
|
|
|
|
2014-10-03 22:56:57 +02:00
|
|
|
TORRENT_TRY
|
|
|
|
{
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2014-10-03 22:56:57 +02:00
|
|
|
c->m_in_constructor = false;
|
2006-04-25 23:04:48 +02:00
|
|
|
#endif
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
c->add_stat(std::int64_t(peerinfo->prev_amount_download) << 10
|
|
|
|
, std::int64_t(peerinfo->prev_amount_upload) << 10);
|
2015-05-16 08:33:37 +02:00
|
|
|
peerinfo->prev_amount_download = 0;
|
|
|
|
peerinfo->prev_amount_upload = 0;
|
2008-04-07 10:15:31 +02:00
|
|
|
|
2007-10-31 10:48:20 +01:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-09-01 21:04:58 +02:00
|
|
|
for (auto const& ext : m_extensions)
|
2014-10-03 22:56:57 +02:00
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
std::shared_ptr<peer_plugin> pp(ext->new_connection(
|
|
|
|
peer_connection_handle(c->self())));
|
|
|
|
if (pp) c->add_extension(pp);
|
2014-10-03 22:56:57 +02:00
|
|
|
}
|
2007-10-31 10:48:20 +01:00
|
|
|
#endif
|
2007-09-19 08:05:14 +02:00
|
|
|
|
2014-10-03 22:56:57 +02:00
|
|
|
// add the newly connected peer to this torrent's peer list
|
2016-09-01 21:04:58 +02:00
|
|
|
sorted_insert(m_connections, c.get());
|
2014-10-03 22:56:57 +02:00
|
|
|
m_ses.insert_peer(c);
|
2015-06-30 01:56:39 +02:00
|
|
|
need_peer_list();
|
2014-10-26 08:34:31 +01:00
|
|
|
m_peer_list->set_connection(peerinfo, c.get());
|
2014-10-03 22:56:57 +02:00
|
|
|
if (peerinfo->seed)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_num_seeds < 0xffff);
|
|
|
|
++m_num_seeds;
|
|
|
|
}
|
|
|
|
update_want_peers();
|
|
|
|
update_want_tick();
|
|
|
|
c->start();
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2014-10-03 22:56:57 +02:00
|
|
|
if (c->is_disconnecting()) return false;
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2016-09-25 15:50:48 +02:00
|
|
|
TORRENT_CATCH (std::exception const&)
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2016-09-01 21:04:58 +02:00
|
|
|
peer_iterator i = sorted_find(m_connections, c.get());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (i != m_connections.end())
|
|
|
|
{
|
|
|
|
m_connections.erase(i);
|
|
|
|
update_want_peers();
|
|
|
|
update_want_tick();
|
|
|
|
}
|
2015-02-15 06:17:09 +01:00
|
|
|
c->disconnect(errors::no_error, op_bittorrent, 1);
|
2007-10-31 10:48:20 +01:00
|
|
|
return false;
|
2006-01-06 21:20:20 +01:00
|
|
|
}
|
2008-09-25 09:40:55 +02:00
|
|
|
|
2010-09-05 18:01:36 +02:00
|
|
|
if (m_share_mode)
|
|
|
|
recalc_share_mode();
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
return peerinfo->connection != nullptr;
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
2016-08-22 17:02:51 +02:00
|
|
|
bool torrent::set_metadata(span<char const> metadata_buf)
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-06-10 22:46:09 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2008-09-24 04:32:33 +02:00
|
|
|
if (m_torrent_file->is_valid()) return false;
|
|
|
|
|
2016-08-22 17:02:51 +02:00
|
|
|
sha1_hash const info_hash = hasher(metadata_buf).final();
|
2008-09-24 04:32:33 +02:00
|
|
|
if (info_hash != m_torrent_file->info_hash())
|
|
|
|
{
|
|
|
|
if (alerts().should_post<metadata_failed_alert>())
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<metadata_failed_alert>(get_handle()
|
2016-10-02 21:27:50 +02:00
|
|
|
, errors::mismatching_info_hash);
|
2008-09-24 04:32:33 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node metadata;
|
2009-02-13 20:01:02 +01:00
|
|
|
error_code ec;
|
2016-08-22 17:02:51 +02:00
|
|
|
int ret = bdecode(metadata_buf.begin(), metadata_buf.end(), metadata, ec);
|
2010-11-15 06:10:36 +01:00
|
|
|
if (ret != 0 || !m_torrent_file->parse_info_section(metadata, ec, 0))
|
2007-12-28 21:11:10 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
2008-09-24 04:32:33 +02:00
|
|
|
// this means the metadata is correct, since we
|
|
|
|
// verified it against the info-hash, but we
|
|
|
|
// failed to parse it. Pause the torrent
|
|
|
|
if (alerts().should_post<metadata_failed_alert>())
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<metadata_failed_alert>(get_handle(), ec);
|
2008-09-24 04:32:33 +02:00
|
|
|
}
|
2015-11-03 06:12:30 +01:00
|
|
|
set_error(errors::invalid_swarm_metadata, torrent_status::error_file_none);
|
2008-09-24 04:32:33 +02:00
|
|
|
pause();
|
2007-12-28 21:11:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
|
|
|
|
if (m_ses.alerts().should_post<metadata_received_alert>())
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<metadata_received_alert>(
|
|
|
|
get_handle());
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2012-02-16 11:06:21 +01:00
|
|
|
// we have to initialize the torrent before we start
|
|
|
|
// disconnecting redundant peers, otherwise we'll think
|
|
|
|
// we're a seed, because we have all 0 pieces
|
|
|
|
init();
|
|
|
|
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::num_total_pieces_added
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_torrent_file->num_pieces());
|
|
|
|
|
2012-02-10 17:37:50 +01:00
|
|
|
// disconnect redundant peers
|
2014-07-06 21:18:00 +02:00
|
|
|
int idx = 0;
|
|
|
|
for (peer_iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end(); ++idx)
|
2012-02-10 17:37:50 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if ((*i)->disconnect_if_redundant())
|
|
|
|
{
|
|
|
|
i = m_connections.begin() + idx;
|
|
|
|
--idx;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
}
|
2012-02-10 17:37:50 +01:00
|
|
|
}
|
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2013-02-20 09:10:31 +01:00
|
|
|
|
2007-12-29 08:16:36 +01:00
|
|
|
return true;
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
namespace {
|
|
|
|
|
2013-09-14 23:47:31 +02:00
|
|
|
bool connecting_time_compare(peer_connection const* lhs, peer_connection const* rhs)
|
|
|
|
{
|
|
|
|
bool lhs_connecting = lhs->is_connecting() && !lhs->is_disconnecting();
|
|
|
|
bool rhs_connecting = rhs->is_connecting() && !rhs->is_disconnecting();
|
|
|
|
if (lhs_connecting > rhs_connecting) return false;
|
|
|
|
if (lhs_connecting < rhs_connecting) return true;
|
|
|
|
|
|
|
|
// a lower value of connected_time means it's been waiting
|
|
|
|
// longer. This is a less-than comparison, so if lhs has
|
|
|
|
// waited longer than rhs, we should return false.
|
2013-12-02 10:39:17 +01:00
|
|
|
return lhs->connected_time() > rhs->connected_time();
|
2013-09-14 23:47:31 +02:00
|
|
|
}
|
|
|
|
|
2016-09-01 21:04:58 +02:00
|
|
|
} // anonymous namespace
|
2015-04-20 06:52:49 +02:00
|
|
|
|
2008-04-07 04:51:21 +02:00
|
|
|
bool torrent::attach_peer(peer_connection* p)
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2007-08-16 14:41:46 +02:00
|
|
|
// INVARIANT_CHECK;
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2012-01-14 17:04:25 +01:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2016-09-07 23:51:18 +02:00
|
|
|
#ifdef TORRENT_MACOS_DEPRECATED_LIBCRYPTO
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
#endif
|
2012-01-14 17:04:25 +01:00
|
|
|
if (is_ssl_torrent())
|
|
|
|
{
|
|
|
|
// if this is an SSL torrent, don't allow non SSL peers on it
|
2016-08-31 18:45:45 +02:00
|
|
|
std::shared_ptr<socket_type> s = p->get_socket();
|
2012-01-14 17:04:25 +01:00
|
|
|
|
|
|
|
//
|
2016-08-26 06:14:11 +02:00
|
|
|
#define SSL(t) socket_type_int_impl<ssl_stream<t>>::value: \
|
|
|
|
ssl_conn = s->get<ssl_stream<t>>()->native_handle(); \
|
2012-01-14 17:04:25 +01:00
|
|
|
break;
|
|
|
|
|
2016-07-10 20:27:42 +02:00
|
|
|
SSL* ssl_conn = nullptr;
|
2012-01-14 17:04:25 +01:00
|
|
|
|
|
|
|
switch (s->type())
|
|
|
|
{
|
2015-06-06 19:49:18 +02:00
|
|
|
case SSL(tcp::socket)
|
2012-01-14 17:04:25 +01:00
|
|
|
case SSL(socks5_stream)
|
|
|
|
case SSL(http_stream)
|
|
|
|
case SSL(utp_stream)
|
|
|
|
};
|
|
|
|
|
|
|
|
#undef SSL
|
|
|
|
|
2016-07-10 20:27:42 +02:00
|
|
|
if (ssl_conn == nullptr)
|
2012-01-14 17:04:25 +01:00
|
|
|
{
|
|
|
|
// don't allow non SSL peers on SSL torrents
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::requires_ssl_connection, op_bittorrent);
|
2012-01-14 17:04:25 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-07-28 17:06:28 +02:00
|
|
|
if (!m_ssl_ctx)
|
|
|
|
{
|
|
|
|
// we don't have a valid cert, don't accept any connection!
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::invalid_ssl_cert, op_ssl_handshake);
|
2013-07-28 17:06:28 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-01-14 17:04:25 +01:00
|
|
|
if (SSL_get_SSL_CTX(ssl_conn) != m_ssl_ctx->native_handle())
|
|
|
|
{
|
|
|
|
// if the SSL_CTX associated with this connection is
|
|
|
|
// not the one belonging to this torrent, the SSL handshake
|
|
|
|
// connected to one torrent, and the BitTorrent protocol
|
|
|
|
// to a different one. This is probably an attempt to circumvent
|
|
|
|
// access control. Don't allow it.
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::invalid_ssl_cert, op_bittorrent);
|
2012-01-14 17:04:25 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-09-07 23:51:18 +02:00
|
|
|
#ifdef TORRENT_MACOS_DEPRECATED_LIBCRYPTO
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
#endif
|
2013-07-28 17:06:28 +02:00
|
|
|
#else // TORRENT_USE_OPENSSL
|
|
|
|
if (is_ssl_torrent())
|
|
|
|
{
|
|
|
|
// Don't accidentally allow seeding of SSL torrents, just
|
|
|
|
// because libtorrent wasn't built with SSL support
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::requires_ssl_connection, op_ssl_handshake);
|
2013-07-28 17:06:28 +02:00
|
|
|
return false;
|
|
|
|
}
|
2012-01-14 17:04:25 +01:00
|
|
|
#endif // TORRENT_USE_OPENSSL
|
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
TORRENT_ASSERT(p != nullptr);
|
2012-02-07 04:46:21 +01:00
|
|
|
TORRENT_ASSERT(!p->is_outgoing());
|
2004-01-13 04:08:59 +01:00
|
|
|
|
2008-06-05 20:19:03 +02:00
|
|
|
m_has_incoming = true;
|
|
|
|
|
2011-03-06 21:38:24 +01:00
|
|
|
if (m_apply_ip_filter
|
2015-05-16 08:33:37 +02:00
|
|
|
&& m_ip_filter
|
|
|
|
&& m_ip_filter->access(p->remote().address()) & ip_filter::blocked)
|
2011-03-06 21:38:24 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<peer_blocked_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-05-23 01:56:14 +02:00
|
|
|
, p->remote(), peer_blocked_alert::ip_filter);
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::banned_by_ip_filter, op_bittorrent);
|
2011-03-06 21:38:24 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if ((m_state == torrent_status::checking_files
|
2008-11-19 01:46:48 +01:00
|
|
|
|| m_state == torrent_status::checking_resume_data)
|
2008-03-14 11:17:27 +01:00
|
|
|
&& valid_metadata())
|
2008-03-08 07:06:31 +01:00
|
|
|
{
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::torrent_not_ready, op_bittorrent);
|
2008-04-07 04:51:21 +02:00
|
|
|
return false;
|
2008-03-08 07:06:31 +01:00
|
|
|
}
|
2015-06-20 19:44:07 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_ses.has_connection(p))
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::peer_not_constructed, op_bittorrent);
|
2008-04-07 04:51:21 +02:00
|
|
|
return false;
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
|
|
|
|
2006-10-11 16:02:21 +02:00
|
|
|
if (m_ses.is_aborted())
|
2006-05-28 21:03:54 +02:00
|
|
|
{
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::session_closing, op_bittorrent);
|
2008-04-07 04:51:21 +02:00
|
|
|
return false;
|
2006-05-28 21:03:54 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int connection_limit_factor = 0;
|
|
|
|
for (int i = 0; i < p->num_classes(); ++i)
|
|
|
|
{
|
2016-11-25 17:17:25 +01:00
|
|
|
peer_class_t pc = p->class_at(i);
|
2016-06-20 17:32:06 +02:00
|
|
|
if (m_ses.peer_classes().at(pc) == nullptr) continue;
|
2014-07-06 21:18:00 +02:00
|
|
|
int f = m_ses.peer_classes().at(pc)->connection_limit_factor;
|
|
|
|
if (connection_limit_factor < f) connection_limit_factor = f;
|
|
|
|
}
|
|
|
|
if (connection_limit_factor == 0) connection_limit_factor = 100;
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint64_t limit = std::uint64_t(m_max_connections) * 100 / connection_limit_factor;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2013-01-02 08:48:09 +01:00
|
|
|
bool maybe_replace_peer = false;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_connections.size() >= limit)
|
2007-12-27 11:19:36 +01:00
|
|
|
{
|
2012-01-21 10:05:34 +01:00
|
|
|
// if more than 10% of the connections are outgoing
|
|
|
|
// connection attempts that haven't completed yet,
|
|
|
|
// disconnect one of them and let this incoming
|
|
|
|
// connection through.
|
2012-12-31 07:54:54 +01:00
|
|
|
if (m_num_connecting > m_max_connections / 10)
|
2012-01-21 10:05:34 +01:00
|
|
|
{
|
2012-12-31 07:54:54 +01:00
|
|
|
// find one of the connecting peers and disconnect it
|
|
|
|
// find any peer that's connecting (i.e. a half-open TCP connection)
|
|
|
|
// that's also not disconnecting
|
2016-09-23 22:49:39 +02:00
|
|
|
// disconnect the peer that's been waiting to establish a connection
|
2013-09-14 23:47:31 +02:00
|
|
|
// the longest
|
2016-09-23 22:49:39 +02:00
|
|
|
auto i = std::max_element(begin(), end(), &connecting_time_compare);
|
2012-12-31 07:54:54 +01:00
|
|
|
|
2013-09-14 23:47:31 +02:00
|
|
|
if (i == end() || !(*i)->is_connecting() || (*i)->is_disconnecting())
|
2012-12-31 07:54:54 +01:00
|
|
|
{
|
|
|
|
// this seems odd, but we might as well handle it
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::too_many_connections, op_bittorrent);
|
2012-12-31 07:54:54 +01:00
|
|
|
return false;
|
|
|
|
}
|
2015-02-15 06:17:09 +01:00
|
|
|
(*i)->disconnect(errors::too_many_connections, op_bittorrent);
|
2015-06-20 19:44:07 +02:00
|
|
|
|
2012-12-31 07:54:54 +01:00
|
|
|
// if this peer was let in via connections slack,
|
|
|
|
// it has done its duty of causing the disconnection
|
|
|
|
// of another peer
|
|
|
|
p->peer_disconnected_other();
|
2012-01-21 10:05:34 +01:00
|
|
|
}
|
2012-12-31 07:54:54 +01:00
|
|
|
else
|
2012-01-21 10:05:34 +01:00
|
|
|
{
|
2013-01-02 08:48:09 +01:00
|
|
|
maybe_replace_peer = true;
|
2012-01-21 10:05:34 +01:00
|
|
|
}
|
2007-12-27 11:19:36 +01:00
|
|
|
}
|
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-10-23 04:00:47 +02:00
|
|
|
for (auto& ext : m_extensions)
|
|
|
|
{
|
|
|
|
std::shared_ptr<peer_plugin> pp(ext->new_connection(
|
2015-07-02 06:13:26 +02:00
|
|
|
peer_connection_handle(p->self())));
|
2016-10-23 04:00:47 +02:00
|
|
|
if (pp) p->add_extension(pp);
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
#endif
|
|
|
|
torrent_state st = get_peer_list_state();
|
|
|
|
need_peer_list();
|
|
|
|
if (!m_peer_list->new_connection(*p, m_ses.session_time(), &st))
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
peers_erased(st.erased);
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
debug_log("CLOSING CONNECTION \"%s\" peer list full "
|
|
|
|
"connections: %d limit: %d"
|
|
|
|
, print_endpoint(p->remote()).c_str()
|
|
|
|
, int(m_connections.size())
|
|
|
|
, m_max_connections);
|
2016-09-14 04:46:07 +02:00
|
|
|
}
|
2007-12-19 22:36:54 +01:00
|
|
|
#endif
|
2016-10-23 04:00:47 +02:00
|
|
|
p->disconnect(errors::too_many_connections, op_bittorrent);
|
2008-04-07 04:51:21 +02:00
|
|
|
return false;
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
peers_erased(st.erased);
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(sorted_find(m_connections, p) == m_connections.end());
|
|
|
|
sorted_insert(m_connections, p);
|
|
|
|
update_want_peers();
|
|
|
|
update_want_tick();
|
|
|
|
|
2014-09-24 11:03:57 +02:00
|
|
|
if (p->peer_info_struct() && p->peer_info_struct()->seed)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_num_seeds < 0xffff);
|
|
|
|
++m_num_seeds;
|
|
|
|
}
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
debug_log("incoming peer (%d)", int(m_connections.size()));
|
|
|
|
#endif
|
|
|
|
|
2016-07-02 01:46:59 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2008-01-31 18:52:29 +01:00
|
|
|
TORRENT_ASSERT(p->remote() == p->get_socket()->remote_endpoint(ec) || ec);
|
|
|
|
#endif
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
TORRENT_ASSERT(p->peer_info_struct() != nullptr);
|
2013-01-02 08:48:09 +01:00
|
|
|
|
2014-10-26 08:34:31 +01:00
|
|
|
// we need to do this after we've added the peer to the peer_list
|
2013-01-02 08:48:09 +01:00
|
|
|
// since that's when the peer is assigned its peer_info object,
|
|
|
|
// which holds the rank
|
|
|
|
if (maybe_replace_peer)
|
|
|
|
{
|
|
|
|
// now, find the lowest rank peer and disconnect that
|
|
|
|
// if it's lower rank than the incoming connection
|
|
|
|
peer_connection* peer = find_lowest_ranking_peer();
|
|
|
|
|
2015-01-06 09:08:49 +01:00
|
|
|
// TODO: 2 if peer is a really good peer, maybe we shouldn't disconnect it
|
2015-06-22 08:14:17 +02:00
|
|
|
// perhaps this logic should be disabled if we have too many idle peers
|
|
|
|
// (with some definition of idle)
|
2013-01-02 08:48:09 +01:00
|
|
|
if (peer && peer->peer_rank() < p->peer_rank())
|
|
|
|
{
|
2015-06-22 08:14:17 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("CLOSING CONNECTION \"%s\" peer list full (low peer rank) "
|
|
|
|
"connections: %d limit: %d"
|
|
|
|
, print_endpoint(peer->remote()).c_str()
|
|
|
|
, int(m_connections.size())
|
|
|
|
, m_max_connections);
|
|
|
|
}
|
2015-06-22 08:14:17 +02:00
|
|
|
#endif
|
2015-02-15 06:17:09 +01:00
|
|
|
peer->disconnect(errors::too_many_connections, op_bittorrent);
|
2013-01-02 08:48:09 +01:00
|
|
|
p->peer_disconnected_other();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-06-22 08:14:17 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("CLOSING CONNECTION \"%s\" peer list full (low peer rank) "
|
|
|
|
"connections: %d limit: %d"
|
|
|
|
, print_endpoint(p->remote()).c_str()
|
|
|
|
, int(m_connections.size())
|
|
|
|
, m_max_connections);
|
|
|
|
}
|
2015-06-22 08:14:17 +02:00
|
|
|
#endif
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::too_many_connections, op_bittorrent);
|
2013-01-02 08:48:09 +01:00
|
|
|
// we have to do this here because from the peer's point of
|
|
|
|
// it wasn't really attached to the torrent, but we do need
|
2014-10-26 08:34:31 +01:00
|
|
|
// to let peer_list know we're removing it
|
2013-01-02 08:48:09 +01:00
|
|
|
remove_peer(p);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-21 20:26:09 +01:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2014-10-26 08:34:31 +01:00
|
|
|
if (m_peer_list) m_peer_list->check_invariant();
|
2005-10-01 13:20:47 +02:00
|
|
|
#endif
|
2010-09-05 18:01:36 +02:00
|
|
|
|
|
|
|
if (m_share_mode)
|
|
|
|
recalc_share_mode();
|
|
|
|
|
2015-06-22 08:14:17 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("ATTACHED CONNECTION \"%s\" connections: %d limit: %d"
|
|
|
|
, print_endpoint(p->remote()).c_str(), int(m_connections.size())
|
|
|
|
, m_max_connections);
|
|
|
|
}
|
2015-06-22 08:14:17 +02:00
|
|
|
#endif
|
|
|
|
|
2008-04-07 04:51:21 +02:00
|
|
|
return true;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool torrent::want_tick() const
|
|
|
|
{
|
|
|
|
if (m_abort) return false;
|
|
|
|
|
|
|
|
if (!m_connections.empty()) return true;
|
|
|
|
|
|
|
|
// we might want to connect web seeds
|
|
|
|
if (!is_finished() && !m_web_seeds.empty() && m_files_checked)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0)
|
|
|
|
return true;
|
|
|
|
|
2015-05-26 20:39:49 +02:00
|
|
|
// if we don't get ticks we won't become inactive
|
2016-06-01 07:05:32 +02:00
|
|
|
if (!m_paused && !m_inactive) return true;
|
2015-05-26 20:39:49 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::update_want_tick()
|
|
|
|
{
|
|
|
|
update_list(aux::session_interface::torrent_want_tick, want_tick());
|
|
|
|
}
|
|
|
|
|
2015-05-25 23:46:42 +02:00
|
|
|
// this function adjusts which lists this torrent is part of (checking,
|
|
|
|
// seeding or downloading)
|
|
|
|
void torrent::update_state_list()
|
|
|
|
{
|
|
|
|
bool is_checking = false;
|
|
|
|
bool is_downloading = false;
|
|
|
|
bool is_seeding = false;
|
|
|
|
|
2015-09-19 23:49:01 +02:00
|
|
|
if (is_auto_managed() && !has_error())
|
2015-05-25 23:46:42 +02:00
|
|
|
{
|
2015-09-19 23:49:01 +02:00
|
|
|
if (m_state == torrent_status::checking_files
|
|
|
|
|| m_state == torrent_status::allocating)
|
|
|
|
{
|
|
|
|
is_checking = true;
|
|
|
|
}
|
|
|
|
else if (m_state == torrent_status::downloading_metadata
|
|
|
|
|| m_state == torrent_status::downloading
|
|
|
|
|| m_state == torrent_status::finished
|
2016-09-21 07:10:06 +02:00
|
|
|
|| m_state == torrent_status::seeding)
|
2015-09-19 23:49:01 +02:00
|
|
|
{
|
|
|
|
// torrents that are started (not paused) and
|
|
|
|
// inactive are not part of any list. They will not be touched because
|
|
|
|
// they are inactive
|
|
|
|
if (is_finished())
|
|
|
|
is_seeding = true;
|
|
|
|
else
|
|
|
|
is_downloading = true;
|
|
|
|
}
|
2015-05-25 23:46:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
update_list(aux::session_interface::torrent_downloading_auto_managed
|
|
|
|
, is_downloading);
|
|
|
|
update_list(aux::session_interface::torrent_seeding_auto_managed
|
|
|
|
, is_seeding);
|
|
|
|
update_list(aux::session_interface::torrent_checking_auto_managed
|
|
|
|
, is_checking);
|
|
|
|
}
|
|
|
|
|
2014-08-22 10:55:37 +02:00
|
|
|
// returns true if this torrent is interested in connecting to more peers
|
2014-07-06 21:18:00 +02:00
|
|
|
bool torrent::want_peers() const
|
2007-05-02 19:38:37 +02:00
|
|
|
{
|
2014-08-22 10:55:37 +02:00
|
|
|
// if all our connection slots are taken, we can't connect to more
|
|
|
|
if (m_connections.size() >= m_max_connections) return false;
|
|
|
|
|
|
|
|
// if we're paused, obviously we're not connecting to peers
|
2015-12-04 07:08:01 +01:00
|
|
|
if (is_paused() || m_abort || m_graceful_pause_mode) return false;
|
2014-08-22 10:55:37 +02:00
|
|
|
|
|
|
|
if ((m_state == torrent_status::checking_files
|
|
|
|
|| m_state == torrent_status::checking_resume_data)
|
|
|
|
&& valid_metadata())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// if we don't know of any more potential peers to connect to, there's
|
|
|
|
// no point in trying
|
2014-10-26 08:34:31 +01:00
|
|
|
if (!m_peer_list || m_peer_list->num_connect_candidates() == 0)
|
2014-08-22 10:55:37 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// if the user disabled outgoing connections for seeding torrents,
|
|
|
|
// don't make any
|
2015-05-16 08:33:37 +02:00
|
|
|
if (!settings().get_bool(settings_pack::seeding_outgoing_connections)
|
2014-08-22 10:55:37 +02:00
|
|
|
&& (m_state == torrent_status::seeding
|
|
|
|
|| m_state == torrent_status::finished))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
2007-05-02 19:38:37 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool torrent::want_peers_download() const
|
|
|
|
{
|
|
|
|
return (m_state == torrent_status::downloading
|
|
|
|
|| m_state == torrent_status::downloading_metadata)
|
|
|
|
&& want_peers();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool torrent::want_peers_finished() const
|
|
|
|
{
|
|
|
|
return (m_state == torrent_status::finished
|
|
|
|
|| m_state == torrent_status::seeding)
|
|
|
|
&& want_peers();
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::update_want_peers()
|
|
|
|
{
|
|
|
|
update_list(aux::session_interface::torrent_want_peers_download, want_peers_download());
|
|
|
|
update_list(aux::session_interface::torrent_want_peers_finished, want_peers_finished());
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::update_want_scrape()
|
|
|
|
{
|
2014-10-06 00:30:09 +02:00
|
|
|
update_list(aux::session_interface::torrent_want_scrape
|
2016-06-01 07:05:32 +02:00
|
|
|
, m_paused && m_auto_managed && !m_abort);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2015-08-18 16:42:03 +02:00
|
|
|
namespace {
|
|
|
|
|
2015-09-02 07:30:40 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-07-12 20:08:04 +02:00
|
|
|
char const* list_name(int idx)
|
|
|
|
{
|
|
|
|
#define TORRENT_LIST_NAME(n) case aux::session_interface:: n: return #n;
|
|
|
|
switch (idx)
|
|
|
|
{
|
|
|
|
TORRENT_LIST_NAME(torrent_state_updates);
|
|
|
|
TORRENT_LIST_NAME(torrent_want_tick);
|
|
|
|
TORRENT_LIST_NAME(torrent_want_peers_download);
|
|
|
|
TORRENT_LIST_NAME(torrent_want_peers_finished);
|
|
|
|
TORRENT_LIST_NAME(torrent_want_scrape);
|
|
|
|
TORRENT_LIST_NAME(torrent_downloading_auto_managed);
|
|
|
|
TORRENT_LIST_NAME(torrent_seeding_auto_managed);
|
|
|
|
TORRENT_LIST_NAME(torrent_checking_auto_managed);
|
2016-05-02 18:36:21 +02:00
|
|
|
default: TORRENT_ASSERT_FAIL_VAL(idx);
|
2015-07-12 20:08:04 +02:00
|
|
|
}
|
|
|
|
#undef TORRENT_LIST_NAME
|
2015-07-27 23:49:22 +02:00
|
|
|
return "";
|
2015-07-12 20:08:04 +02:00
|
|
|
}
|
2015-09-02 07:30:40 +02:00
|
|
|
#endif // TORRENT_DISABLE_LOGGING
|
2015-07-12 20:08:04 +02:00
|
|
|
|
2015-08-18 16:42:03 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::update_list(int list, bool in)
|
|
|
|
{
|
|
|
|
link& l = m_links[list];
|
|
|
|
std::vector<torrent*>& v = m_ses.torrent_list(list);
|
2015-07-12 20:08:04 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (in)
|
|
|
|
{
|
|
|
|
if (l.in_list()) return;
|
|
|
|
l.insert(v, this);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!l.in_list()) return;
|
|
|
|
l.unlink(v, list);
|
|
|
|
}
|
2015-07-12 20:08:04 +02:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
debug_log("*** UPDATE LIST [ %s : %d ]", list_name(list), int(in));
|
2015-07-12 20:08:04 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2015-02-15 06:17:09 +01:00
|
|
|
void torrent::disconnect_all(error_code const& ec, operation_t op)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-04-25 23:04:48 +02:00
|
|
|
while (!m_connections.empty())
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-10-31 10:48:20 +01:00
|
|
|
peer_connection* p = *m_connections.begin();
|
|
|
|
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
|
2005-09-01 23:04:21 +02:00
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2006-04-25 23:04:48 +02:00
|
|
|
std::size_t size = m_connections.size();
|
|
|
|
#endif
|
2008-01-17 21:03:59 +01:00
|
|
|
if (p->is_disconnecting())
|
2008-01-17 22:25:19 +01:00
|
|
|
m_connections.erase(m_connections.begin());
|
2008-01-17 21:03:59 +01:00
|
|
|
else
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(ec, op);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_connections.size() <= size);
|
2004-01-20 12:01:50 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
update_want_peers();
|
|
|
|
update_want_tick();
|
2004-01-20 12:01:50 +01:00
|
|
|
}
|
2004-01-13 04:08:59 +01:00
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
namespace {
|
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
// this returns true if lhs is a better disconnect candidate than rhs
|
|
|
|
bool compare_disconnect_peer(peer_connection const* lhs, peer_connection const* rhs)
|
2008-05-12 07:17:11 +02:00
|
|
|
{
|
2010-10-03 12:07:38 +02:00
|
|
|
// prefer to disconnect peers that are already disconnecting
|
|
|
|
if (lhs->is_disconnecting() != rhs->is_disconnecting())
|
|
|
|
return lhs->is_disconnecting();
|
2009-05-22 08:27:47 +02:00
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
// prefer to disconnect peers we're not interested in
|
|
|
|
if (lhs->is_interesting() != rhs->is_interesting())
|
|
|
|
return rhs->is_interesting();
|
2008-05-12 07:17:11 +02:00
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
// prefer to disconnect peers that are not seeds
|
|
|
|
if (lhs->is_seed() != rhs->is_seed())
|
|
|
|
return rhs->is_seed();
|
2008-05-12 07:17:11 +02:00
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
// prefer to disconnect peers that are on parole
|
|
|
|
if (lhs->on_parole() != rhs->on_parole())
|
|
|
|
return lhs->on_parole();
|
2008-05-12 07:17:11 +02:00
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
// prefer to disconnect peers that send data at a lower rate
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t lhs_transferred = lhs->statistics().total_payload_download();
|
|
|
|
std::int64_t rhs_transferred = rhs->statistics().total_payload_download();
|
2008-05-12 07:17:11 +02:00
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point now = aux::time_now();
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t lhs_time_connected = total_seconds(now - lhs->connected_time());
|
|
|
|
std::int64_t rhs_time_connected = total_seconds(now - rhs->connected_time());
|
2008-05-12 07:17:11 +02:00
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
lhs_transferred /= lhs_time_connected + 1;
|
|
|
|
rhs_transferred /= (rhs_time_connected + 1);
|
2016-02-20 06:48:17 +01:00
|
|
|
if (lhs_transferred != rhs_transferred)
|
2010-10-03 12:07:38 +02:00
|
|
|
return lhs_transferred < rhs_transferred;
|
2008-05-12 07:17:11 +02:00
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
// prefer to disconnect peers that chokes us
|
|
|
|
if (lhs->is_choked() != rhs->is_choked())
|
|
|
|
return lhs->is_choked();
|
2008-05-12 07:17:11 +02:00
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
return lhs->last_received() < rhs->last_received();
|
2008-05-12 07:17:11 +02:00
|
|
|
}
|
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2010-10-03 12:07:38 +02:00
|
|
|
int torrent::disconnect_peers(int num, error_code const& ec)
|
2008-05-12 07:17:11 +02:00
|
|
|
{
|
2009-01-27 07:17:55 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-07-02 01:46:59 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2014-07-06 21:18:00 +02:00
|
|
|
for (peer_iterator i = m_connections.begin()
|
2009-05-22 08:27:47 +02:00
|
|
|
, end(m_connections.end()); i != end; ++i)
|
2009-02-03 08:46:24 +01:00
|
|
|
{
|
|
|
|
// make sure this peer is not a dangling pointer
|
|
|
|
TORRENT_ASSERT(m_ses.has_peer(*i));
|
|
|
|
}
|
|
|
|
#endif
|
2009-05-22 08:27:47 +02:00
|
|
|
int ret = 0;
|
|
|
|
while (ret < num && !m_connections.empty())
|
2008-05-12 07:17:11 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
peer_iterator i = std::min_element(
|
2009-05-22 08:27:47 +02:00
|
|
|
m_connections.begin(), m_connections.end(), compare_disconnect_peer);
|
|
|
|
|
2008-05-12 07:17:11 +02:00
|
|
|
peer_connection* p = *i;
|
|
|
|
++ret;
|
2010-03-08 02:54:56 +01:00
|
|
|
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2016-05-02 18:36:21 +02:00
|
|
|
int const num_conns = int(m_connections.size());
|
2010-03-08 02:54:56 +01:00
|
|
|
#endif
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(ec, op_bittorrent);
|
2011-02-21 06:24:41 +01:00
|
|
|
TORRENT_ASSERT(int(m_connections.size()) == num_conns - 1);
|
2008-05-12 07:17:11 +02:00
|
|
|
}
|
2009-05-22 08:27:47 +02:00
|
|
|
|
2008-05-12 07:17:11 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-10-18 02:32:16 +02:00
|
|
|
// called when torrent is finished (all interesting
|
|
|
|
// pieces have been downloaded)
|
2005-06-11 01:12:50 +02:00
|
|
|
void torrent::finished()
|
2004-01-20 12:01:50 +01:00
|
|
|
{
|
2015-05-25 23:46:42 +02:00
|
|
|
update_state_list();
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2008-10-05 03:44:57 +02:00
|
|
|
TORRENT_ASSERT(is_finished());
|
|
|
|
|
2008-07-03 12:05:51 +02:00
|
|
|
set_state(torrent_status::finished);
|
2008-05-29 05:37:19 +02:00
|
|
|
set_queue_position(-1);
|
|
|
|
|
2014-08-27 08:36:28 +02:00
|
|
|
m_became_finished = m_ses.session_time();
|
|
|
|
|
2008-05-29 05:37:19 +02:00
|
|
|
// we have to call completed() before we start
|
|
|
|
// disconnecting peers, since there's an assert
|
|
|
|
// to make sure we're cleared the piece picker
|
|
|
|
if (is_seed()) completed();
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2009-11-24 19:49:59 +01:00
|
|
|
send_upload_only();
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
|
|
|
|
2013-11-04 02:32:16 +01:00
|
|
|
if (m_completed_time == 0)
|
2016-07-09 22:26:26 +02:00
|
|
|
m_completed_time = time(nullptr);
|
2010-03-09 04:21:35 +01:00
|
|
|
|
2008-08-18 22:02:50 +02:00
|
|
|
// disconnect all seeds
|
2014-07-06 21:18:00 +02:00
|
|
|
if (settings().get_bool(settings_pack::close_redundant_connections))
|
2013-01-27 22:25:06 +01:00
|
|
|
{
|
|
|
|
// TODO: 1 should disconnect all peers that have the pieces we have
|
|
|
|
// not just seeds. It would be pretty expensive to check all pieces
|
|
|
|
// for all peers though
|
|
|
|
std::vector<peer_connection*> seeds;
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const p : m_connections)
|
2005-09-01 23:04:21 +02:00
|
|
|
{
|
2013-01-27 22:25:06 +01:00
|
|
|
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
|
|
|
|
if (p->upload_only())
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-03 04:53:54 +02:00
|
|
|
p->peer_log(peer_log_alert::info, "SEED", "CLOSING CONNECTION");
|
2005-09-01 23:04:21 +02:00
|
|
|
#endif
|
2013-01-27 22:25:06 +01:00
|
|
|
seeds.push_back(p);
|
|
|
|
}
|
2005-09-01 23:04:21 +02:00
|
|
|
}
|
2013-01-27 22:25:06 +01:00
|
|
|
std::for_each(seeds.begin(), seeds.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&peer_connection::disconnect, _1, errors::torrent_finished
|
2015-02-15 06:17:09 +01:00
|
|
|
, op_bittorrent, 0));
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-21 14:16:11 +01:00
|
|
|
|
2009-06-10 10:30:55 +02:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
2009-05-06 09:06:26 +02:00
|
|
|
|
2015-08-06 02:13:58 +02:00
|
|
|
if (m_storage)
|
|
|
|
{
|
|
|
|
// we need to keep the object alive during this operation
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_release_files(m_storage
|
2016-11-22 07:48:14 +01:00
|
|
|
, std::bind(&torrent::on_cache_flushed, shared_from_this()));
|
2015-08-06 02:13:58 +02:00
|
|
|
}
|
2015-06-22 08:14:17 +02:00
|
|
|
|
2011-01-30 14:15:29 +01:00
|
|
|
// this torrent just completed downloads, which means it will fall
|
|
|
|
// under a different limit with the auto-manager. Make sure we
|
|
|
|
// update auto-manage torrents in that case
|
2011-12-23 18:16:14 +01:00
|
|
|
if (m_auto_managed)
|
2012-11-03 04:50:12 +01:00
|
|
|
m_ses.trigger_auto_manage();
|
2005-06-11 01:12:50 +02:00
|
|
|
}
|
2008-05-20 05:21:45 +02:00
|
|
|
|
|
|
|
// this is called when we were finished, but some files were
|
2015-07-21 19:12:37 +02:00
|
|
|
// marked for downloading, and we are no longer finished
|
2008-05-20 05:21:45 +02:00
|
|
|
void torrent::resume_download()
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// the invariant doesn't hold here, because it expects the torrent
|
|
|
|
// to be in downloading state (which it will be set to shortly)
|
|
|
|
// INVARIANT_CHECK;
|
2015-06-22 08:14:17 +02:00
|
|
|
|
2013-11-10 09:00:53 +01:00
|
|
|
if (m_state == torrent_status::checking_resume_data
|
|
|
|
|| m_state == torrent_status::checking_files
|
|
|
|
|| m_state == torrent_status::allocating)
|
2014-01-26 05:45:49 +01:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-07-21 19:12:37 +02:00
|
|
|
debug_log("*** RESUME_DOWNLOAD [ skipping, state: %d ]"
|
|
|
|
, int(m_state));
|
2014-01-26 05:45:49 +01:00
|
|
|
#endif
|
2013-11-10 09:00:53 +01:00
|
|
|
return;
|
2014-01-26 05:45:49 +01:00
|
|
|
}
|
2013-11-10 09:00:53 +01:00
|
|
|
|
2015-07-21 19:12:37 +02:00
|
|
|
// we're downloading now, which means we're no longer in seed mode
|
|
|
|
if (m_seed_mode)
|
|
|
|
leave_seed_mode(false);
|
|
|
|
|
2008-06-08 15:47:35 +02:00
|
|
|
TORRENT_ASSERT(!is_finished());
|
2008-07-03 12:05:51 +02:00
|
|
|
set_state(torrent_status::downloading);
|
2008-05-29 05:37:19 +02:00
|
|
|
set_queue_position((std::numeric_limits<int>::max)());
|
2009-11-24 19:49:59 +01:00
|
|
|
|
2010-03-09 04:21:35 +01:00
|
|
|
m_completed_time = 0;
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-01-26 05:45:49 +01:00
|
|
|
debug_log("*** RESUME_DOWNLOAD");
|
|
|
|
#endif
|
2009-11-24 19:49:59 +01:00
|
|
|
send_upload_only();
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_tick();
|
2015-05-25 23:46:42 +02:00
|
|
|
update_state_list();
|
2008-05-20 05:21:45 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::maybe_done_flushing()
|
|
|
|
{
|
|
|
|
if (!has_picker()) return;
|
|
|
|
|
|
|
|
// when we're suggesting read cache pieces, we
|
|
|
|
// still need the piece picker, to keep track
|
|
|
|
// of availability counts for pieces
|
|
|
|
if (m_picker->is_seeding()
|
2016-06-15 19:29:54 +02:00
|
|
|
&& settings().get_int(settings_pack::suggest_mode)
|
|
|
|
!= settings_pack::suggest_read_cache)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
// no need for the piece picker anymore
|
|
|
|
m_picker.reset();
|
|
|
|
m_have_all = true;
|
|
|
|
update_gauge();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// called when torrent is complete. i.e. all pieces downloaded
|
|
|
|
// not necessarily flushed to disk
|
2005-06-11 01:12:50 +02:00
|
|
|
void torrent::completed()
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
maybe_done_flushing();
|
2008-05-29 05:37:19 +02:00
|
|
|
|
2008-07-03 12:05:51 +02:00
|
|
|
set_state(torrent_status::seeding);
|
2014-08-27 08:36:28 +02:00
|
|
|
m_became_seed = m_ses.session_time();
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// no need for this anymore
|
2015-06-29 04:47:11 +02:00
|
|
|
m_file_progress.clear();
|
|
|
|
|
2008-11-29 09:38:40 +01:00
|
|
|
if (!m_announcing) return;
|
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point now = aux::time_now();
|
2008-11-29 09:38:40 +01:00
|
|
|
for (std::vector<announce_entry>::iterator i = m_trackers.begin()
|
|
|
|
, end(m_trackers.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
if (i->complete_sent) continue;
|
|
|
|
i->next_announce = now;
|
2009-12-02 05:05:24 +01:00
|
|
|
i->min_announce = now;
|
2008-11-29 09:38:40 +01:00
|
|
|
}
|
|
|
|
announce_with_tracker();
|
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
|
2016-06-13 13:47:16 +02:00
|
|
|
// the beginning) and return the new index to the tracker.
|
2004-09-12 12:12:16 +02:00
|
|
|
int torrent::prioritize_tracker(int index)
|
|
|
|
{
|
2006-05-28 21:03:54 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(index >= 0);
|
2009-06-10 10:42:05 +02:00
|
|
|
TORRENT_ASSERT(index < int(m_trackers.size()));
|
2015-08-14 05:52:25 +02:00
|
|
|
if (index >= int(m_trackers.size())) return -1;
|
2004-09-12 12:12:16 +02:00
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
while (index > 0 && m_trackers[index].tier == m_trackers[index - 1].tier)
|
2004-09-12 12:12:16 +02:00
|
|
|
{
|
2008-11-29 09:38:40 +01:00
|
|
|
using std::swap;
|
2016-09-07 23:51:18 +02:00
|
|
|
swap(m_trackers[index], m_trackers[index - 1]);
|
2008-12-12 11:00:20 +01:00
|
|
|
if (m_last_working_tracker == index) --m_last_working_tracker;
|
|
|
|
else if (m_last_working_tracker == index - 1) ++m_last_working_tracker;
|
2004-09-12 12:12:16 +02:00
|
|
|
--index;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2008-11-29 09:38:40 +01:00
|
|
|
int torrent::deprioritize_tracker(int index)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2008-11-29 09:38:40 +01:00
|
|
|
TORRENT_ASSERT(index >= 0);
|
2009-06-10 10:42:05 +02:00
|
|
|
TORRENT_ASSERT(index < int(m_trackers.size()));
|
2015-08-14 05:52:25 +02:00
|
|
|
if (index >= int(m_trackers.size())) return -1;
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2009-06-10 10:42:05 +02:00
|
|
|
while (index < int(m_trackers.size()) - 1 && m_trackers[index].tier == m_trackers[index + 1].tier)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2008-11-29 09:38:40 +01:00
|
|
|
using std::swap;
|
|
|
|
swap(m_trackers[index], m_trackers[index + 1]);
|
2008-12-12 11:00:20 +01:00
|
|
|
if (m_last_working_tracker == index) ++m_last_working_tracker;
|
|
|
|
else if (m_last_working_tracker == index + 1) --m_last_working_tracker;
|
2008-11-29 09:38:40 +01:00
|
|
|
++index;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2008-11-29 09:38:40 +01:00
|
|
|
return index;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2010-07-14 06:16:38 +02:00
|
|
|
void torrent::files_checked()
|
2009-05-23 23:36:09 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_torrent_file->is_valid());
|
2003-12-07 16:03:06 +01:00
|
|
|
|
2012-09-28 01:04:51 +02:00
|
|
|
if (m_abort)
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("files_checked(), paused");
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2009-01-02 10:47:51 +01:00
|
|
|
|
2008-10-05 03:44:57 +02:00
|
|
|
// we might be finished already, in which case we should
|
2009-02-03 08:46:24 +01:00
|
|
|
// not switch to downloading mode. If all files are
|
|
|
|
// filtered, we're finished when we start.
|
2012-10-06 19:50:06 +02:00
|
|
|
if (m_state != torrent_status::finished
|
2015-07-21 19:12:37 +02:00
|
|
|
&& m_state != torrent_status::seeding
|
|
|
|
&& !m_seed_mode)
|
|
|
|
{
|
2008-10-05 03:44:57 +02:00
|
|
|
set_state(torrent_status::downloading);
|
2015-07-21 19:12:37 +02:00
|
|
|
}
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2009-02-09 03:04:43 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<torrent_checked_alert>())
|
2008-08-18 22:02:50 +02:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<torrent_checked_alert>(
|
|
|
|
get_handle());
|
2008-08-18 22:02:50 +02:00
|
|
|
}
|
2015-06-10 07:22:01 +02:00
|
|
|
|
2011-02-11 08:10:47 +01:00
|
|
|
// calling pause will also trigger the auto managed
|
|
|
|
// recalculation
|
2012-03-27 02:24:09 +02:00
|
|
|
// if we just got here by downloading the metadata,
|
|
|
|
// just keep going, no need to disconnect all peers just
|
|
|
|
// to restart the torrent in a second
|
2011-12-23 18:16:14 +01:00
|
|
|
if (m_auto_managed)
|
2012-03-27 02:24:09 +02:00
|
|
|
{
|
|
|
|
// if this is an auto managed torrent, force a recalculation
|
|
|
|
// of which torrents to have active
|
2012-11-03 04:50:12 +01:00
|
|
|
m_ses.trigger_auto_manage();
|
2012-03-27 02:24:09 +02:00
|
|
|
}
|
2011-01-30 14:15:29 +01:00
|
|
|
|
2006-12-04 13:20:34 +01:00
|
|
|
if (!is_seed())
|
|
|
|
{
|
2008-12-08 07:36:22 +01:00
|
|
|
// turn off super seeding if we're not a seed
|
2014-07-13 00:03:29 +02:00
|
|
|
if (m_super_seeding)
|
|
|
|
{
|
|
|
|
m_super_seeding = false;
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2014-07-13 00:03:29 +02:00
|
|
|
}
|
2008-12-08 07:36:22 +01:00
|
|
|
|
2009-02-03 08:46:24 +01:00
|
|
|
if (is_finished() && m_state != torrent_status::finished)
|
|
|
|
finished();
|
2008-08-18 22:02:50 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-09-23 22:51:20 +02:00
|
|
|
for (auto& t : m_trackers)
|
|
|
|
t.complete_sent = true;
|
2009-02-03 08:46:24 +01:00
|
|
|
|
2012-10-06 19:50:06 +02:00
|
|
|
if (m_state != torrent_status::finished
|
|
|
|
&& m_state != torrent_status::seeding)
|
2009-02-03 08:46:24 +01:00
|
|
|
finished();
|
2006-12-04 13:20:34 +01:00
|
|
|
}
|
2007-05-10 00:54:26 +02:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-05-08 00:46:42 +02:00
|
|
|
for (auto& ext : m_extensions)
|
2007-05-10 00:54:26 +02:00
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
ext->on_files_checked();
|
2007-05-10 00:54:26 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-10-12 08:03:19 +02:00
|
|
|
m_connections_initialized = true;
|
|
|
|
m_files_checked = true;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_tick();
|
|
|
|
|
2013-10-12 08:03:19 +02:00
|
|
|
for (torrent::peer_iterator i = m_connections.begin();
|
|
|
|
i != m_connections.end();)
|
2005-10-16 11:15:46 +02:00
|
|
|
{
|
2013-10-12 08:03:19 +02:00
|
|
|
peer_connection* pc = *i;
|
|
|
|
++i;
|
|
|
|
|
2005-10-16 11:15:46 +02:00
|
|
|
// all peer connections have to initialize themselves now that the metadata
|
|
|
|
// is available
|
2013-10-12 08:03:19 +02:00
|
|
|
if (!m_connections_initialized)
|
2005-10-16 11:15:46 +02:00
|
|
|
{
|
2008-08-29 19:21:56 +02:00
|
|
|
if (pc->is_disconnecting()) continue;
|
|
|
|
pc->on_metadata_impl();
|
|
|
|
if (pc->is_disconnecting()) continue;
|
2008-07-18 12:03:42 +02:00
|
|
|
pc->init();
|
2005-10-16 11:15:46 +02:00
|
|
|
}
|
2013-10-12 08:03:19 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-03 04:53:54 +02:00
|
|
|
pc->peer_log(peer_log_alert::info, "ON_FILES_CHECKED");
|
2013-10-12 08:03:19 +02:00
|
|
|
#endif
|
|
|
|
if (pc->is_interesting() && !pc->has_peer_choked())
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (request_a_block(*this, *pc))
|
|
|
|
{
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::unchoke_piece_picks);
|
2014-07-06 21:18:00 +02:00
|
|
|
pc->send_block_requests();
|
|
|
|
}
|
2013-10-12 08:03:19 +02:00
|
|
|
}
|
2005-10-16 11:15:46 +02:00
|
|
|
}
|
2008-04-23 03:01:00 +02:00
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
start_announcing();
|
2013-08-19 05:54:45 +02:00
|
|
|
|
|
|
|
maybe_connect_web_seeds();
|
2003-11-07 02:44:30 +01:00
|
|
|
}
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
alert_manager& torrent::alerts() const
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
return m_ses.alerts();
|
2004-01-07 01:48:02 +01:00
|
|
|
}
|
|
|
|
|
2015-09-25 23:22:38 +02:00
|
|
|
bool torrent::is_seed() const
|
|
|
|
{
|
|
|
|
if (!valid_metadata()) return false;
|
|
|
|
if (m_seed_mode) return true;
|
|
|
|
if (m_have_all) return true;
|
|
|
|
if (m_picker && m_picker->num_passed() == m_picker->num_pieces()) return true;
|
|
|
|
return m_state == torrent_status::seeding;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool torrent::is_finished() const
|
|
|
|
{
|
|
|
|
if (is_seed()) return true;
|
|
|
|
|
|
|
|
// this is slightly different from m_picker->is_finished()
|
|
|
|
// because any piece that has *passed* is considered here,
|
|
|
|
// which may be more than the piece we *have* (i.e. written to disk)
|
|
|
|
// keep in mind that num_filtered() does not include pieces we
|
|
|
|
// have that are filtered
|
|
|
|
return valid_metadata() && has_picker()
|
|
|
|
&& m_torrent_file->num_pieces() - m_picker->num_filtered() - m_picker->num_passed() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool torrent::is_inactive() const
|
|
|
|
{
|
|
|
|
if (!settings().get_bool(settings_pack::dont_count_slow_torrents))
|
|
|
|
return false;
|
|
|
|
return m_inactive;
|
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
std::string torrent::save_path() const
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
2008-03-08 07:06:31 +01:00
|
|
|
return m_save_path;
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::rename_file(file_index_t const index, std::string const& name)
|
2008-05-28 10:44:40 +02:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
|
|
|
TORRENT_ASSERT(index >= file_index_t(0));
|
|
|
|
TORRENT_ASSERT(index < fs.end_file());
|
|
|
|
TORRENT_UNUSED(fs);
|
2008-05-28 10:44:40 +02:00
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
// storage may be nullptr during shutdown
|
2016-12-31 18:35:10 +01:00
|
|
|
if (!m_storage)
|
2014-12-26 22:25:37 +01:00
|
|
|
{
|
|
|
|
if (alerts().should_post<file_rename_failed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<file_rename_failed_alert>(get_handle()
|
2016-10-02 21:27:50 +02:00
|
|
|
, index, errors::session_is_closing);
|
2014-12-26 22:25:37 +01:00
|
|
|
return;
|
|
|
|
}
|
2008-05-28 10:44:40 +02:00
|
|
|
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_rename_file(m_storage, index, name
|
2016-11-23 07:43:57 +01:00
|
|
|
, std::bind(&torrent::on_file_renamed, shared_from_this(), _1, _2, _3));
|
2014-12-26 22:25:37 +01:00
|
|
|
return;
|
2008-05-28 10:44:40 +02:00
|
|
|
}
|
|
|
|
|
2016-11-25 17:17:25 +01:00
|
|
|
void torrent::move_storage(std::string const& save_path, int const flags)
|
2004-07-18 02:39:58 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-05-28 21:03:54 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2013-08-12 02:51:49 +02:00
|
|
|
if (m_abort)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<storage_moved_failed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<storage_moved_failed_alert>(get_handle(), boost::asio::error::operation_aborted
|
|
|
|
, "", "");
|
2013-08-12 02:51:49 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-12-18 23:39:25 +01:00
|
|
|
// if we don't have metadata yet, we don't know anything about the file
|
|
|
|
// structure and we have to assume we don't have any file. Deleting files
|
|
|
|
// in this mode would cause us to (recursively) delete m_save_path, which
|
|
|
|
// is bad.
|
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
|
|
|
alerts().emplace_alert<torrent_deleted_alert>(get_handle(), m_torrent_file->info_hash());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
// storage may be nullptr during shutdown
|
2016-12-31 18:35:10 +01:00
|
|
|
if (m_storage)
|
2005-01-10 12:14:22 +01:00
|
|
|
{
|
2012-04-29 06:20:02 +02:00
|
|
|
#if TORRENT_USE_UNC_PATHS
|
|
|
|
std::string path = canonicalize_path(save_path);
|
|
|
|
#else
|
|
|
|
std::string const& path = save_path;
|
|
|
|
#endif
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_move_storage(m_storage, path, std::uint8_t(flags)
|
2016-11-22 07:48:14 +01:00
|
|
|
, std::bind(&torrent::on_storage_moved, shared_from_this(), _1, _2, _3));
|
2014-01-01 03:04:26 +01:00
|
|
|
m_moving_storage = true;
|
2005-01-10 12:14:22 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-29 06:20:02 +02:00
|
|
|
#if TORRENT_USE_UNC_PATHS
|
|
|
|
m_save_path = canonicalize_path(save_path);
|
2012-04-29 06:23:29 +02:00
|
|
|
#else
|
|
|
|
|
|
|
|
m_save_path = save_path;
|
2012-04-29 06:20:02 +02:00
|
|
|
#endif
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2014-06-13 00:39:41 +02:00
|
|
|
|
2009-05-11 23:18:09 +02:00
|
|
|
if (alerts().should_post<storage_moved_alert>())
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<storage_moved_alert>(get_handle(), m_save_path);
|
2009-05-11 23:18:09 +02:00
|
|
|
}
|
2005-01-10 12:14:22 +01:00
|
|
|
}
|
2004-07-18 02:39:58 +02:00
|
|
|
}
|
|
|
|
|
2016-11-26 07:51:47 +01:00
|
|
|
void torrent::on_storage_moved(status_t const status, std::string const& path
|
2016-11-22 07:48:14 +01:00
|
|
|
, storage_error const& error) try
|
2007-06-11 23:24:14 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-06-11 23:24:14 +02:00
|
|
|
|
2014-01-01 03:04:26 +01:00
|
|
|
m_moving_storage = false;
|
2016-11-26 07:51:47 +01:00
|
|
|
if (status == status_t::no_error
|
|
|
|
|| status == status_t::need_full_check)
|
2009-05-11 23:18:09 +02:00
|
|
|
{
|
|
|
|
if (alerts().should_post<storage_moved_alert>())
|
2016-11-22 07:48:14 +01:00
|
|
|
alerts().emplace_alert<storage_moved_alert>(get_handle(), path);
|
|
|
|
m_save_path = path;
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2016-11-26 07:51:47 +01:00
|
|
|
if (status == status_t::need_full_check)
|
2013-05-09 04:50:16 +02:00
|
|
|
force_recheck();
|
2009-05-11 23:18:09 +02:00
|
|
|
}
|
|
|
|
else
|
2007-06-11 23:24:14 +02:00
|
|
|
{
|
2009-05-11 23:18:09 +02:00
|
|
|
if (alerts().should_post<storage_moved_failed_alert>())
|
2016-11-22 07:48:14 +01:00
|
|
|
alerts().emplace_alert<storage_moved_failed_alert>(get_handle(), error.ec
|
2016-12-22 16:42:33 +01:00
|
|
|
, resolve_filename(error.file()), error.operation_str());
|
2007-06-11 23:24:14 +02:00
|
|
|
}
|
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2007-06-11 23:24:14 +02:00
|
|
|
|
2008-04-09 22:09:36 +02:00
|
|
|
torrent_handle torrent::get_handle()
|
2004-01-07 01:48:02 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-04-09 22:09:36 +02:00
|
|
|
return torrent_handle(shared_from_this());
|
2004-01-07 01:48:02 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
aux::session_settings const& torrent::settings() const
|
2006-05-15 00:30:05 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-10-11 16:02:21 +02:00
|
|
|
return m_ses.settings();
|
2006-05-15 00:30:05 +02:00
|
|
|
}
|
2004-01-07 01:48:02 +01:00
|
|
|
|
2014-01-21 20:26:09 +01:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2004-06-14 01:30:42 +02:00
|
|
|
void torrent::check_invariant() const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2016-12-12 03:37:07 +01:00
|
|
|
TORRENT_ASSERT(current_stats_state() == int(m_current_gauge_state + counters::num_checking_torrents)
|
2014-07-06 21:18:00 +02:00
|
|
|
|| m_current_gauge_state == no_gauge_state);
|
|
|
|
|
|
|
|
for (std::vector<time_critical_piece>::const_iterator i = m_time_critical_pieces.begin()
|
2013-01-28 08:42:51 +01:00
|
|
|
, end(m_time_critical_pieces.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(!is_seed());
|
|
|
|
TORRENT_ASSERT(!has_picker() || !m_picker->have_piece(i->piece));
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
switch (current_stats_state())
|
|
|
|
{
|
|
|
|
case counters::num_error_torrents: TORRENT_ASSERT(has_error()); break;
|
2015-06-22 08:14:17 +02:00
|
|
|
case counters::num_checking_torrents:
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_NO_DEPRECATE
|
|
|
|
TORRENT_ASSERT(state() == torrent_status::checking_files);
|
|
|
|
#else
|
|
|
|
TORRENT_ASSERT(state() == torrent_status::checking_files
|
|
|
|
|| state() == torrent_status::queued_for_checking);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case counters::num_seeding_torrents: TORRENT_ASSERT(is_seed()); break;
|
|
|
|
case counters::num_upload_only_torrents: TORRENT_ASSERT(is_upload_only()); break;
|
|
|
|
case counters::num_stopped_torrents: TORRENT_ASSERT(!is_auto_managed()
|
2016-06-01 07:05:32 +02:00
|
|
|
&& (m_paused || m_graceful_pause_mode));
|
2014-07-06 21:18:00 +02:00
|
|
|
break;
|
|
|
|
case counters::num_queued_seeding_torrents:
|
2016-06-01 07:05:32 +02:00
|
|
|
TORRENT_ASSERT((m_paused || m_graceful_pause_mode) && is_seed()); break;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2008-11-19 01:46:48 +01:00
|
|
|
|
2014-11-12 17:57:59 +01:00
|
|
|
if (m_torrent_file)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_info_hash == m_torrent_file->info_hash());
|
|
|
|
}
|
|
|
|
|
2016-06-18 14:31:07 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2014-07-06 21:18:00 +02:00
|
|
|
for (int i = 0; i < aux::session_interface::num_torrent_lists; ++i)
|
|
|
|
{
|
|
|
|
if (!m_links[i].in_list()) continue;
|
2016-12-22 16:42:33 +01:00
|
|
|
int const index = m_links[i].index;
|
2011-06-03 10:40:13 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(index >= 0);
|
|
|
|
TORRENT_ASSERT(index < int(m_ses.torrent_list(i).size()));
|
2008-08-10 11:34:39 +02:00
|
|
|
}
|
2013-12-05 08:42:32 +01:00
|
|
|
#endif
|
2008-08-10 11:34:39 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(want_peers_download() == m_links[aux::session_interface::torrent_want_peers_download].in_list());
|
|
|
|
TORRENT_ASSERT(want_peers_finished() == m_links[aux::session_interface::torrent_want_peers_finished].in_list());
|
|
|
|
TORRENT_ASSERT(want_tick() == m_links[aux::session_interface::torrent_want_tick].in_list());
|
2016-06-01 07:05:32 +02:00
|
|
|
TORRENT_ASSERT((m_paused && m_auto_managed && !m_abort) == m_links[aux::session_interface::torrent_want_scrape].in_list());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-05-25 23:46:42 +02:00
|
|
|
bool is_checking = false;
|
|
|
|
bool is_downloading = false;
|
|
|
|
bool is_seeding = false;
|
|
|
|
|
2015-09-19 23:49:01 +02:00
|
|
|
if (is_auto_managed() && !has_error())
|
2015-05-25 23:46:42 +02:00
|
|
|
{
|
2015-09-19 23:49:01 +02:00
|
|
|
if (m_state == torrent_status::checking_files
|
|
|
|
|| m_state == torrent_status::allocating)
|
|
|
|
{
|
|
|
|
is_checking = true;
|
|
|
|
}
|
|
|
|
else if (m_state == torrent_status::downloading_metadata
|
|
|
|
|| m_state == torrent_status::downloading
|
|
|
|
|| m_state == torrent_status::finished
|
|
|
|
|| m_state == torrent_status::seeding
|
|
|
|
|| m_state == torrent_status::downloading)
|
|
|
|
{
|
|
|
|
if (is_finished())
|
|
|
|
is_seeding = true;
|
|
|
|
else
|
|
|
|
is_downloading = true;
|
|
|
|
}
|
2015-05-25 23:46:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TORRENT_ASSERT(m_links[aux::session_interface::torrent_checking_auto_managed].in_list()
|
|
|
|
== is_checking);
|
|
|
|
TORRENT_ASSERT(m_links[aux::session_interface::torrent_downloading_auto_managed].in_list()
|
|
|
|
== is_downloading);
|
|
|
|
TORRENT_ASSERT(m_links[aux::session_interface::torrent_seeding_auto_managed].in_list()
|
|
|
|
== is_seeding);
|
|
|
|
|
2015-07-21 19:12:37 +02:00
|
|
|
if (m_seed_mode)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(is_seed());
|
|
|
|
}
|
2015-05-25 23:46:42 +02:00
|
|
|
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
// this fires during disconnecting peers
|
|
|
|
// if (is_paused()) TORRENT_ASSERT(num_peers() == 0 || m_graceful_pause_mode);
|
|
|
|
|
2014-09-24 11:03:57 +02:00
|
|
|
int seeds = 0;
|
2007-08-17 09:37:08 +02:00
|
|
|
int num_uploads = 0;
|
2007-07-08 22:45:42 +02:00
|
|
|
std::map<piece_block, int> num_requests;
|
2014-07-05 16:10:25 +02:00
|
|
|
for (const_peer_iterator i = this->begin(); i != this->end(); ++i)
|
2006-05-28 21:03:54 +02:00
|
|
|
{
|
2008-07-12 19:51:59 +02:00
|
|
|
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
|
2008-01-07 02:10:46 +01:00
|
|
|
// make sure this peer is not a dangling pointer
|
|
|
|
TORRENT_ASSERT(m_ses.has_peer(*i));
|
2008-07-12 19:51:59 +02:00
|
|
|
#endif
|
2007-10-31 10:48:20 +01:00
|
|
|
peer_connection const& p = *(*i);
|
2014-09-24 11:03:57 +02:00
|
|
|
|
|
|
|
if (p.peer_info_struct() && p.peer_info_struct()->seed)
|
|
|
|
++seeds;
|
|
|
|
|
2015-08-18 16:42:03 +02:00
|
|
|
for (std::vector<pending_block>::const_iterator j = p.request_queue().begin()
|
|
|
|
, end(p.request_queue().end()); j != end; ++j)
|
|
|
|
{
|
|
|
|
if (!j->not_wanted && !j->timed_out) ++num_requests[j->block];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::vector<pending_block>::const_iterator j = p.download_queue().begin()
|
|
|
|
, end(p.download_queue().end()); j != end; ++j)
|
|
|
|
{
|
|
|
|
if (!j->not_wanted && !j->timed_out) ++num_requests[j->block];
|
|
|
|
}
|
|
|
|
|
2009-01-28 07:14:21 +01:00
|
|
|
if (!p.is_choked() && !p.ignore_unchoke_slots()) ++num_uploads;
|
2006-05-28 21:03:54 +02:00
|
|
|
torrent* associated_torrent = p.associated_torrent().lock().get();
|
2016-07-09 22:26:26 +02:00
|
|
|
if (associated_torrent != this && associated_torrent != nullptr)
|
2016-05-02 18:36:21 +02:00
|
|
|
TORRENT_ASSERT_FAIL();
|
2006-05-28 21:03:54 +02:00
|
|
|
}
|
2011-02-21 06:24:41 +01:00
|
|
|
TORRENT_ASSERT(num_uploads == int(m_num_uploads));
|
2014-09-24 11:03:57 +02:00
|
|
|
TORRENT_ASSERT(seeds == int(m_num_seeds));
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2007-07-08 22:45:42 +02:00
|
|
|
if (has_picker())
|
|
|
|
{
|
|
|
|
for (std::map<piece_block, int>::iterator i = num_requests.begin()
|
|
|
|
, end(num_requests.end()); i != end; ++i)
|
|
|
|
{
|
2009-03-17 10:34:44 +01:00
|
|
|
piece_block b = i->first;
|
|
|
|
int count = i->second;
|
|
|
|
int picker_count = m_picker->num_peers(b);
|
2012-09-11 07:27:14 +02:00
|
|
|
// if we're no longer downloading the piece
|
|
|
|
// (for instance, it may be fully downloaded and waiting
|
|
|
|
// for the hash check to return), the piece picker always
|
|
|
|
// returns 0 requests, regardless of how many peers may still
|
|
|
|
// have the block in their queue
|
|
|
|
if (!m_picker->is_downloaded(b) && m_picker->is_downloading(b.piece_index))
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (picker_count != count)
|
|
|
|
{
|
2016-05-17 15:24:06 +02:00
|
|
|
std::fprintf(stderr, "picker count discrepancy: "
|
2014-07-06 21:18:00 +02:00
|
|
|
"picker: %d != peerlist: %d\n", picker_count, count);
|
|
|
|
|
2015-08-18 16:42:03 +02:00
|
|
|
for (const_peer_iterator j = this->begin(); j != this->end(); ++j)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2015-08-18 16:42:03 +02:00
|
|
|
peer_connection const& p = *(*j);
|
2016-05-17 15:24:06 +02:00
|
|
|
std::fprintf(stderr, "peer: %s\n", print_endpoint(p.remote()).c_str());
|
2016-12-22 16:42:33 +01:00
|
|
|
for (auto const& k : p.request_queue())
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
std::fprintf(stderr, " rq: (%d, %d) %s %s %s\n"
|
|
|
|
, static_cast<int>(k.block.piece_index)
|
|
|
|
, k.block.block_index, k.not_wanted ? "not-wanted" : ""
|
|
|
|
, k.timed_out ? "timed-out" : "", k.busy ? "busy": "");
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2016-12-22 16:42:33 +01:00
|
|
|
for (auto const& k : p.download_queue())
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
std::fprintf(stderr, " dq: (%d, %d) %s %s %s\n"
|
|
|
|
, static_cast<int>(k.block.piece_index)
|
|
|
|
, k.block.block_index, k.not_wanted ? "not-wanted" : ""
|
|
|
|
, k.timed_out ? "timed-out" : "", k.busy ? "busy": "");
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
2016-05-02 18:36:21 +02:00
|
|
|
TORRENT_ASSERT_FAIL();
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
2007-07-08 22:45:42 +02:00
|
|
|
}
|
2008-06-07 16:03:21 +02:00
|
|
|
TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered());
|
2007-07-08 22:45:42 +02:00
|
|
|
}
|
|
|
|
|
2006-12-11 13:48:33 +01:00
|
|
|
if (valid_metadata())
|
|
|
|
{
|
2010-02-22 02:51:25 +01:00
|
|
|
TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == m_torrent_file->num_pieces());
|
2006-12-11 13:48:33 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-02-22 02:51:25 +01:00
|
|
|
TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == 0);
|
2006-12-11 13:48:33 +01:00
|
|
|
}
|
|
|
|
|
2008-07-12 19:51:59 +02:00
|
|
|
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
|
2009-04-30 07:49:46 +02:00
|
|
|
// make sure we haven't modified the peer object
|
|
|
|
// in a way that breaks the sort order
|
2016-06-01 07:05:17 +02:00
|
|
|
if (m_peer_list && m_peer_list->begin() != m_peer_list->end())
|
2009-04-30 07:49:46 +02:00
|
|
|
{
|
2016-06-01 07:05:17 +02:00
|
|
|
auto i = m_peer_list->begin();
|
|
|
|
auto p = i++;
|
|
|
|
auto end(m_peer_list->end());
|
2014-07-06 21:18:00 +02:00
|
|
|
peer_address_compare cmp;
|
2016-05-02 18:36:21 +02:00
|
|
|
for (; i != end; ++i, ++p)
|
2009-04-30 07:49:46 +02:00
|
|
|
{
|
2016-05-02 18:36:21 +02:00
|
|
|
TORRENT_ASSERT(!cmp(*i, *p));
|
2009-04-30 07:49:46 +02:00
|
|
|
}
|
2007-09-19 08:05:14 +02:00
|
|
|
}
|
2008-07-12 19:51:59 +02:00
|
|
|
#endif
|
2007-10-31 10:48:20 +01:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t total_done = quantized_bytes_done();
|
2007-09-01 05:00:31 +02:00
|
|
|
if (m_torrent_file->is_valid())
|
2007-02-15 04:03:50 +01:00
|
|
|
{
|
|
|
|
if (is_seed())
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(total_done == m_torrent_file->total_size());
|
2007-02-15 04:03:50 +01:00
|
|
|
else
|
2008-06-07 04:58:28 +02:00
|
|
|
TORRENT_ASSERT(total_done != m_torrent_file->total_size() || !m_files_checked);
|
2008-06-18 14:34:39 +02:00
|
|
|
|
2010-02-14 02:39:55 +01:00
|
|
|
TORRENT_ASSERT(block_size() <= m_torrent_file->piece_length());
|
2007-02-15 04:03:50 +01:00
|
|
|
}
|
2006-12-11 13:48:33 +01:00
|
|
|
else
|
2007-02-15 04:03:50 +01:00
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(total_done == 0);
|
2007-02-15 04:03:50 +01:00
|
|
|
}
|
2014-07-05 01:40:31 +02:00
|
|
|
/*
|
2007-10-18 06:18:09 +02:00
|
|
|
if (m_picker && !m_abort)
|
2007-10-18 02:32:16 +02:00
|
|
|
{
|
|
|
|
// make sure that pieces that have completed the download
|
|
|
|
// of all their blocks are in the disk io thread's queue
|
|
|
|
// to be checked.
|
2014-07-06 21:18:00 +02:00
|
|
|
std::vector<piece_picker::downloading_piece> dl_queue
|
2007-10-18 02:32:16 +02:00
|
|
|
= m_picker->get_download_queue();
|
|
|
|
for (std::vector<piece_picker::downloading_piece>::const_iterator i =
|
|
|
|
dl_queue.begin(); i != dl_queue.end(); ++i)
|
|
|
|
{
|
|
|
|
const int blocks_per_piece = m_picker->blocks_in_piece(i->index);
|
|
|
|
|
|
|
|
bool complete = true;
|
|
|
|
for (int j = 0; j < blocks_per_piece; ++j)
|
|
|
|
{
|
|
|
|
if (i->info[j].state == piece_picker::block_info::state_finished)
|
|
|
|
continue;
|
|
|
|
complete = false;
|
|
|
|
break;
|
|
|
|
}
|
2014-07-05 01:40:31 +02:00
|
|
|
TORRENT_ASSERT(complete);
|
2007-10-18 02:32:16 +02:00
|
|
|
}
|
|
|
|
}
|
2015-06-20 23:51:59 +02:00
|
|
|
*/
|
2010-02-22 02:51:25 +01:00
|
|
|
if (m_files_checked && valid_metadata())
|
2009-02-03 08:46:24 +01:00
|
|
|
{
|
2010-02-14 02:39:55 +01:00
|
|
|
TORRENT_ASSERT(block_size() > 0);
|
2009-02-03 08:46:24 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-01-31 18:52:29 +01:00
|
|
|
void torrent::set_sequential_download(bool sd)
|
2010-07-14 06:16:38 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2011-11-15 03:34:00 +01:00
|
|
|
if (m_sequential_download == sd) return;
|
2010-07-14 06:16:38 +02:00
|
|
|
m_sequential_download = sd;
|
2015-06-20 23:51:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("*** set-sequential-download: %d", sd);
|
|
|
|
#endif
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2013-02-20 09:10:31 +01:00
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2010-07-14 06:16:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::queue_up()
|
|
|
|
{
|
|
|
|
set_queue_position(queue_position() == 0
|
|
|
|
? queue_position() : queue_position() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::queue_down()
|
|
|
|
{
|
|
|
|
set_queue_position(queue_position() + 1);
|
|
|
|
}
|
2006-09-04 19:17:45 +02:00
|
|
|
|
2008-05-29 05:37:19 +02:00
|
|
|
void torrent::set_queue_position(int p)
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-06-17 18:49:55 +02:00
|
|
|
TORRENT_ASSERT((p == -1) == is_finished()
|
|
|
|
|| (!m_auto_managed && p == -1)
|
|
|
|
|| (m_abort && p == -1));
|
2008-06-08 15:47:35 +02:00
|
|
|
if (is_finished() && p != -1) return;
|
2008-05-29 05:37:19 +02:00
|
|
|
if (p == m_sequence_number) return;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(p >= -1);
|
2008-06-11 14:14:10 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
state_updated();
|
2008-06-03 22:21:47 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_ses.set_queue_position(this, p);
|
2008-05-29 05:37:19 +02:00
|
|
|
}
|
2006-09-04 19:17:45 +02:00
|
|
|
|
2012-08-31 19:04:02 +02:00
|
|
|
void torrent::set_max_uploads(int limit, bool state_update)
|
2004-10-29 15:21:09 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(limit >= -1);
|
2016-12-15 20:20:08 +01:00
|
|
|
if (limit <= 0) limit = (1 << 24) - 1;
|
2012-08-31 19:04:02 +02:00
|
|
|
if (m_max_uploads != limit && state_update) state_updated();
|
2007-08-16 14:41:46 +02:00
|
|
|
m_max_uploads = limit;
|
2015-06-20 23:51:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("*** set-max-uploads: %d", m_max_uploads);
|
|
|
|
#endif
|
2013-02-20 09:10:31 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (state_update)
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2004-10-29 15:21:09 +02:00
|
|
|
}
|
|
|
|
|
2012-08-31 19:04:02 +02:00
|
|
|
void torrent::set_max_connections(int limit, bool state_update)
|
2004-10-29 15:21:09 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(limit >= -1);
|
2016-12-15 20:20:08 +01:00
|
|
|
if (limit <= 0) limit = (1 << 24) - 1;
|
2012-08-31 19:04:02 +02:00
|
|
|
if (m_max_connections != limit && state_update) state_updated();
|
2007-08-16 14:41:46 +02:00
|
|
|
m_max_connections = limit;
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
2010-10-03 12:07:38 +02:00
|
|
|
|
2015-06-20 23:51:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("*** set-max-connections: %d", m_max_connections);
|
|
|
|
#endif
|
|
|
|
|
2011-02-21 06:24:41 +01:00
|
|
|
if (num_peers() > int(m_max_connections))
|
2010-10-03 12:07:38 +02:00
|
|
|
{
|
|
|
|
disconnect_peers(num_peers() - m_max_connections
|
2016-10-02 21:27:50 +02:00
|
|
|
, errors::too_many_connections);
|
2010-10-03 12:07:38 +02:00
|
|
|
}
|
2013-02-20 09:10:31 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (state_update)
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2004-10-29 15:21:09 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::set_upload_limit(int limit)
|
2010-10-01 18:09:22 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
set_limit_impl(limit, peer_connection::upload_channel);
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2015-06-20 23:51:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("*** set-upload-limit: %d", limit);
|
|
|
|
#endif
|
2010-10-01 18:09:22 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::set_download_limit(int limit)
|
2010-10-01 18:09:22 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
set_limit_impl(limit, peer_connection::download_channel);
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2015-06-20 23:51:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("*** set-download-limit: %d", limit);
|
|
|
|
#endif
|
2010-10-01 18:09:22 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::set_limit_impl(int limit, int channel, bool state_update)
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(limit >= -1);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (limit <= 0) limit = 0;
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_peer_class == 0 && limit == 0) return;
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_peer_class == 0)
|
|
|
|
setup_peer_class();
|
|
|
|
|
|
|
|
struct peer_class* tpc = m_ses.peer_classes().at(m_peer_class);
|
|
|
|
TORRENT_ASSERT(tpc);
|
|
|
|
if (tpc->channel[channel].throttle() != limit && state_update)
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2014-07-06 21:18:00 +02:00
|
|
|
tpc->channel[channel].throttle(limit);
|
|
|
|
}
|
2013-02-20 09:10:31 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::setup_peer_class()
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_peer_class == 0);
|
|
|
|
m_peer_class = m_ses.peer_classes().new_peer_class(name());
|
|
|
|
add_class(m_ses.peer_classes(), m_peer_class);
|
2004-07-24 13:54:17 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int torrent::limit_impl(int channel) const
|
2007-04-10 11:25:17 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (m_peer_class == 0) return -1;
|
|
|
|
int limit = m_ses.peer_classes().at(m_peer_class)->channel[channel].throttle();
|
2007-08-21 06:46:17 +02:00
|
|
|
if (limit == (std::numeric_limits<int>::max)()) limit = -1;
|
2007-04-10 11:25:17 +02:00
|
|
|
return limit;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int torrent::upload_limit() const
|
2004-07-24 13:54:17 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
return limit_impl(peer_connection::upload_channel);
|
2004-07-24 13:54:17 +02:00
|
|
|
}
|
|
|
|
|
2007-04-10 11:25:17 +02:00
|
|
|
int torrent::download_limit() const
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
return limit_impl(peer_connection::download_channel);
|
2007-04-10 11:25:17 +02:00
|
|
|
}
|
|
|
|
|
2016-03-13 08:50:37 +01:00
|
|
|
bool torrent::delete_files(int const options)
|
2007-10-13 05:33:33 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2010-08-18 19:14:40 +02:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-03 04:53:54 +02:00
|
|
|
log_to_all_peers("deleting files");
|
2007-10-13 05:33:33 +02:00
|
|
|
#endif
|
|
|
|
|
2015-02-15 06:17:09 +01:00
|
|
|
disconnect_all(errors::torrent_removed, op_bittorrent);
|
2008-07-12 10:25:19 +02:00
|
|
|
stop_announcing();
|
2007-10-13 05:33:33 +02:00
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
// storage may be nullptr during shutdown
|
2016-12-31 18:35:10 +01:00
|
|
|
if (m_storage)
|
2007-10-13 05:33:33 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_storage);
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_delete_files(m_storage, options
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&torrent::on_files_deleted, shared_from_this(), _1));
|
2013-08-22 07:27:45 +02:00
|
|
|
m_deleted = true;
|
2013-03-17 01:50:33 +01:00
|
|
|
return true;
|
2007-10-13 05:33:33 +02:00
|
|
|
}
|
2013-03-17 01:50:33 +01:00
|
|
|
return false;
|
2007-10-13 05:33:33 +02:00
|
|
|
}
|
|
|
|
|
2008-07-12 19:00:52 +02:00
|
|
|
void torrent::clear_error()
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2009-02-23 02:21:19 +01:00
|
|
|
if (!m_error) return;
|
2008-11-19 01:46:48 +01:00
|
|
|
bool checking_files = should_check_files();
|
2012-11-03 04:50:12 +01:00
|
|
|
m_ses.trigger_auto_manage();
|
2009-02-23 02:21:19 +01:00
|
|
|
m_error = error_code();
|
2015-11-03 06:12:30 +01:00
|
|
|
m_error_file = torrent_status::error_file_none;
|
2010-12-30 02:47:30 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2015-05-25 23:46:42 +02:00
|
|
|
update_want_peers();
|
|
|
|
update_state_list();
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2010-12-30 02:47:30 +01:00
|
|
|
// if we haven't downloaded the metadata from m_url, try again
|
|
|
|
if (!m_url.empty() && !m_torrent_file->is_valid())
|
|
|
|
{
|
|
|
|
start_download_url();
|
|
|
|
return;
|
|
|
|
}
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2010-02-22 02:51:25 +01:00
|
|
|
// if the error happened during initialization, try again now
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_connections_initialized && valid_metadata()) init();
|
2008-11-19 01:46:48 +01:00
|
|
|
if (!checking_files && should_check_files())
|
2014-07-06 21:18:00 +02:00
|
|
|
start_checking();
|
|
|
|
}
|
2016-12-22 16:42:33 +01:00
|
|
|
std::string torrent::resolve_filename(file_index_t const file) const
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2015-11-03 06:12:30 +01:00
|
|
|
if (file == torrent_status::error_file_none) return "";
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2015-11-03 06:12:30 +01:00
|
|
|
if (file == torrent_status::error_file_url) return m_url;
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2015-11-03 06:12:30 +01:00
|
|
|
if (file == torrent_status::error_file_ssl_ctx) return "SSL Context";
|
2016-10-23 04:00:47 +02:00
|
|
|
if (file == torrent_status::error_file_exception) return "exception";
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
if (m_storage && file >= file_index_t(0))
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
file_storage const& st = m_torrent_file->files();
|
|
|
|
return combine_path(m_save_path, st.file_path(file));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return m_save_path;
|
|
|
|
}
|
2008-07-12 19:00:52 +02:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::set_error(error_code const& ec, file_index_t const error_file)
|
2009-01-27 10:18:51 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2009-02-23 02:21:19 +01:00
|
|
|
m_error = ec;
|
|
|
|
m_error_file = error_file;
|
2010-08-18 19:14:40 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
|
2011-01-22 20:06:43 +01:00
|
|
|
if (alerts().should_post<torrent_error_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_error_alert>(get_handle(), ec
|
|
|
|
, resolve_filename(error_file));
|
2011-01-22 20:06:43 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2010-08-18 19:14:40 +02:00
|
|
|
if (ec)
|
|
|
|
{
|
|
|
|
char buf[1024];
|
2016-05-17 15:24:06 +02:00
|
|
|
std::snprintf(buf, sizeof(buf), "error %s: %s", ec.message().c_str()
|
2014-07-06 21:18:00 +02:00
|
|
|
, resolve_filename(error_file).c_str());
|
2010-08-18 19:14:40 +02:00
|
|
|
log_to_all_peers(buf);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2015-05-25 23:46:42 +02:00
|
|
|
update_state_list();
|
2009-01-27 10:18:51 +01:00
|
|
|
}
|
|
|
|
|
2008-04-24 05:28:48 +02:00
|
|
|
void torrent::auto_managed(bool a)
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-04-24 05:28:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
if (m_auto_managed == a) return;
|
2016-06-05 03:34:00 +02:00
|
|
|
bool const checking_files = should_check_files();
|
2008-04-24 05:28:48 +02:00
|
|
|
m_auto_managed = a;
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
update_want_scrape();
|
2015-05-25 23:46:42 +02:00
|
|
|
update_state_list();
|
2011-06-05 22:48:00 +02:00
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
|
|
|
|
2011-06-05 22:48:00 +02:00
|
|
|
// we need to save this new state as well
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2011-06-05 22:48:00 +02:00
|
|
|
|
2008-04-24 05:28:48 +02:00
|
|
|
// recalculate which torrents should be
|
|
|
|
// paused
|
2012-11-03 04:50:12 +01:00
|
|
|
m_ses.trigger_auto_manage();
|
2008-09-20 19:42:25 +02:00
|
|
|
|
|
|
|
if (!checking_files && should_check_files())
|
2009-02-08 02:29:09 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
start_checking();
|
2009-02-08 02:29:09 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2015-04-21 06:30:34 +02:00
|
|
|
namespace {
|
|
|
|
|
2016-11-25 17:17:25 +01:00
|
|
|
std::uint16_t clamped_subtract_u16(int a, int b)
|
2014-08-27 08:36:28 +02:00
|
|
|
{
|
|
|
|
if (a < b) return 0;
|
2016-11-25 17:17:25 +01:00
|
|
|
return std::uint16_t(a - b);
|
2014-08-27 08:36:28 +02:00
|
|
|
}
|
|
|
|
|
2016-11-25 17:17:25 +01:00
|
|
|
std::int16_t clamped_subtract_s16(int a, int b)
|
2015-09-20 17:59:00 +02:00
|
|
|
{
|
2016-06-18 20:01:38 +02:00
|
|
|
if (a + (std::numeric_limits<std::int16_t>::min)() < b)
|
|
|
|
return (std::numeric_limits<std::int16_t>::min)();
|
2016-11-25 17:17:25 +01:00
|
|
|
return std::int16_t(a - b);
|
2015-09-20 17:59:00 +02:00
|
|
|
}
|
|
|
|
|
2015-04-21 06:30:34 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2014-08-27 08:36:28 +02:00
|
|
|
// this is called every time the session timer takes a step back. Since the
|
|
|
|
// session time is meant to fit in 16 bits, it only covers a range of
|
|
|
|
// about 18 hours. This means every few hours the whole epoch of this
|
|
|
|
// clock is shifted forward. All timestamp in this clock must then be
|
|
|
|
// shifted backwards to remain the same. Anything that's shifted back
|
|
|
|
// beyond the new epoch is clamped to 0 (to represent the oldest timestamp
|
|
|
|
// currently representable by the session_time)
|
2016-11-25 17:17:25 +01:00
|
|
|
void torrent::step_session_time(int const seconds)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2014-10-26 08:34:31 +01:00
|
|
|
if (m_peer_list)
|
2008-09-20 19:42:25 +02:00
|
|
|
{
|
2016-06-01 07:05:17 +02:00
|
|
|
for (auto pe : *m_peer_list)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2014-08-27 08:36:28 +02:00
|
|
|
pe->last_optimistically_unchoked
|
2016-11-25 17:17:25 +01:00
|
|
|
= clamped_subtract_u16(pe->last_optimistically_unchoked, seconds);
|
|
|
|
pe->last_connected = clamped_subtract_u16(pe->last_connected, seconds);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2008-09-20 19:42:25 +02:00
|
|
|
}
|
2011-01-30 14:15:29 +01:00
|
|
|
|
2016-11-25 17:17:25 +01:00
|
|
|
// m_active_time, m_seeding_time and m_finished_time are absolute counters
|
2015-09-20 17:59:00 +02:00
|
|
|
// of the historical time we've spent in each state. The current time
|
|
|
|
// we've spent in those states (this session) is calculated by
|
|
|
|
// session_time() - m_started
|
|
|
|
// session_time() - m_became_seed
|
|
|
|
// session_time() - m_became_finished respectively. If any of the
|
|
|
|
// comparison points were pulled back to the oldest representable value (0)
|
|
|
|
// the left-over time must be transferred into the m_*_time counters.
|
|
|
|
|
|
|
|
if (m_started < seconds && !is_paused())
|
2014-08-27 08:36:28 +02:00
|
|
|
{
|
2016-04-24 22:43:45 +02:00
|
|
|
int const lost_seconds = seconds - m_started;
|
2015-09-20 17:59:00 +02:00
|
|
|
m_active_time += lost_seconds;
|
|
|
|
}
|
2016-11-25 17:17:25 +01:00
|
|
|
m_started = clamped_subtract_u16(m_started, seconds);
|
2014-08-27 08:36:28 +02:00
|
|
|
|
2015-09-20 17:59:00 +02:00
|
|
|
if (m_became_seed < seconds && is_seed())
|
|
|
|
{
|
2016-04-24 22:43:45 +02:00
|
|
|
int const lost_seconds = seconds - m_became_seed;
|
2015-09-20 17:59:00 +02:00
|
|
|
m_seeding_time += lost_seconds;
|
|
|
|
}
|
2016-11-25 17:17:25 +01:00
|
|
|
m_became_seed = clamped_subtract_u16(m_became_seed, seconds);
|
2014-08-27 08:36:28 +02:00
|
|
|
|
2015-09-20 17:59:00 +02:00
|
|
|
if (m_finished_time < seconds && is_finished())
|
|
|
|
{
|
2016-04-24 22:43:45 +02:00
|
|
|
int const lost_seconds = seconds - m_became_finished;
|
2015-09-20 17:59:00 +02:00
|
|
|
m_finished_time += lost_seconds;
|
2014-08-27 08:36:28 +02:00
|
|
|
}
|
2016-11-25 17:17:25 +01:00
|
|
|
m_became_finished = clamped_subtract_u16(m_became_finished, seconds);
|
2014-08-27 08:36:28 +02:00
|
|
|
|
2015-09-20 17:59:00 +02:00
|
|
|
m_last_upload = clamped_subtract_s16(m_last_upload, seconds);
|
|
|
|
m_last_download = clamped_subtract_s16(m_last_download, seconds);
|
2016-11-10 23:08:32 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2015-09-20 17:59:00 +02:00
|
|
|
m_last_scrape = clamped_subtract_s16(m_last_scrape, seconds);
|
2016-11-10 23:08:32 +01:00
|
|
|
#endif
|
2014-08-27 08:36:28 +02:00
|
|
|
|
2016-11-25 17:17:25 +01:00
|
|
|
m_last_saved_resume = clamped_subtract_u16(m_last_saved_resume, seconds);
|
|
|
|
m_upload_mode_time = clamped_subtract_u16(m_upload_mode_time, seconds);
|
2008-04-24 05:28:48 +02:00
|
|
|
}
|
|
|
|
|
2008-05-06 20:03:41 +02:00
|
|
|
// the higher seed rank, the more important to seed
|
2014-07-06 21:18:00 +02:00
|
|
|
int torrent::seed_rank(aux::session_settings const& s) const
|
2008-04-24 05:28:48 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-05-06 20:03:41 +02:00
|
|
|
enum flags
|
|
|
|
{
|
2012-01-23 02:57:40 +01:00
|
|
|
seed_ratio_not_met = 0x40000000,
|
2015-09-19 23:49:01 +02:00
|
|
|
no_seeds = 0x20000000,
|
|
|
|
recently_started = 0x10000000,
|
|
|
|
prio_mask = 0x0fffffff
|
2008-05-06 20:03:41 +02:00
|
|
|
};
|
2008-04-24 05:28:48 +02:00
|
|
|
|
2008-10-19 10:05:36 +02:00
|
|
|
if (!is_finished()) return 0;
|
|
|
|
|
2012-01-23 02:57:40 +01:00
|
|
|
int scale = 1000;
|
|
|
|
if (!is_seed()) scale = 500;
|
2008-04-24 05:28:48 +02:00
|
|
|
|
2008-05-06 20:03:41 +02:00
|
|
|
int ret = 0;
|
2008-04-24 05:28:48 +02:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t const fin_time = finished_time();
|
|
|
|
std::int64_t const download_time = int(active_time()) - fin_time;
|
2008-05-06 20:03:41 +02:00
|
|
|
|
|
|
|
// if we haven't yet met the seed limits, set the seed_ratio_not_met
|
|
|
|
// flag. That will make this seed prioritized
|
2009-01-19 11:05:48 +01:00
|
|
|
// downloaded may be 0 if the torrent is 0-sized
|
2016-09-02 14:27:38 +02:00
|
|
|
std::int64_t const downloaded = (std::max)(m_total_downloaded, m_torrent_file->total_size());
|
2014-08-27 08:36:28 +02:00
|
|
|
if (fin_time < s.get_int(settings_pack::seed_time_limit)
|
2014-07-06 21:18:00 +02:00
|
|
|
&& (download_time > 1
|
2014-08-27 08:36:28 +02:00
|
|
|
&& fin_time * 100 / download_time < s.get_int(settings_pack::seed_time_ratio_limit))
|
2009-01-19 11:05:48 +01:00
|
|
|
&& downloaded > 0
|
2014-07-06 21:18:00 +02:00
|
|
|
&& m_total_uploaded * 100 / downloaded < s.get_int(settings_pack::share_ratio_limit))
|
2008-05-06 20:03:41 +02:00
|
|
|
ret |= seed_ratio_not_met;
|
|
|
|
|
|
|
|
// if this torrent is running, and it was started less
|
|
|
|
// than 30 minutes ago, give it priority, to avoid oscillation
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!is_paused() && (m_ses.session_time() - m_started) < 30 * 60)
|
2008-05-06 20:03:41 +02:00
|
|
|
ret |= recently_started;
|
|
|
|
|
|
|
|
// if we have any scrape data, use it to calculate
|
|
|
|
// seed rank
|
|
|
|
int seeds = 0;
|
|
|
|
int downloaders = 0;
|
|
|
|
|
2010-09-06 06:02:15 +02:00
|
|
|
if (m_complete != 0xffffff) seeds = m_complete;
|
2014-10-26 08:34:31 +01:00
|
|
|
else seeds = m_peer_list ? m_peer_list->num_seeds() : 0;
|
2008-05-06 20:03:41 +02:00
|
|
|
|
2013-02-04 08:17:31 +01:00
|
|
|
if (m_incomplete != 0xffffff) downloaders = m_incomplete;
|
2014-10-26 08:34:31 +01:00
|
|
|
else downloaders = m_peer_list ? m_peer_list->num_peers() - m_peer_list->num_seeds() : 0;
|
2008-05-06 20:03:41 +02:00
|
|
|
|
|
|
|
if (seeds == 0)
|
|
|
|
{
|
|
|
|
ret |= no_seeds;
|
|
|
|
ret |= downloaders & prio_mask;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-01-23 02:57:40 +01:00
|
|
|
ret |= ((1 + downloaders) * scale / seeds) & prio_mask;
|
2008-05-06 20:03:41 +02:00
|
|
|
}
|
2008-04-24 05:28:48 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-02-20 06:48:17 +01:00
|
|
|
// this is an async operation triggered by the client
|
2014-07-06 21:18:00 +02:00
|
|
|
// TODO: add a flag to ignore stats, and only care about resume data for
|
|
|
|
// content. For unchanged files, don't trigger a load of the metadata
|
|
|
|
// just to save an empty resume data file
|
2010-10-29 04:21:43 +02:00
|
|
|
void torrent::save_resume_data(int flags)
|
2008-04-13 20:54:36 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-04-13 20:54:36 +02:00
|
|
|
INVARIANT_CHECK;
|
2015-07-06 01:32:13 +02:00
|
|
|
|
2011-03-04 07:17:55 +01:00
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
|
|
|
|
, errors::no_metadata);
|
2011-03-04 07:17:55 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if ((flags & torrent_handle::only_if_modified) && !m_need_save_resume_data)
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
|
|
|
|
, errors::resume_data_not_modified);
|
2014-07-06 21:18:00 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-04-12 02:36:31 +02:00
|
|
|
m_need_save_resume_data = false;
|
2014-07-06 21:18:00 +02:00
|
|
|
m_last_saved_resume = m_ses.session_time();
|
2016-06-18 20:01:38 +02:00
|
|
|
m_save_resume_flags = std::uint8_t(flags);
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2010-04-12 02:36:31 +02:00
|
|
|
|
2016-12-31 18:35:10 +01:00
|
|
|
if ((flags & torrent_handle::flush_disk_cache) && m_storage)
|
|
|
|
m_ses.disk_thread().async_release_files(m_storage);
|
2015-12-06 18:45:20 +01:00
|
|
|
|
|
|
|
state_updated();
|
|
|
|
|
2016-11-17 20:34:49 +01:00
|
|
|
auto rd = std::make_shared<entry>();
|
2015-12-06 18:45:20 +01:00
|
|
|
write_resume_data(*rd);
|
|
|
|
alerts().emplace_alert<save_resume_data_alert>(rd, get_handle());
|
2008-04-13 20:54:36 +02:00
|
|
|
}
|
2015-05-26 20:39:49 +02:00
|
|
|
|
2008-09-20 19:42:25 +02:00
|
|
|
bool torrent::should_check_files() const
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
return m_state == torrent_status::checking_files
|
2016-06-01 07:05:32 +02:00
|
|
|
&& !m_paused
|
2009-02-23 02:21:19 +01:00
|
|
|
&& !has_error()
|
2010-10-30 10:36:18 +02:00
|
|
|
&& !m_abort
|
2016-06-03 13:32:48 +02:00
|
|
|
&& !m_session_paused;
|
2008-09-20 19:42:25 +02:00
|
|
|
}
|
|
|
|
|
2010-01-09 22:17:52 +01:00
|
|
|
void torrent::flush_cache()
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2013-08-12 02:51:49 +02:00
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
// storage may be nullptr during shutdown
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_storage)
|
2013-08-12 02:51:49 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_abort);
|
|
|
|
return;
|
|
|
|
}
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_release_files(m_storage
|
2016-11-22 07:48:14 +01:00
|
|
|
, std::bind(&torrent::on_cache_flushed, shared_from_this()));
|
2010-01-09 22:17:52 +01:00
|
|
|
}
|
|
|
|
|
2016-11-22 07:48:14 +01:00
|
|
|
void torrent::on_cache_flushed() try
|
2010-01-09 22:17:52 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2011-04-19 10:21:09 +02:00
|
|
|
|
|
|
|
if (m_ses.is_aborted()) return;
|
|
|
|
|
2010-01-09 22:17:52 +01:00
|
|
|
if (alerts().should_post<cache_flushed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<cache_flushed_alert>(get_handle());
|
2010-01-09 22:17:52 +01:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2010-01-09 22:17:52 +01:00
|
|
|
|
2008-06-29 21:08:30 +02:00
|
|
|
bool torrent::is_paused() const
|
|
|
|
{
|
2016-06-05 03:34:00 +02:00
|
|
|
return m_paused || m_session_paused;
|
2008-06-29 21:08:30 +02:00
|
|
|
}
|
|
|
|
|
2010-10-30 10:36:18 +02:00
|
|
|
void torrent::pause(bool graceful)
|
2004-03-21 03:03:37 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-06-01 07:05:32 +02:00
|
|
|
if (!m_paused)
|
2015-12-10 07:56:33 +01:00
|
|
|
{
|
|
|
|
// we need to save this new state
|
|
|
|
set_need_save_resume();
|
|
|
|
}
|
2011-06-05 22:48:00 +02:00
|
|
|
|
2016-03-07 03:42:18 +01:00
|
|
|
int const flags = graceful ? flag_graceful_pause : 0;
|
2016-06-01 07:05:32 +02:00
|
|
|
set_paused(true, flags | flag_clear_disk_cache);
|
2008-06-29 21:08:30 +02:00
|
|
|
}
|
|
|
|
|
2016-03-07 03:42:18 +01:00
|
|
|
void torrent::do_pause(bool const clear_disk_cache)
|
2008-06-29 21:08:30 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-06-29 21:08:30 +02:00
|
|
|
if (!is_paused()) return;
|
2006-11-14 01:08:16 +01:00
|
|
|
|
2016-02-23 02:07:29 +01:00
|
|
|
// this torrent may be about to consider itself inactive. If so, we want
|
|
|
|
// to prevent it from doing so, since it's being paused unconditionally
|
|
|
|
// now. An illustrative example of this is a torrent that completes
|
|
|
|
// downloading when active_seeds = 0. It completes, it gets paused and it
|
|
|
|
// should not come back to life again.
|
|
|
|
if (m_pending_active_change)
|
|
|
|
{
|
|
|
|
m_inactivity_timer.cancel();
|
|
|
|
}
|
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-05-08 00:46:42 +02:00
|
|
|
for (auto& ext : m_extensions)
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
if (ext->on_pause()) return;
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-08-27 08:36:28 +02:00
|
|
|
m_need_connect_boost = true;
|
2014-05-19 00:17:51 +02:00
|
|
|
m_inactive = false;
|
|
|
|
|
2015-05-26 20:39:49 +02:00
|
|
|
update_state_list();
|
|
|
|
update_want_tick();
|
|
|
|
|
2014-08-27 08:36:28 +02:00
|
|
|
m_active_time += m_ses.session_time() - m_started;
|
|
|
|
|
|
|
|
if (is_seed())
|
|
|
|
m_seeding_time += m_ses.session_time() - m_became_seed;
|
|
|
|
|
|
|
|
if (is_finished())
|
|
|
|
m_finished_time += m_ses.session_time() - m_became_finished;
|
|
|
|
|
2016-06-03 13:32:48 +02:00
|
|
|
m_announce_to_dht = false;
|
|
|
|
m_announce_to_trackers = false;
|
|
|
|
m_announce_to_lsd = false;
|
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
|
|
|
update_want_scrape();
|
2016-06-03 13:32:48 +02:00
|
|
|
update_gauge();
|
|
|
|
update_state_list();
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-03 04:53:54 +02:00
|
|
|
log_to_all_peers("pausing");
|
2007-08-16 14:41:46 +02:00
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// when checking and being paused in graceful pause mode, we
|
|
|
|
// post the paused alert when the last outstanding disk job completes
|
|
|
|
if (m_state == torrent_status::checking_files)
|
2008-03-30 17:44:31 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_checking_piece == m_num_checked_pieces)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_paused_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_paused_alert>(get_handle());
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2015-02-15 06:17:09 +01:00
|
|
|
disconnect_all(errors::torrent_paused, op_bittorrent);
|
2014-07-06 21:18:00 +02:00
|
|
|
return;
|
2008-03-30 17:44:31 +02:00
|
|
|
}
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2010-10-30 10:36:18 +02:00
|
|
|
if (!m_graceful_pause_mode)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// this will make the storage close all
|
|
|
|
// files and flush all cached data
|
2016-12-31 18:35:10 +01:00
|
|
|
if (m_storage && clear_disk_cache)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-06-03 13:32:48 +02:00
|
|
|
// the torrent_paused alert will be posted from on_torrent_paused
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_stop_torrent(m_storage
|
2016-11-23 07:43:57 +01:00
|
|
|
, std::bind(&torrent::on_torrent_paused, shared_from_this()));
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (alerts().should_post<torrent_paused_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_paused_alert>(get_handle());
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2015-02-15 06:17:09 +01:00
|
|
|
disconnect_all(errors::torrent_paused, op_bittorrent);
|
2010-10-30 10:36:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// disconnect all peers with no outstanding data to receive
|
|
|
|
// and choke all remaining peers to prevent responding to new
|
|
|
|
// requests
|
2015-12-10 07:56:33 +01:00
|
|
|
std::vector<peer_connection*> to_disconnect;
|
2016-06-03 13:32:48 +02:00
|
|
|
for (peer_connection* p : m_connections)
|
2010-10-30 10:36:18 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
|
|
|
|
|
2015-12-10 07:56:33 +01:00
|
|
|
if (p->is_disconnecting()) continue;
|
2010-10-30 10:36:18 +02:00
|
|
|
|
|
|
|
if (p->outstanding_bytes() > 0)
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-03 04:53:54 +02:00
|
|
|
p->peer_log(peer_log_alert::info, "CHOKING_PEER", "torrent graceful paused");
|
2010-10-30 10:36:18 +02:00
|
|
|
#endif
|
|
|
|
// remove any un-sent requests from the queue
|
|
|
|
p->clear_request_queue();
|
|
|
|
// don't accept new requests from the peer
|
2014-09-22 05:47:43 +02:00
|
|
|
p->choke_this_peer();
|
2010-10-30 10:36:18 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-12-10 07:56:33 +01:00
|
|
|
to_disconnect.push_back(p);
|
|
|
|
}
|
|
|
|
|
2016-06-03 13:32:48 +02:00
|
|
|
for (peer_connection* p : to_disconnect)
|
|
|
|
{
|
2015-12-10 07:56:33 +01:00
|
|
|
// since we're currently in graceful pause mode, the last peer to
|
|
|
|
// disconnect (assuming all peers end up begin disconnected here)
|
|
|
|
// will post the torrent_paused_alert
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-05-03 04:53:54 +02:00
|
|
|
p->peer_log(peer_log_alert::info, "CLOSING_CONNECTION", "torrent_paused");
|
2010-10-30 10:36:18 +02:00
|
|
|
#endif
|
2015-02-15 06:17:09 +01:00
|
|
|
p->disconnect(errors::torrent_paused, op_bittorrent);
|
2010-10-30 10:36:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
stop_announcing();
|
2004-03-21 03:03:37 +01:00
|
|
|
}
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2010-08-18 19:14:40 +02:00
|
|
|
void torrent::log_to_all_peers(char const* message)
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-09-13 23:07:22 +02:00
|
|
|
|
|
|
|
auto it = m_connections.begin();
|
|
|
|
bool log_peers = it != m_connections.end()
|
|
|
|
&& (*it)->should_log(peer_log_alert::info);
|
|
|
|
|
|
|
|
if (log_peers)
|
2010-08-18 19:14:40 +02:00
|
|
|
{
|
2016-09-13 23:07:22 +02:00
|
|
|
for (auto const& c : m_connections)
|
|
|
|
{
|
|
|
|
c->peer_log(peer_log_alert::info, "TORRENT", "%s", message);
|
|
|
|
}
|
2010-08-18 19:14:40 +02:00
|
|
|
}
|
2010-09-22 18:59:28 +02:00
|
|
|
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("%s", message);
|
2010-08-18 19:14:40 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-10-06 19:51:59 +02:00
|
|
|
// add or remove a url that will be attempted for
|
|
|
|
// finding the file(s) in this torrent.
|
2016-10-03 08:08:03 +02:00
|
|
|
web_seed_t* torrent::add_web_seed(std::string const& url
|
2016-05-22 01:05:42 +02:00
|
|
|
, web_seed_entry::type_t type
|
|
|
|
, std::string const& auth
|
2016-10-03 08:08:03 +02:00
|
|
|
, web_seed_entry::headers_t const& extra_headers
|
|
|
|
, bool const ephemeral)
|
2012-10-06 19:51:59 +02:00
|
|
|
{
|
2014-12-31 23:05:34 +01:00
|
|
|
web_seed_t ent(url, type, auth, extra_headers);
|
2016-10-03 08:08:03 +02:00
|
|
|
ent.ephemeral = ephemeral;
|
|
|
|
|
2012-10-06 19:51:59 +02:00
|
|
|
// don't add duplicates
|
2016-10-03 08:08:03 +02:00
|
|
|
auto it = std::find(m_web_seeds.begin(), m_web_seeds.end(), ent);
|
|
|
|
if (it != m_web_seeds.end()) return &*it;
|
2012-10-06 19:51:59 +02:00
|
|
|
m_web_seeds.push_back(ent);
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2016-10-03 08:08:03 +02:00
|
|
|
return &m_web_seeds.back();
|
2012-10-06 19:51:59 +02:00
|
|
|
}
|
2015-06-10 07:22:01 +02:00
|
|
|
|
2016-06-03 13:32:48 +02:00
|
|
|
void torrent::set_session_paused(bool const b)
|
|
|
|
{
|
|
|
|
if (m_session_paused == b) return;
|
|
|
|
bool const paused_before = is_paused();
|
|
|
|
m_session_paused = b;
|
|
|
|
|
|
|
|
if (paused_before == is_paused()) return;
|
|
|
|
|
|
|
|
if (b) do_pause();
|
|
|
|
else do_resume();
|
|
|
|
}
|
|
|
|
|
2016-06-01 07:05:32 +02:00
|
|
|
void torrent::set_paused(bool b, int flags)
|
2010-03-31 04:40:00 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2010-10-30 10:36:18 +02:00
|
|
|
|
2015-12-10 07:56:33 +01:00
|
|
|
// if there are no peers, there is no point in a graceful pause mode. In
|
|
|
|
// fact, the promise to post the torrent_paused_alert exactly once is
|
|
|
|
// maintained by the last peer to be disconnected in graceful pause mode,
|
|
|
|
// if there are no peers, we must not enter graceful pause mode, and post
|
|
|
|
// the torrent_paused_alert immediately instead.
|
|
|
|
if (m_connections.empty())
|
2016-03-07 03:42:18 +01:00
|
|
|
flags &= ~flag_graceful_pause;
|
2015-12-10 07:56:33 +01:00
|
|
|
|
2016-06-01 07:05:32 +02:00
|
|
|
if (m_paused == b)
|
2015-09-20 03:06:56 +02:00
|
|
|
{
|
|
|
|
// there is one special case here. If we are
|
|
|
|
// currently in graceful pause mode, and we just turned into regular
|
|
|
|
// paused mode, we need to actually pause the torrent properly
|
2016-06-01 07:05:32 +02:00
|
|
|
if (m_paused == true
|
2015-09-20 03:06:56 +02:00
|
|
|
&& m_graceful_pause_mode == true
|
2016-03-07 03:42:18 +01:00
|
|
|
&& (flags & flag_graceful_pause) == 0)
|
2015-09-20 03:06:56 +02:00
|
|
|
{
|
2016-03-07 03:42:18 +01:00
|
|
|
m_graceful_pause_mode = false;
|
2015-11-28 20:06:40 +01:00
|
|
|
update_gauge();
|
2015-09-20 03:06:56 +02:00
|
|
|
do_pause();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2010-03-31 04:40:00 +02:00
|
|
|
|
2016-06-03 13:32:48 +02:00
|
|
|
bool const paused_before = is_paused();
|
2010-03-31 04:40:00 +02:00
|
|
|
|
2016-06-03 13:32:48 +02:00
|
|
|
m_paused = b;
|
2015-12-10 07:56:33 +01:00
|
|
|
|
2016-06-05 03:34:00 +02:00
|
|
|
// the session may still be paused, in which case
|
|
|
|
// the effective state of the torrent did not change
|
2016-06-03 13:32:48 +02:00
|
|
|
if (paused_before == is_paused()) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-06-03 13:32:48 +02:00
|
|
|
m_graceful_pause_mode = (flags & flag_graceful_pause) ? true : false;
|
2015-12-10 07:56:33 +01:00
|
|
|
|
2016-06-03 13:32:48 +02:00
|
|
|
if (b) do_pause((flags & flag_clear_disk_cache) != 0);
|
|
|
|
else do_resume();
|
2010-03-31 04:40:00 +02:00
|
|
|
}
|
|
|
|
|
2004-03-21 03:03:37 +01:00
|
|
|
void torrent::resume()
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-06-01 07:05:32 +02:00
|
|
|
if (!m_paused
|
2010-03-29 02:34:04 +02:00
|
|
|
&& m_announce_to_dht
|
|
|
|
&& m_announce_to_trackers
|
|
|
|
&& m_announce_to_lsd) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2010-03-31 04:40:00 +02:00
|
|
|
m_announce_to_dht = true;
|
|
|
|
m_announce_to_trackers = true;
|
|
|
|
m_announce_to_lsd = true;
|
2016-06-01 07:05:32 +02:00
|
|
|
m_paused = false;
|
2016-06-03 13:32:48 +02:00
|
|
|
if (!m_session_paused) m_graceful_pause_mode = false;
|
2011-06-05 22:48:00 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
|
2011-06-05 22:48:00 +02:00
|
|
|
// we need to save this new state
|
2015-11-30 02:31:57 +01:00
|
|
|
set_need_save_resume();
|
2011-06-05 22:48:00 +02:00
|
|
|
|
2008-06-29 21:08:30 +02:00
|
|
|
do_resume();
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::do_resume()
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-05-23 04:58:11 +02:00
|
|
|
if (is_paused())
|
|
|
|
{
|
|
|
|
update_want_tick();
|
|
|
|
return;
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-05-08 00:46:42 +02:00
|
|
|
for (auto& ext : m_extensions)
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
if (ext->on_resume()) return;
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
if (alerts().should_post<torrent_resumed_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_resumed_alert>(get_handle());
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_started = m_ses.session_time();
|
2014-08-27 08:36:28 +02:00
|
|
|
if (is_seed()) m_became_seed = m_started;
|
|
|
|
if (is_finished()) m_became_finished = m_started;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
clear_error();
|
|
|
|
|
2015-12-12 21:08:38 +01:00
|
|
|
if (m_state == torrent_status::checking_files)
|
|
|
|
{
|
|
|
|
if (m_auto_managed) m_ses.trigger_auto_manage();
|
|
|
|
if (should_check_files()) start_checking();
|
|
|
|
}
|
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
|
|
|
update_want_tick();
|
|
|
|
update_want_scrape();
|
2016-06-03 13:32:48 +02:00
|
|
|
update_gauge();
|
|
|
|
|
|
|
|
if (should_check_files()) start_checking();
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2015-12-12 21:08:38 +01:00
|
|
|
if (m_state == torrent_status::checking_files) return;
|
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
start_announcing();
|
2014-08-27 08:36:28 +02:00
|
|
|
|
|
|
|
do_connect_boost();
|
2008-07-12 10:25:19 +02:00
|
|
|
}
|
2004-07-24 13:54:17 +02:00
|
|
|
|
2016-12-15 20:20:08 +01:00
|
|
|
void torrent::update_tracker_timer(time_point const now)
|
2008-07-12 10:25:19 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2011-08-18 01:01:35 +02:00
|
|
|
if (!m_announcing)
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2011-08-18 01:01:35 +02:00
|
|
|
debug_log("*** update tracker timer: not announcing");
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2008-07-12 10:25:19 +02:00
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point next_announce = max_time();
|
2008-11-29 09:38:40 +01:00
|
|
|
int tier = INT_MAX;
|
2009-06-28 22:21:55 +02:00
|
|
|
|
|
|
|
bool found_working = false;
|
|
|
|
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const& t : m_trackers)
|
2008-11-29 09:38:40 +01:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("*** tracker: \"%s\" "
|
|
|
|
"[ tiers: %d trackers: %d"
|
|
|
|
" found: %d i->tier: %d tier: %d"
|
|
|
|
" working: %d fails: %d limit: %d upd: %d ]"
|
2016-12-15 20:20:08 +01:00
|
|
|
, t.url.c_str(), settings().get_bool(settings_pack::announce_to_all_tiers)
|
2016-09-14 04:46:07 +02:00
|
|
|
, settings().get_bool(settings_pack::announce_to_all_trackers), found_working
|
2016-12-15 20:20:08 +01:00
|
|
|
, t.tier, tier, t.is_working(), t.fails, t.fail_limit
|
|
|
|
, t.updating);
|
2016-09-14 04:46:07 +02:00
|
|
|
}
|
2011-08-18 01:01:35 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
if (settings().get_bool(settings_pack::announce_to_all_tiers)
|
2009-06-28 22:21:55 +02:00
|
|
|
&& found_working
|
2016-12-15 20:20:08 +01:00
|
|
|
&& t.tier <= tier
|
2009-06-28 22:21:55 +02:00
|
|
|
&& tier != INT_MAX)
|
|
|
|
continue;
|
|
|
|
|
2016-12-15 20:20:08 +01:00
|
|
|
if (t.tier > tier && !settings().get_bool(settings_pack::announce_to_all_tiers)) break;
|
|
|
|
if (t.is_working()) { tier = t.tier; found_working = false; }
|
|
|
|
if (t.fails >= t.fail_limit && t.fail_limit != 0) continue;
|
|
|
|
if (t.updating)
|
2011-09-12 07:21:16 +02:00
|
|
|
{
|
|
|
|
found_working = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
time_point next_tracker_announce = (std::max)(t.next_announce, t.min_announce);
|
2011-09-12 07:21:16 +02:00
|
|
|
if (next_tracker_announce < next_announce
|
2016-12-15 20:20:08 +01:00
|
|
|
&& (!found_working || t.is_working()))
|
2011-09-12 07:21:16 +02:00
|
|
|
next_announce = next_tracker_announce;
|
|
|
|
}
|
2016-12-15 20:20:08 +01:00
|
|
|
if (t.is_working()) found_working = true;
|
2011-09-12 07:21:16 +02:00
|
|
|
if (found_working
|
2014-07-06 21:18:00 +02:00
|
|
|
&& !settings().get_bool(settings_pack::announce_to_all_trackers)
|
|
|
|
&& !settings().get_bool(settings_pack::announce_to_all_tiers)) break;
|
2008-11-29 09:38:40 +01:00
|
|
|
}
|
2010-05-03 10:24:30 +02:00
|
|
|
|
2015-11-27 20:04:46 +01:00
|
|
|
if (next_announce <= now) next_announce = now;
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2015-08-16 18:17:23 +02:00
|
|
|
debug_log("*** update tracker timer: next_announce < now %d"
|
2011-08-18 01:01:35 +02:00
|
|
|
" m_waiting_tracker: %d next_announce_in: %d"
|
2014-10-04 21:54:12 +02:00
|
|
|
, next_announce <= now, m_waiting_tracker
|
|
|
|
, int(total_seconds(now - next_announce)));
|
2011-08-18 01:01:35 +02:00
|
|
|
#endif
|
2010-07-15 07:16:06 +02:00
|
|
|
|
2013-03-18 06:46:08 +01:00
|
|
|
// don't re-issue the timer if it's the same expiration time as last time
|
2016-10-06 06:08:14 +02:00
|
|
|
// if m_waiting_tracker is 0, expires_at() is undefined
|
2013-03-18 06:46:08 +01:00
|
|
|
if (m_waiting_tracker && m_tracker_timer.expires_at() == next_announce) return;
|
|
|
|
|
2010-07-15 07:16:06 +02:00
|
|
|
error_code ec;
|
2016-10-06 06:08:14 +02:00
|
|
|
auto self = shared_from_this();
|
2010-07-15 07:16:06 +02:00
|
|
|
|
2008-11-29 09:38:40 +01:00
|
|
|
m_tracker_timer.expires_at(next_announce, ec);
|
2016-10-06 06:08:14 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("tracker::on_tracker_announce");
|
|
|
|
++m_waiting_tracker;
|
|
|
|
m_tracker_timer.async_wait([self](error_code const& e)
|
|
|
|
{ self->wrap(&torrent::on_tracker_announce, e); });
|
2008-07-12 10:25:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::start_announcing()
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2012-09-28 01:04:51 +02:00
|
|
|
if (is_paused())
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("start_announcing(), paused");
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2008-08-27 17:15:41 +02:00
|
|
|
// if we don't have metadata, we need to announce
|
|
|
|
// before checking files, to get peers to
|
|
|
|
// request the metadata from
|
2012-09-28 01:04:51 +02:00
|
|
|
if (!m_files_checked && valid_metadata())
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("start_announcing(), files not checked (with valid metadata)");
|
2013-05-20 10:05:39 +02:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2016-02-20 21:49:49 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
// deprecated in 1.2
|
2013-05-20 10:05:39 +02:00
|
|
|
if (!m_torrent_file->is_valid() && !m_url.empty())
|
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2013-05-20 10:05:39 +02:00
|
|
|
debug_log("start_announcing(), downloading URL");
|
2012-09-28 01:04:51 +02:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2016-02-20 21:49:49 +01:00
|
|
|
#endif
|
2008-07-12 10:25:19 +02:00
|
|
|
if (m_announcing) return;
|
|
|
|
|
|
|
|
m_announcing = true;
|
|
|
|
|
2012-08-03 07:13:40 +02:00
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
2014-10-26 08:34:31 +01:00
|
|
|
if ((!m_peer_list || m_peer_list->num_peers() < 50) && m_ses.dht())
|
2012-08-03 07:13:40 +02:00
|
|
|
{
|
|
|
|
// we don't have any peers, prioritize
|
|
|
|
// announcing this torrent with the DHT
|
2013-01-20 20:54:54 +01:00
|
|
|
m_ses.prioritize_dht(shared_from_this());
|
2012-08-03 07:13:40 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
if (!m_trackers.empty())
|
|
|
|
{
|
|
|
|
// tell the tracker that we're back
|
2008-11-29 09:38:40 +01:00
|
|
|
std::for_each(m_trackers.begin(), m_trackers.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&announce_entry::reset, _1));
|
2008-07-12 10:25:19 +02:00
|
|
|
}
|
|
|
|
|
2010-02-18 07:45:07 +01:00
|
|
|
// reset the stats, since from the tracker's
|
|
|
|
// point of view, this is a new session
|
|
|
|
m_total_failed_bytes = 0;
|
|
|
|
m_total_redundant_bytes = 0;
|
|
|
|
m_stat.clear();
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_tick();
|
|
|
|
|
2010-02-18 07:45:07 +01:00
|
|
|
announce_with_tracker();
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
lsd_announce();
|
2008-07-12 10:25:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::stop_announcing()
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-07-12 10:25:19 +02:00
|
|
|
if (!m_announcing) return;
|
|
|
|
|
|
|
|
error_code ec;
|
|
|
|
m_tracker_timer.cancel(ec);
|
2008-06-08 07:14:40 +02:00
|
|
|
|
2008-07-12 10:25:19 +02:00
|
|
|
m_announcing = false;
|
2004-07-24 13:54:17 +02:00
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point now = aux::time_now();
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto& t : m_trackers)
|
2009-12-02 05:05:24 +01:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
t.next_announce = now;
|
|
|
|
t.min_announce = now;
|
2009-12-02 05:05:24 +01:00
|
|
|
}
|
2008-11-29 09:38:40 +01:00
|
|
|
announce_with_tracker(tracker_request::stopped);
|
2004-03-21 03:03:37 +01:00
|
|
|
}
|
|
|
|
|
2014-08-27 08:36:28 +02:00
|
|
|
int torrent::finished_time() const
|
|
|
|
{
|
2015-09-19 23:49:01 +02:00
|
|
|
// m_finished_time does not account for the current "session", just the
|
|
|
|
// time before we last started this torrent. To get the current time, we
|
|
|
|
// need to add the time since we started it
|
2014-08-27 08:36:28 +02:00
|
|
|
return m_finished_time + ((!is_finished() || is_paused()) ? 0
|
|
|
|
: (m_ses.session_time() - m_became_finished));
|
|
|
|
}
|
|
|
|
|
|
|
|
int torrent::active_time() const
|
|
|
|
{
|
2015-09-19 23:49:01 +02:00
|
|
|
// m_active_time does not account for the current "session", just the
|
|
|
|
// time before we last started this torrent. To get the current time, we
|
|
|
|
// need to add the time since we started it
|
2014-08-27 08:36:28 +02:00
|
|
|
return m_active_time + (is_paused() ? 0
|
|
|
|
: m_ses.session_time() - m_started);
|
|
|
|
}
|
|
|
|
|
|
|
|
int torrent::seeding_time() const
|
|
|
|
{
|
2015-09-19 23:49:01 +02:00
|
|
|
// m_seeding_time does not account for the current "session", just the
|
|
|
|
// time before we last started this torrent. To get the current time, we
|
|
|
|
// need to add the time since we started it
|
2014-08-27 08:36:28 +02:00
|
|
|
return m_seeding_time + ((!is_seed() || is_paused()) ? 0
|
|
|
|
: m_ses.session_time() - m_became_seed);
|
|
|
|
}
|
|
|
|
|
2015-08-29 22:07:19 +02:00
|
|
|
void torrent::second_tick(int tick_interval_ms)
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(want_tick());
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-10-06 06:08:14 +02:00
|
|
|
auto self = shared_from_this();
|
2013-08-15 08:10:41 +02:00
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-08-31 14:27:36 +02:00
|
|
|
for (auto const& ext : m_extensions)
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
ext->tick();
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
2013-08-15 08:10:41 +02:00
|
|
|
|
|
|
|
if (m_abort) return;
|
2006-11-14 01:08:16 +01:00
|
|
|
#endif
|
|
|
|
|
2009-06-19 00:32:55 +02:00
|
|
|
// if we're in upload only mode and we're auto-managed
|
|
|
|
// leave upload mode every 10 minutes hoping that the error
|
|
|
|
// condition has been fixed
|
2014-08-27 08:36:28 +02:00
|
|
|
if (m_upload_mode && m_auto_managed
|
|
|
|
&& int(m_ses.session_time() - m_upload_mode_time)
|
2014-07-06 21:18:00 +02:00
|
|
|
>= settings().get_int(settings_pack::optimistic_disk_retry))
|
2009-06-19 00:32:55 +02:00
|
|
|
{
|
|
|
|
set_upload_mode(false);
|
|
|
|
}
|
|
|
|
|
2013-10-28 00:56:45 +01:00
|
|
|
if (is_paused() && !m_graceful_pause_mode)
|
2005-03-30 01:47:13 +02:00
|
|
|
{
|
|
|
|
// let the stats fade out to 0
|
2015-05-26 20:39:49 +02:00
|
|
|
m_stat.second_tick(tick_interval_ms);
|
2011-11-15 03:34:00 +01:00
|
|
|
// if the rate is 0, there's no update because of network transfers
|
|
|
|
if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0)
|
|
|
|
state_updated();
|
2014-07-06 21:18:00 +02:00
|
|
|
else
|
|
|
|
update_want_tick();
|
|
|
|
|
2005-03-30 01:47:13 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (settings().get_bool(settings_pack::rate_limit_ip_overhead))
|
2009-04-26 02:21:59 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
int up_limit = upload_limit();
|
|
|
|
int down_limit = download_limit();
|
2009-04-26 02:21:59 +02:00
|
|
|
|
|
|
|
if (down_limit > 0
|
|
|
|
&& m_stat.download_ip_overhead() >= down_limit
|
|
|
|
&& alerts().should_post<performance_alert>())
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<performance_alert>(get_handle()
|
|
|
|
, performance_alert::download_limit_too_low);
|
2009-04-26 02:21:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (up_limit > 0
|
|
|
|
&& m_stat.upload_ip_overhead() >= up_limit
|
|
|
|
&& alerts().should_post<performance_alert>())
|
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<performance_alert>(get_handle()
|
|
|
|
, performance_alert::upload_limit_too_low);
|
2009-04-26 02:21:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-17 10:34:44 +01:00
|
|
|
// ---- TIME CRITICAL PIECES ----
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 0
|
|
|
|
std::vector<partial_piece_info> queue;
|
|
|
|
get_download_queue(&queue);
|
|
|
|
|
|
|
|
std::vector<peer_info> peer_list;
|
|
|
|
get_peer_info(peer_list);
|
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
std::sort(queue.begin(), queue.end(), std::bind(&partial_piece_info::piece_index, _1)
|
|
|
|
< std::bind(&partial_piece_info::piece_index, _2));
|
2014-06-06 03:26:18 +02:00
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("average piece download time: %.2f s (+/- %.2f s)\n"
|
2014-06-06 03:26:18 +02:00
|
|
|
, m_average_piece_time / 1000.f
|
|
|
|
, m_piece_time_deviation / 1000.f);
|
|
|
|
for (std::vector<partial_piece_info>::iterator i = queue.begin()
|
|
|
|
, end(queue.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
extern void print_piece(libtorrent::partial_piece_info* pp
|
|
|
|
, std::vector<libtorrent::peer_info> const& peers
|
|
|
|
, std::vector<time_critical_piece> const& time_critical);
|
|
|
|
|
|
|
|
print_piece(&*i, peer_list, m_time_critical_pieces);
|
|
|
|
}
|
|
|
|
#endif // TORRENT_DEBUG_STREAMING
|
|
|
|
|
2014-06-06 17:27:44 +02:00
|
|
|
if (!m_time_critical_pieces.empty() && !upload_mode())
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
|
|
|
request_time_critical_pieces();
|
|
|
|
}
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
// ---- WEB SEEDS ----
|
|
|
|
|
2013-08-19 05:54:45 +02:00
|
|
|
maybe_connect_web_seeds();
|
2015-05-26 20:39:49 +02:00
|
|
|
|
2012-05-07 00:46:21 +02:00
|
|
|
m_swarm_last_seen_complete = m_last_seen_complete;
|
2014-07-06 21:18:00 +02:00
|
|
|
int idx = 0;
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto i = m_connections.begin(); i != m_connections.end(); ++idx)
|
2003-12-07 06:53:04 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// keep the peer object alive while we're
|
|
|
|
// inspecting it
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<peer_connection> p = (*i)->self();
|
2007-05-24 19:07:43 +02:00
|
|
|
++i;
|
2010-07-15 08:27:44 +02:00
|
|
|
|
2012-05-07 00:46:21 +02:00
|
|
|
// look for the peer that saw a seed most recently
|
|
|
|
m_swarm_last_seen_complete = (std::max)(p->last_seen_complete(), m_swarm_last_seen_complete);
|
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
// updates the peer connection's ul/dl bandwidth
|
|
|
|
// resource requests
|
2016-10-23 04:00:47 +02:00
|
|
|
p->second_tick(tick_interval_ms);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (p->is_disconnecting())
|
|
|
|
{
|
|
|
|
i = m_connections.begin() + idx;
|
|
|
|
--idx;
|
2007-05-24 19:07:43 +02:00
|
|
|
}
|
2003-12-07 06:53:04 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<stats_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<stats_alert>(get_handle(), tick_interval_ms, m_stat);
|
2010-01-02 15:16:35 +01:00
|
|
|
|
2008-04-16 08:31:05 +02:00
|
|
|
m_total_uploaded += m_stat.last_payload_uploaded();
|
|
|
|
m_total_downloaded += m_stat.last_payload_downloaded();
|
2009-07-19 06:59:27 +02:00
|
|
|
m_stat.second_tick(tick_interval_ms);
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2014-07-13 00:03:29 +02:00
|
|
|
// these counters are saved in the resume data, since they updated
|
|
|
|
// we need to save the resume data too
|
|
|
|
m_need_save_resume_data = true;
|
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
// if the rate is 0, there's no update because of network transfers
|
|
|
|
if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0)
|
|
|
|
state_updated();
|
2014-05-19 00:17:51 +02:00
|
|
|
|
|
|
|
// this section determines whether the torrent is active or not. When it
|
|
|
|
// changes state, it may also trigger the auto-manage logic to reconsider
|
|
|
|
// which torrents should be queued and started. There is a low pass
|
|
|
|
// filter in order to avoid flapping (auto_manage_startup).
|
2014-10-06 00:30:09 +02:00
|
|
|
bool is_inactive = is_inactive_internal();
|
2014-05-19 01:42:10 +02:00
|
|
|
|
2015-05-25 23:46:42 +02:00
|
|
|
if (settings().get_bool(settings_pack::dont_count_slow_torrents))
|
2014-05-19 00:17:51 +02:00
|
|
|
{
|
2016-10-06 06:08:14 +02:00
|
|
|
if (is_inactive != m_inactive && !m_pending_active_change)
|
2015-05-25 23:46:42 +02:00
|
|
|
{
|
2016-10-06 06:08:14 +02:00
|
|
|
int const delay = settings().get_int(settings_pack::auto_manage_startup);
|
2015-05-25 23:46:42 +02:00
|
|
|
m_inactivity_timer.expires_from_now(seconds(delay));
|
2016-10-06 06:08:14 +02:00
|
|
|
m_inactivity_timer.async_wait([self](error_code const& ec) {
|
|
|
|
self->wrap(&torrent::on_inactivity_tick, ec); });
|
2015-05-25 23:46:42 +02:00
|
|
|
m_pending_active_change = true;
|
|
|
|
}
|
|
|
|
else if (is_inactive == m_inactive
|
|
|
|
&& m_pending_active_change)
|
|
|
|
{
|
|
|
|
m_inactivity_timer.cancel();
|
|
|
|
}
|
2014-05-19 00:17:51 +02:00
|
|
|
}
|
2014-10-06 00:30:09 +02:00
|
|
|
|
|
|
|
update_want_tick();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool torrent::is_inactive_internal() const
|
|
|
|
{
|
|
|
|
if (is_finished())
|
|
|
|
return m_stat.upload_payload_rate()
|
2015-05-16 08:33:37 +02:00
|
|
|
< settings().get_int(settings_pack::inactive_up_rate);
|
2014-05-19 00:17:51 +02:00
|
|
|
else
|
2014-10-06 00:30:09 +02:00
|
|
|
return m_stat.download_payload_rate()
|
2015-05-16 08:33:37 +02:00
|
|
|
< settings().get_int(settings_pack::inactive_down_rate);
|
2014-10-06 00:30:09 +02:00
|
|
|
}
|
2014-05-19 00:17:51 +02:00
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
void torrent::on_inactivity_tick(error_code const& ec) try
|
2014-10-06 00:30:09 +02:00
|
|
|
{
|
2015-05-25 23:46:42 +02:00
|
|
|
m_pending_active_change = false;
|
|
|
|
|
2014-10-06 00:30:09 +02:00
|
|
|
if (ec) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2014-10-06 00:31:29 +02:00
|
|
|
bool is_inactive = is_inactive_internal();
|
|
|
|
if (is_inactive == m_inactive) return;
|
|
|
|
|
|
|
|
m_inactive = is_inactive;
|
|
|
|
|
2015-05-25 23:46:42 +02:00
|
|
|
update_state_list();
|
2015-05-26 20:39:49 +02:00
|
|
|
update_want_tick();
|
2015-05-25 23:46:42 +02:00
|
|
|
|
2015-05-16 08:33:37 +02:00
|
|
|
if (settings().get_bool(settings_pack::dont_count_slow_torrents))
|
2014-10-06 00:30:09 +02:00
|
|
|
m_ses.trigger_auto_manage();
|
2004-03-23 23:58:18 +01:00
|
|
|
}
|
2016-10-23 04:00:47 +02:00
|
|
|
catch (...) { handle_exception(); }
|
2004-03-23 23:58:18 +01:00
|
|
|
|
2013-08-19 05:54:45 +02:00
|
|
|
void torrent::maybe_connect_web_seeds()
|
|
|
|
{
|
2014-06-15 20:02:59 +02:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2013-08-19 05:54:45 +02:00
|
|
|
// if we have everything we want we don't need to connect to any web-seed
|
|
|
|
if (!is_finished() && !m_web_seeds.empty() && m_files_checked
|
|
|
|
&& int(m_connections.size()) < m_max_connections
|
2015-05-16 08:33:37 +02:00
|
|
|
&& m_ses.num_connections() < settings().get_int(settings_pack::connections_limit))
|
2013-08-19 05:54:45 +02:00
|
|
|
{
|
|
|
|
// keep trying web-seeds if there are any
|
|
|
|
// first find out which web seeds we are connected to
|
2014-12-31 23:05:34 +01:00
|
|
|
for (std::list<web_seed_t>::iterator i = m_web_seeds.begin();
|
2013-08-19 05:54:45 +02:00
|
|
|
i != m_web_seeds.end();)
|
|
|
|
{
|
2014-12-31 23:05:34 +01:00
|
|
|
std::list<web_seed_t>::iterator w = i++;
|
2013-08-19 05:54:45 +02:00
|
|
|
if (w->peer_info.connection) continue;
|
2015-03-12 05:34:54 +01:00
|
|
|
if (w->retry > aux::time_now()) continue;
|
2013-08-19 05:54:45 +02:00
|
|
|
if (w->resolving) continue;
|
2015-08-23 11:01:55 +02:00
|
|
|
if (w->removed) continue;
|
2013-08-19 05:54:45 +02:00
|
|
|
|
|
|
|
connect_to_url_seed(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-05 18:01:36 +02:00
|
|
|
void torrent::recalc_share_mode()
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(share_mode());
|
|
|
|
if (is_seed()) return;
|
|
|
|
|
|
|
|
int pieces_in_torrent = m_torrent_file->num_pieces();
|
|
|
|
int num_seeds = 0;
|
|
|
|
int num_peers = 0;
|
|
|
|
int num_downloaders = 0;
|
|
|
|
int missing_pieces = 0;
|
|
|
|
int num_interested = 0;
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const p : m_connections)
|
2010-09-05 18:01:36 +02:00
|
|
|
{
|
|
|
|
if (p->is_connecting()) continue;
|
2015-02-08 23:29:10 +01:00
|
|
|
if (p->is_disconnecting()) continue;
|
2010-09-05 18:01:36 +02:00
|
|
|
++num_peers;
|
|
|
|
if (p->is_seed())
|
|
|
|
{
|
|
|
|
++num_seeds;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->share_mode()) continue;
|
2015-02-08 23:29:10 +01:00
|
|
|
if (p->upload_only()) continue;
|
2010-09-05 18:01:36 +02:00
|
|
|
|
2016-12-15 20:20:08 +01:00
|
|
|
if (p->is_peer_interested()) ++num_interested;
|
2015-02-08 23:29:10 +01:00
|
|
|
|
2010-09-05 18:01:36 +02:00
|
|
|
++num_downloaders;
|
|
|
|
missing_pieces += pieces_in_torrent - p->num_have_pieces();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_peers == 0) return;
|
|
|
|
|
|
|
|
if (num_seeds * 100 / num_peers > 50
|
|
|
|
&& (num_peers * 100 / m_max_connections > 90
|
|
|
|
|| num_peers > 20))
|
|
|
|
{
|
|
|
|
// we are connected to more than 90% seeds (and we're beyond
|
|
|
|
// 90% of the max number of connections). That will
|
|
|
|
// limit our ability to upload. We need more downloaders.
|
|
|
|
// disconnect some seeds so that we don't have more than 50%
|
|
|
|
int to_disconnect = num_seeds - num_peers / 2;
|
|
|
|
std::vector<peer_connection*> seeds;
|
|
|
|
seeds.reserve(num_seeds);
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const p : m_connections)
|
2010-09-05 18:01:36 +02:00
|
|
|
{
|
|
|
|
if (p->is_seed()) seeds.push_back(p);
|
|
|
|
}
|
|
|
|
|
2016-08-15 01:48:31 +02:00
|
|
|
aux::random_shuffle(seeds.begin(), seeds.end());
|
2010-09-05 18:01:36 +02:00
|
|
|
TORRENT_ASSERT(to_disconnect <= int(seeds.size()));
|
|
|
|
for (int i = 0; i < to_disconnect; ++i)
|
2015-02-08 23:29:10 +01:00
|
|
|
seeds[i]->disconnect(errors::upload_upload_connection
|
2015-02-15 06:17:09 +01:00
|
|
|
, op_bittorrent);
|
2010-09-05 18:01:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (num_downloaders == 0) return;
|
|
|
|
|
|
|
|
// assume that the seeds are about as fast as us. During the time
|
|
|
|
// we can download one piece, and upload one piece, each seed
|
|
|
|
// can upload two pieces.
|
|
|
|
missing_pieces -= 2 * num_seeds;
|
|
|
|
|
|
|
|
if (missing_pieces <= 0) return;
|
2015-05-26 20:39:49 +02:00
|
|
|
|
2010-09-05 18:01:36 +02:00
|
|
|
// missing_pieces represents our opportunity to download pieces
|
|
|
|
// and share them more than once each
|
|
|
|
|
|
|
|
// now, download at least one piece, otherwise download one more
|
|
|
|
// piece if our downloaded (and downloading) pieces is less than 50%
|
|
|
|
// of the uploaded bytes
|
2016-12-22 16:42:33 +01:00
|
|
|
int num_downloaded_pieces = std::max(m_picker->num_have()
|
2010-09-05 18:01:36 +02:00
|
|
|
, pieces_in_torrent - m_picker->num_filtered());
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
if (std::int64_t(num_downloaded_pieces) * m_torrent_file->piece_length()
|
2014-07-06 21:18:00 +02:00
|
|
|
* settings().get_int(settings_pack::share_mode_target) > m_total_uploaded
|
2010-09-05 18:01:36 +02:00
|
|
|
&& num_downloaded_pieces > 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// don't have more pieces downloading in parallel than 5% of the total
|
|
|
|
// number of pieces we have downloaded
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_picker->get_download_queue_size() > num_downloaded_pieces / 20)
|
2010-09-05 18:01:36 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
// one more important property is that there are enough pieces
|
|
|
|
// that more than one peer wants to download
|
|
|
|
// make sure that there are enough downloaders for the rarest
|
|
|
|
// piece. Go through all pieces, figure out which one is the rarest
|
|
|
|
// and how many peers that has that piece
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
std::vector<piece_index_t> rarest_pieces;
|
2010-09-05 18:01:36 +02:00
|
|
|
|
|
|
|
int num_pieces = m_torrent_file->num_pieces();
|
|
|
|
int rarest_rarity = INT_MAX;
|
2016-12-22 16:42:33 +01:00
|
|
|
for (piece_index_t i(0); i < piece_index_t(num_pieces); ++i)
|
2010-09-05 18:01:36 +02:00
|
|
|
{
|
2015-02-08 23:15:59 +01:00
|
|
|
piece_picker::piece_stats_t ps = m_picker->piece_stats(i);
|
|
|
|
if (ps.peer_count == 0) continue;
|
|
|
|
if (ps.priority == 0 && (ps.have || ps.downloading))
|
2010-09-05 18:01:36 +02:00
|
|
|
{
|
|
|
|
m_picker->set_piece_priority(i, 1);
|
|
|
|
continue;
|
|
|
|
}
|
2015-02-08 23:15:59 +01:00
|
|
|
// don't count pieces we already have or are trying to download
|
|
|
|
if (ps.priority > 0 || ps.have) continue;
|
2016-12-15 20:20:08 +01:00
|
|
|
if (ps.peer_count > rarest_rarity) continue;
|
|
|
|
if (ps.peer_count == rarest_rarity)
|
2010-09-05 18:01:36 +02:00
|
|
|
{
|
|
|
|
rarest_pieces.push_back(i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
rarest_pieces.clear();
|
2015-02-08 23:15:59 +01:00
|
|
|
rarest_rarity = ps.peer_count;
|
2010-09-05 18:01:36 +02:00
|
|
|
rarest_pieces.push_back(i);
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
|
|
|
update_want_peers();
|
2010-09-05 18:01:36 +02:00
|
|
|
|
|
|
|
// now, rarest_pieces is a list of all pieces that are the rarest ones.
|
|
|
|
// and rarest_rarity is the number of peers that have the rarest pieces
|
|
|
|
|
|
|
|
// if there's only a single peer that doesn't have the rarest piece
|
|
|
|
// it's impossible for us to download one piece and upload it
|
|
|
|
// twice. i.e. we cannot get a positive share ratio
|
2015-02-08 23:15:59 +01:00
|
|
|
if (num_peers - rarest_rarity
|
|
|
|
< settings().get_int(settings_pack::share_mode_target))
|
|
|
|
return;
|
2010-09-05 18:01:36 +02:00
|
|
|
|
|
|
|
// now, pick one of the rarest pieces to download
|
2016-12-05 14:39:53 +01:00
|
|
|
int const pick = random(std::uint32_t(rarest_pieces.size() - 1));
|
2016-06-03 13:32:48 +02:00
|
|
|
bool const was_finished = is_finished();
|
2010-09-05 18:01:36 +02:00
|
|
|
m_picker->set_piece_priority(rarest_pieces[pick], 1);
|
2014-07-06 21:18:00 +02:00
|
|
|
update_gauge();
|
2010-09-05 18:01:36 +02:00
|
|
|
update_peer_interest(was_finished);
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
2010-09-05 18:01:36 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::sent_bytes(int bytes_payload, int bytes_protocol)
|
2010-01-15 17:45:42 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
m_stat.sent_bytes(bytes_payload, bytes_protocol);
|
|
|
|
m_ses.sent_bytes(bytes_payload, bytes_protocol);
|
|
|
|
}
|
2010-01-15 17:45:42 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::received_bytes(int bytes_payload, int bytes_protocol)
|
|
|
|
{
|
|
|
|
m_stat.received_bytes(bytes_payload, bytes_protocol);
|
|
|
|
m_ses.received_bytes(bytes_payload, bytes_protocol);
|
|
|
|
}
|
2011-03-15 02:58:48 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::trancieve_ip_packet(int bytes, bool ipv6)
|
|
|
|
{
|
|
|
|
m_stat.trancieve_ip_packet(bytes, ipv6);
|
|
|
|
m_ses.trancieve_ip_packet(bytes, ipv6);
|
|
|
|
}
|
2010-01-15 17:45:42 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::sent_syn(bool ipv6)
|
|
|
|
{
|
|
|
|
m_stat.sent_syn(ipv6);
|
|
|
|
m_ses.sent_syn(ipv6);
|
2010-01-15 17:45:42 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::received_synack(bool ipv6)
|
2009-12-29 18:49:24 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
m_stat.received_synack(ipv6);
|
|
|
|
m_ses.received_synack(ipv6);
|
2009-12-29 18:49:24 +01:00
|
|
|
}
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 0
|
2014-04-22 06:21:14 +02:00
|
|
|
char const* esc(char const* code)
|
|
|
|
{
|
|
|
|
// this is a silly optimization
|
|
|
|
// to avoid copying of strings
|
|
|
|
enum { num_strings = 200 };
|
|
|
|
static char buf[num_strings][20];
|
|
|
|
static int round_robin = 0;
|
|
|
|
char* ret = buf[round_robin];
|
|
|
|
++round_robin;
|
|
|
|
if (round_robin >= num_strings) round_robin = 0;
|
|
|
|
ret[0] = '\033';
|
|
|
|
ret[1] = '[';
|
|
|
|
int i = 2;
|
|
|
|
int j = 0;
|
|
|
|
while (code[j]) ret[i++] = code[j++];
|
|
|
|
ret[i++] = 'm';
|
|
|
|
ret[i++] = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int peer_index(libtorrent::tcp::endpoint addr
|
|
|
|
, std::vector<libtorrent::peer_info> const& peers)
|
|
|
|
{
|
|
|
|
std::vector<peer_info>::const_iterator i = std::find_if(peers.begin()
|
2016-05-25 06:31:52 +02:00
|
|
|
, peers.end(), std::bind(&peer_info::ip, _1) == addr);
|
2014-04-22 06:21:14 +02:00
|
|
|
if (i == peers.end()) return -1;
|
|
|
|
|
|
|
|
return i - peers.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_piece(libtorrent::partial_piece_info* pp
|
|
|
|
, std::vector<libtorrent::peer_info> const& peers
|
|
|
|
, std::vector<time_critical_piece> const& time_critical)
|
|
|
|
{
|
2016-12-10 20:15:25 +01:00
|
|
|
time_point const now = clock_type::now();
|
2014-04-22 06:21:14 +02:00
|
|
|
|
|
|
|
float deadline = 0.f;
|
|
|
|
float last_request = 0.f;
|
|
|
|
int timed_out = -1;
|
|
|
|
|
|
|
|
int piece = pp->piece_index;
|
|
|
|
std::vector<time_critical_piece>::const_iterator i
|
|
|
|
= std::find_if(time_critical.begin(), time_critical.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&time_critical_piece::piece, _1) == piece);
|
2014-04-22 06:21:14 +02:00
|
|
|
if (i != time_critical.end())
|
|
|
|
{
|
|
|
|
deadline = total_milliseconds(i->deadline - now) / 1000.f;
|
2014-07-25 11:03:56 +02:00
|
|
|
if (i->last_requested == min_time())
|
|
|
|
last_request = -1;
|
|
|
|
else
|
|
|
|
last_request = total_milliseconds(now - i->last_requested) / 1000.f;
|
2014-04-22 06:21:14 +02:00
|
|
|
timed_out = i->timed_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
int num_blocks = pp->blocks_in_piece;
|
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("%5d: [", piece);
|
2014-04-22 06:21:14 +02:00
|
|
|
for (int j = 0; j < num_blocks; ++j)
|
|
|
|
{
|
|
|
|
int index = pp ? peer_index(pp->blocks[j].peer(), peers) % 36 : -1;
|
|
|
|
char chr = '+';
|
|
|
|
if (index >= 0)
|
|
|
|
chr = (index < 10)?'0' + index:'A' + index - 10;
|
|
|
|
|
|
|
|
char const* color = "";
|
|
|
|
char const* multi_req = "";
|
|
|
|
|
|
|
|
if (pp->blocks[j].num_peers > 1)
|
|
|
|
multi_req = esc("1");
|
|
|
|
|
|
|
|
if (pp->blocks[j].bytes_progress > 0
|
|
|
|
&& pp->blocks[j].state == block_info::requested)
|
|
|
|
{
|
|
|
|
color = esc("33;7");
|
|
|
|
chr = '0' + (pp->blocks[j].bytes_progress * 10 / pp->blocks[j].block_size);
|
|
|
|
}
|
|
|
|
else if (pp->blocks[j].state == block_info::finished) color = esc("32;7");
|
|
|
|
else if (pp->blocks[j].state == block_info::writing) color = esc("36;7");
|
|
|
|
else if (pp->blocks[j].state == block_info::requested) color = esc("0");
|
|
|
|
else { color = esc("0"); chr = ' '; }
|
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("%s%s%c%s", color, multi_req, chr, esc("0"));
|
2014-04-22 06:21:14 +02:00
|
|
|
}
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("%s]", esc("0"));
|
2014-04-22 06:21:14 +02:00
|
|
|
if (deadline != 0.f)
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf(" deadline: %f last-req: %f timed_out: %d\n"
|
2014-04-22 06:21:14 +02:00
|
|
|
, deadline, last_request, timed_out);
|
|
|
|
else
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("\n");
|
2014-04-22 06:21:14 +02:00
|
|
|
}
|
|
|
|
#endif // TORRENT_DEBUG_STREAMING
|
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
namespace {
|
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
struct busy_block_t
|
|
|
|
{
|
|
|
|
int peers;
|
|
|
|
int index;
|
|
|
|
bool operator<(busy_block_t rhs) const { return peers < rhs.peers; }
|
|
|
|
};
|
|
|
|
|
2015-02-14 22:32:41 +01:00
|
|
|
void pick_busy_blocks(piece_picker const* picker
|
2016-12-22 16:42:33 +01:00
|
|
|
, piece_index_t piece
|
2015-02-14 22:32:41 +01:00
|
|
|
, int blocks_in_piece
|
2014-04-22 06:21:14 +02:00
|
|
|
, int timed_out
|
|
|
|
, std::vector<piece_block>& interesting_blocks
|
|
|
|
, piece_picker::downloading_piece const& pi)
|
|
|
|
{
|
|
|
|
// if there aren't any free blocks in the piece, and the piece is
|
|
|
|
// old enough, we may switch into busy mode for this piece. In this
|
|
|
|
// case busy_blocks and busy_count are set to contain the eligible
|
|
|
|
// busy blocks we may pick
|
|
|
|
// first, figure out which blocks are eligible for picking
|
|
|
|
// in "busy-mode"
|
2016-10-22 20:43:40 +02:00
|
|
|
TORRENT_ALLOCA(busy_blocks, busy_block_t, blocks_in_piece);
|
2014-04-22 06:21:14 +02:00
|
|
|
int busy_count = 0;
|
|
|
|
|
2015-02-14 22:32:41 +01:00
|
|
|
piece_picker::block_info const* info = picker->blocks_for_piece(pi);
|
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
// pick busy blocks from the piece
|
|
|
|
for (int k = 0; k < blocks_in_piece; ++k)
|
|
|
|
{
|
|
|
|
// only consider blocks that have been requested
|
|
|
|
// and we're still waiting for them
|
2015-02-14 22:32:41 +01:00
|
|
|
if (info[k].state != piece_picker::block_info::state_requested)
|
2014-04-22 06:21:14 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
piece_block b(piece, k);
|
|
|
|
|
|
|
|
// only allow a single additional request per block, in order
|
|
|
|
// to spread it out evenly across all stalled blocks
|
2015-02-14 22:32:41 +01:00
|
|
|
if (info[k].num_peers > timed_out)
|
2014-04-22 06:21:14 +02:00
|
|
|
continue;
|
|
|
|
|
2016-02-20 06:48:17 +01:00
|
|
|
busy_blocks[busy_count].peers = info[k].num_peers;
|
2014-04-22 06:21:14 +02:00
|
|
|
busy_blocks[busy_count].index = k;
|
|
|
|
++busy_count;
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf(" [%d (%d)]", b.block_index, info[k].num_peers);
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
}
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("\n");
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
|
2016-10-22 20:43:40 +02:00
|
|
|
busy_blocks = busy_blocks.first(busy_count);
|
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
// then sort blocks by the number of peers with requests
|
|
|
|
// to the blocks (request the blocks with the fewest peers
|
|
|
|
// first)
|
2016-10-22 20:43:40 +02:00
|
|
|
std::sort(busy_blocks.begin(), busy_blocks.end());
|
2014-04-22 06:21:14 +02:00
|
|
|
|
|
|
|
// then insert them into the interesting_blocks vector
|
2016-10-22 20:43:40 +02:00
|
|
|
for (auto block : busy_blocks)
|
2014-04-22 06:21:14 +02:00
|
|
|
{
|
|
|
|
interesting_blocks.push_back(
|
2016-10-22 20:43:40 +02:00
|
|
|
piece_block(piece, block.index));
|
2014-04-22 06:21:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pick_time_critical_block(std::vector<peer_connection*>& peers
|
|
|
|
, std::vector<peer_connection*>& ignore_peers
|
|
|
|
, std::set<peer_connection*>& peers_with_requests
|
|
|
|
, piece_picker::downloading_piece const& pi
|
|
|
|
, time_critical_piece* i
|
2015-02-14 22:32:41 +01:00
|
|
|
, piece_picker const* picker
|
2014-04-22 06:21:14 +02:00
|
|
|
, int blocks_in_piece
|
|
|
|
, int timed_out)
|
|
|
|
{
|
|
|
|
std::vector<piece_block> interesting_blocks;
|
|
|
|
std::vector<piece_block> backup1;
|
|
|
|
std::vector<piece_block> backup2;
|
2016-12-22 16:42:33 +01:00
|
|
|
std::vector<piece_index_t> ignore;
|
2014-04-22 06:21:14 +02:00
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point now = aux::time_now();
|
2014-04-22 06:21:14 +02:00
|
|
|
|
|
|
|
// loop until every block has been requested from this piece (i->piece)
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// if this peer's download time exceeds 2 seconds, we're done.
|
|
|
|
// We don't want to build unreasonably long request queues
|
|
|
|
if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000))
|
2014-07-26 19:48:24 +02:00
|
|
|
{
|
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("queue time: %d ms, done\n"
|
2014-07-26 19:48:24 +02:00
|
|
|
, int(total_milliseconds(peers[0]->download_queue_time())));
|
|
|
|
#endif
|
2014-04-22 06:21:14 +02:00
|
|
|
break;
|
2014-07-26 19:48:24 +02:00
|
|
|
}
|
2014-04-22 06:21:14 +02:00
|
|
|
|
|
|
|
// pick the peer with the lowest download_queue_time that has i->piece
|
2016-12-15 20:20:08 +01:00
|
|
|
auto p = std::find_if(peers.begin(), peers.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&peer_connection::has_piece, _1, i->piece));
|
2014-04-22 06:21:14 +02:00
|
|
|
|
|
|
|
// obviously we'll have to skip it if we don't have a peer that has
|
|
|
|
// this piece
|
|
|
|
if (p == peers.end())
|
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("out of peers, done\n");
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
peer_connection& c = **p;
|
|
|
|
|
|
|
|
interesting_blocks.clear();
|
|
|
|
backup1.clear();
|
|
|
|
backup2.clear();
|
|
|
|
|
|
|
|
// specifically request blocks with no affinity towards fast or slow
|
|
|
|
// pieces. If we would, the picked block might end up in one of
|
|
|
|
// the backup lists
|
|
|
|
picker->add_blocks(i->piece, c.get_bitfield(), interesting_blocks
|
|
|
|
, backup1, backup2, blocks_in_piece, 0, c.peer_info_struct()
|
2015-02-08 22:17:00 +01:00
|
|
|
, ignore, 0);
|
2014-04-22 06:21:14 +02:00
|
|
|
|
|
|
|
interesting_blocks.insert(interesting_blocks.end()
|
|
|
|
, backup1.begin(), backup1.end());
|
|
|
|
interesting_blocks.insert(interesting_blocks.end()
|
|
|
|
, backup2.begin(), backup2.end());
|
|
|
|
|
|
|
|
bool busy_mode = false;
|
|
|
|
|
|
|
|
if (interesting_blocks.empty())
|
|
|
|
{
|
|
|
|
busy_mode = true;
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("interesting_blocks.empty()\n");
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// there aren't any free blocks to pick, and the piece isn't
|
|
|
|
// old enough to pick busy blocks yet. break to continue to
|
|
|
|
// the next piece.
|
|
|
|
if (timed_out == 0)
|
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("not timed out, moving on to next piece\n");
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("pick busy blocks\n");
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
|
2015-02-14 22:32:41 +01:00
|
|
|
pick_busy_blocks(picker, i->piece, blocks_in_piece, timed_out
|
2014-04-22 06:21:14 +02:00
|
|
|
, interesting_blocks, pi);
|
|
|
|
}
|
|
|
|
|
|
|
|
// we can't pick anything from this piece, we're done with it.
|
|
|
|
// move on to the next one
|
|
|
|
if (interesting_blocks.empty()) break;
|
|
|
|
|
|
|
|
piece_block b = interesting_blocks.front();
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
// in busy mode we need to make sure we don't do silly
|
|
|
|
// things like requesting the same block twice from the
|
|
|
|
// same peer
|
|
|
|
std::vector<pending_block> const& dq = c.download_queue();
|
|
|
|
|
2016-09-24 17:46:56 +02:00
|
|
|
bool const already_requested = std::find_if(dq.begin(), dq.end()
|
|
|
|
, aux::has_block(b)) != dq.end();
|
2014-06-06 03:26:18 +02:00
|
|
|
|
2014-07-26 19:48:24 +02:00
|
|
|
if (already_requested)
|
2014-04-22 06:21:14 +02:00
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
// if the piece is stalled, we may end up picking a block
|
|
|
|
// that we've already requested from this peer. If so, we should
|
|
|
|
// simply disregard this peer from this piece, since this peer
|
|
|
|
// is likely to be causing the stall. We should request it
|
|
|
|
// from the next peer in the list
|
|
|
|
// the peer will be put back in the set for the next piece
|
|
|
|
ignore_peers.push_back(*p);
|
|
|
|
peers.erase(p);
|
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("piece already requested by peer, try next peer\n");
|
2014-06-06 03:26:18 +02:00
|
|
|
#endif
|
|
|
|
// try next peer
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<pending_block> const& rq = c.request_queue();
|
2014-04-22 06:21:14 +02:00
|
|
|
|
2016-09-24 17:46:56 +02:00
|
|
|
bool const already_in_queue = std::find_if(rq.begin(), rq.end()
|
|
|
|
, aux::has_block(b)) != rq.end();
|
2014-06-06 03:26:18 +02:00
|
|
|
|
|
|
|
if (already_in_queue)
|
|
|
|
{
|
|
|
|
if (!c.make_time_critical(b))
|
2014-04-22 06:21:14 +02:00
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("piece already time-critical and in queue for peer, trying next peer\n");
|
2014-06-06 03:26:18 +02:00
|
|
|
#endif
|
2014-04-22 06:21:14 +02:00
|
|
|
ignore_peers.push_back(*p);
|
|
|
|
peers.erase(p);
|
|
|
|
continue;
|
|
|
|
}
|
2014-06-06 03:26:18 +02:00
|
|
|
i->last_requested = now;
|
2014-04-22 06:21:14 +02:00
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("piece already in queue for peer, making time-critical\n");
|
2014-06-06 03:26:18 +02:00
|
|
|
#endif
|
2014-04-22 06:21:14 +02:00
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
// we inserted a new block in the request queue, this
|
|
|
|
// makes us actually send it later
|
|
|
|
peers_with_requests.insert(peers_with_requests.begin(), &c);
|
|
|
|
}
|
2014-07-26 19:48:24 +02:00
|
|
|
else
|
2014-06-06 03:26:18 +02:00
|
|
|
{
|
|
|
|
if (!c.add_request(b, peer_connection::req_time_critical
|
|
|
|
| (busy_mode ? peer_connection::req_busy : 0)))
|
2014-04-22 06:21:14 +02:00
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("failed to request block [%d, %d]\n"
|
2014-06-06 03:26:18 +02:00
|
|
|
, b.piece_index, b.block_index);
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
2014-06-06 03:26:18 +02:00
|
|
|
ignore_peers.push_back(*p);
|
|
|
|
peers.erase(p);
|
2014-04-22 06:21:14 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("requested block [%d, %d]\n"
|
2014-04-22 06:21:14 +02:00
|
|
|
, b.piece_index, b.block_index);
|
|
|
|
#endif
|
2014-06-06 03:26:18 +02:00
|
|
|
peers_with_requests.insert(peers_with_requests.begin(), &c);
|
2014-04-22 06:21:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!busy_mode) i->last_requested = now;
|
|
|
|
|
|
|
|
if (i->first_requested == min_time()) i->first_requested = now;
|
|
|
|
|
|
|
|
if (!c.can_request_time_critical())
|
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("peer cannot pick time critical pieces\n");
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
peers.erase(p);
|
|
|
|
// try next peer
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// resort p, since it will have a higher download_queue_time now
|
|
|
|
while (p != peers.end()-1 && (*p)->download_queue_time()
|
|
|
|
> (*(p+1))->download_queue_time())
|
|
|
|
{
|
|
|
|
std::iter_swap(p, p+1);
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
} while (!interesting_blocks.empty());
|
|
|
|
}
|
|
|
|
|
2015-04-20 06:52:49 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2009-03-17 10:34:44 +01:00
|
|
|
void torrent::request_time_critical_pieces()
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-04-22 06:21:14 +02:00
|
|
|
TORRENT_ASSERT(!upload_mode());
|
|
|
|
|
2009-03-17 10:34:44 +01:00
|
|
|
// build a list of peers and sort it by download_queue_time
|
2011-08-05 09:56:07 +02:00
|
|
|
// we use this sorted list to determine which peer we should
|
2014-06-06 03:26:18 +02:00
|
|
|
// request a block from. The earlier a peer is in the list,
|
2011-08-05 09:56:07 +02:00
|
|
|
// the sooner we will fully download the block we request.
|
2009-03-17 10:34:44 +01:00
|
|
|
std::vector<peer_connection*> peers;
|
|
|
|
peers.reserve(m_connections.size());
|
2014-06-06 03:26:18 +02:00
|
|
|
|
|
|
|
// some peers are marked as not being able to request time critical
|
|
|
|
// blocks from. For instance, peers that have choked us, peers that are
|
|
|
|
// on parole (i.e. they are believed to have sent us bad data), peers
|
|
|
|
// that are being disconnected, in upload mode etc.
|
2009-03-17 10:34:44 +01:00
|
|
|
std::remove_copy_if(m_connections.begin(), m_connections.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::back_inserter(peers), [] (peer_connection* p)
|
|
|
|
{ return !p->can_request_time_critical(); });
|
2014-06-06 03:26:18 +02:00
|
|
|
|
|
|
|
// sort by the time we believe it will take this peer to send us all
|
|
|
|
// blocks we've requested from it. The shorter time, the better candidate
|
|
|
|
// it is to request a time critical block from.
|
2009-03-17 10:34:44 +01:00
|
|
|
std::sort(peers.begin(), peers.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, [] (peer_connection const* lhs, peer_connection const* rhs)
|
|
|
|
{ return lhs->download_queue_time(16*1024) < rhs->download_queue_time(16*1024); });
|
2009-03-17 10:34:44 +01:00
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
// remove the bottom 10% of peers from the candidate set.
|
|
|
|
// this is just to remove outliers that might stall downloads
|
2016-05-25 06:31:52 +02:00
|
|
|
int const new_size = int((peers.size() * 9 + 9) / 10);
|
2014-07-05 01:40:31 +02:00
|
|
|
TORRENT_ASSERT(new_size <= int(peers.size()));
|
2013-07-27 19:43:34 +02:00
|
|
|
peers.resize(new_size);
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
// remember all the peers we issued requests to, so we can commit them
|
|
|
|
// at the end of this function. Instead of sending the requests right
|
|
|
|
// away, we batch them up and send them in a single write to the TCP
|
|
|
|
// socket, increasing the chance that they will all be sent in the same
|
|
|
|
// packet.
|
2009-03-17 10:34:44 +01:00
|
|
|
std::set<peer_connection*> peers_with_requests;
|
|
|
|
|
2013-02-17 05:07:30 +01:00
|
|
|
// peers that should be temporarily ignored for a specific piece
|
|
|
|
// in order to give priority to other peers. They should be used for
|
|
|
|
// subsequent pieces, so they are stored in this vector until the
|
|
|
|
// piece is done
|
|
|
|
std::vector<peer_connection*> ignore_peers;
|
|
|
|
|
2016-12-10 20:15:25 +01:00
|
|
|
time_point const now = clock_type::now();
|
2009-03-17 10:34:44 +01:00
|
|
|
|
2011-08-05 09:56:07 +02:00
|
|
|
// now, iterate over all time critical pieces, in order of importance, and
|
|
|
|
// request them from the peers, in order of responsiveness. i.e. request
|
|
|
|
// the most time critical pieces from the fastest peers.
|
2014-07-06 21:18:00 +02:00
|
|
|
for (std::vector<time_critical_piece>::iterator i = m_time_critical_pieces.begin()
|
2009-03-17 10:34:44 +01:00
|
|
|
, end(m_time_critical_pieces.end()); i != end; ++i)
|
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("considering %d\n", i->piece);
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (peers.empty())
|
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("out of peers, done\n");
|
2014-04-22 06:21:14 +02:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
2011-08-05 19:35:09 +02:00
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
// the +1000 is to compensate for the fact that we only call this
|
|
|
|
// function once per second, so if we need to request it 500 ms from
|
|
|
|
// now, we should request it right away
|
2009-03-17 10:34:44 +01:00
|
|
|
if (i != m_time_critical_pieces.begin() && i->deadline > now
|
2013-02-17 05:07:30 +01:00
|
|
|
+ milliseconds(m_average_piece_time + m_piece_time_deviation * 4 + 1000))
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
|
|
|
// don't request pieces whose deadline is too far in the future
|
2011-08-05 09:56:07 +02:00
|
|
|
// this is one of the termination conditions. We don't want to
|
|
|
|
// send requests for all pieces in the torrent right away
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 0
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("reached deadline horizon [%f + %f * 4 + 1]\n"
|
2014-04-22 06:21:14 +02:00
|
|
|
, m_average_piece_time / 1000.f
|
|
|
|
, m_piece_time_deviation / 1000.f);
|
|
|
|
#endif
|
2009-03-17 10:34:44 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-02-17 05:07:30 +01:00
|
|
|
piece_picker::downloading_piece pi;
|
|
|
|
m_picker->piece_info(i->piece, pi);
|
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
// the number of "times" this piece has timed out.
|
|
|
|
int timed_out = 0;
|
|
|
|
|
|
|
|
int blocks_in_piece = m_picker->blocks_in_piece(i->piece);
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 0
|
2014-04-22 06:21:14 +02:00
|
|
|
i->timed_out = timed_out;
|
|
|
|
#endif
|
|
|
|
int free_to_request = blocks_in_piece
|
|
|
|
- pi.finished - pi.writing - pi.requested;
|
2013-07-27 19:43:34 +02:00
|
|
|
|
2013-02-17 05:07:30 +01:00
|
|
|
if (free_to_request == 0)
|
|
|
|
{
|
2014-04-22 06:21:14 +02:00
|
|
|
if (i->last_requested == min_time())
|
|
|
|
i->last_requested = now;
|
|
|
|
|
|
|
|
// if it's been more than half of the typical download time
|
|
|
|
// of a piece since we requested the last block, allow
|
|
|
|
// one more request per block
|
|
|
|
if (m_average_piece_time > 0)
|
2016-12-05 14:39:53 +01:00
|
|
|
timed_out = int(total_milliseconds(now - i->last_requested)
|
|
|
|
/ (std::max)(int(m_average_piece_time + m_piece_time_deviation / 2), 1));
|
2014-04-22 06:21:14 +02:00
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 0
|
2014-04-22 06:21:14 +02:00
|
|
|
i->timed_out = timed_out;
|
|
|
|
#endif
|
2013-02-17 05:07:30 +01:00
|
|
|
// every block in this piece is already requested
|
|
|
|
// there's no need to consider this piece, unless it
|
|
|
|
// appears to be stalled.
|
2014-04-22 06:21:14 +02:00
|
|
|
if (pi.requested == 0 || timed_out == 0)
|
2013-02-17 05:07:30 +01:00
|
|
|
{
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("skipping %d (full) [req: %d timed_out: %d ]\n"
|
2014-04-22 06:21:14 +02:00
|
|
|
, i->piece, pi.requested
|
|
|
|
, timed_out);
|
|
|
|
#endif
|
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
// if requested is 0, it means all blocks have been received, and
|
2013-02-17 05:07:30 +01:00
|
|
|
// we're just waiting for it to flush them to disk.
|
|
|
|
// if last_requested is recent enough, we should give it some
|
|
|
|
// more time
|
2013-07-27 19:43:34 +02:00
|
|
|
// skip to the next piece
|
|
|
|
continue;
|
2013-02-17 05:07:30 +01:00
|
|
|
}
|
2013-07-27 19:43:34 +02:00
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
// it's been too long since we requested the last block from
|
|
|
|
// this piece. Allow re-requesting blocks from this piece
|
2014-06-06 03:26:18 +02:00
|
|
|
#if TORRENT_DEBUG_STREAMING > 1
|
2016-05-17 15:24:06 +02:00
|
|
|
std::printf("timed out [average-piece-time: %d ms ]\n"
|
2014-04-22 06:21:14 +02:00
|
|
|
, m_average_piece_time);
|
|
|
|
#endif
|
2013-02-17 05:07:30 +01:00
|
|
|
}
|
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
// pick all blocks for this piece. the peers list is kept up to date
|
|
|
|
// and sorted. when we issue a request to a peer, its download queue
|
|
|
|
// time will increase and it may need to be bumped in the peers list,
|
|
|
|
// since it's ordered by download queue time
|
2014-04-22 06:21:14 +02:00
|
|
|
pick_time_critical_block(peers, ignore_peers
|
|
|
|
, peers_with_requests
|
|
|
|
, pi, &*i, m_picker.get()
|
|
|
|
, blocks_in_piece, timed_out);
|
2013-02-17 05:07:30 +01:00
|
|
|
|
2014-06-06 03:26:18 +02:00
|
|
|
// put back the peers we ignored into the peer list for the next piece
|
|
|
|
if (!ignore_peers.empty())
|
|
|
|
{
|
|
|
|
peers.insert(peers.begin(), ignore_peers.begin(), ignore_peers.end());
|
|
|
|
ignore_peers.clear();
|
|
|
|
|
|
|
|
// TODO: instead of resorting the whole list, insert the peers
|
|
|
|
// directly into the right place
|
|
|
|
std::sort(peers.begin(), peers.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, [] (peer_connection const* lhs, peer_connection const* rhs)
|
|
|
|
{ return lhs->download_queue_time(16*1024) < rhs->download_queue_time(16*1024); });
|
2014-06-06 03:26:18 +02:00
|
|
|
}
|
2014-06-08 23:15:34 +02:00
|
|
|
|
|
|
|
// if this peer's download time exceeds 2 seconds, we're done.
|
|
|
|
// We don't want to build unreasonably long request queues
|
|
|
|
if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000))
|
|
|
|
break;
|
2009-03-17 10:34:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// commit all the time critical requests
|
2016-05-25 06:31:52 +02:00
|
|
|
for (auto p : peers_with_requests)
|
2009-03-17 10:34:44 +01:00
|
|
|
{
|
2016-05-25 06:31:52 +02:00
|
|
|
p->send_block_requests();
|
2009-03-17 10:34:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-30 04:54:07 +01:00
|
|
|
std::set<std::string> torrent::web_seeds(web_seed_entry::type_t type) const
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-12-30 04:54:07 +01:00
|
|
|
std::set<std::string> ret;
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const& s : m_web_seeds)
|
2008-12-30 04:54:07 +01:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
if (s.peer_info.banned) continue;
|
|
|
|
if (s.removed) continue;
|
|
|
|
if (s.type != type) continue;
|
|
|
|
ret.insert(s.url);
|
2008-12-30 04:54:07 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-24 02:29:31 +01:00
|
|
|
void torrent::remove_web_seed(std::string const& url, web_seed_entry::type_t type)
|
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
auto const i = std::find_if(m_web_seeds.begin(), m_web_seeds.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, [&] (web_seed_t const& w) { return w.url == url && w.type == type; });
|
2015-08-23 11:01:55 +02:00
|
|
|
|
2016-05-22 01:05:42 +02:00
|
|
|
if (i != m_web_seeds.end()) remove_web_seed_iter(i);
|
2012-03-24 02:29:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::disconnect_web_seed(peer_connection* p)
|
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
auto const i = std::find_if(m_web_seeds.begin(), m_web_seeds.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, [p] (web_seed_t const& ws) { return ws.peer_info.connection == p; });
|
2014-09-28 04:20:13 +02:00
|
|
|
|
2012-03-24 02:29:31 +01:00
|
|
|
// this happens if the web server responded with a redirect
|
|
|
|
// or with something incorrect, so that we removed the web seed
|
|
|
|
// immediately, before we disconnected
|
|
|
|
if (i == m_web_seeds.end()) return;
|
|
|
|
|
|
|
|
TORRENT_ASSERT(i->resolving == false);
|
|
|
|
|
|
|
|
TORRENT_ASSERT(i->peer_info.connection);
|
2016-07-09 22:26:26 +02:00
|
|
|
i->peer_info.connection = nullptr;
|
2012-03-24 02:29:31 +01:00
|
|
|
}
|
|
|
|
|
2016-05-22 01:05:42 +02:00
|
|
|
void torrent::remove_web_seed_conn(peer_connection* p, error_code const& ec
|
2015-02-15 06:17:09 +01:00
|
|
|
, operation_t op, int error)
|
2012-03-24 02:29:31 +01:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
auto const i = std::find_if(m_web_seeds.begin(), m_web_seeds.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, [p] (web_seed_t const& ws) { return ws.peer_info.connection == p; });
|
|
|
|
|
2012-03-24 02:29:31 +01:00
|
|
|
TORRENT_ASSERT(i != m_web_seeds.end());
|
|
|
|
if (i == m_web_seeds.end()) return;
|
2015-08-23 11:01:55 +02:00
|
|
|
|
|
|
|
peer_connection* peer = static_cast<peer_connection*>(i->peer_info.connection);
|
2016-12-15 20:20:08 +01:00
|
|
|
if (peer != nullptr)
|
2015-08-23 11:01:55 +02:00
|
|
|
{
|
|
|
|
// if we have a connection for this web seed, we also need to
|
|
|
|
// disconnect it and clear its reference to the peer_info object
|
|
|
|
// that's part of the web_seed_t we're about to remove
|
|
|
|
TORRENT_ASSERT(peer->m_in_use == 1337);
|
|
|
|
peer->disconnect(ec, op, error);
|
2016-07-09 22:26:26 +02:00
|
|
|
peer->set_peer_info(nullptr);
|
2015-08-23 11:01:55 +02:00
|
|
|
}
|
2016-05-22 01:05:42 +02:00
|
|
|
remove_web_seed_iter(i);
|
2012-03-24 02:29:31 +01:00
|
|
|
}
|
|
|
|
|
2010-04-14 08:22:00 +02:00
|
|
|
void torrent::retry_web_seed(peer_connection* p, int retry)
|
2007-11-27 04:37:47 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-12-15 20:20:08 +01:00
|
|
|
auto const i = std::find_if(m_web_seeds.begin(), m_web_seeds.end()
|
2016-05-25 06:31:52 +02:00
|
|
|
, [p] (web_seed_t const& ws) { return ws.peer_info.connection == p; });
|
2010-02-12 07:10:20 +01:00
|
|
|
|
2010-04-14 08:22:00 +02:00
|
|
|
TORRENT_ASSERT(i != m_web_seeds.end());
|
|
|
|
if (i == m_web_seeds.end()) return;
|
2015-08-23 11:01:55 +02:00
|
|
|
if (i->removed) return;
|
2015-05-16 08:33:37 +02:00
|
|
|
if (retry == 0) retry = settings().get_int(settings_pack::urlseed_wait_retry);
|
2015-03-12 05:34:54 +01:00
|
|
|
i->retry = aux::time_now() + seconds(retry);
|
2007-11-27 04:37:47 +01:00
|
|
|
}
|
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
torrent_state torrent::get_peer_list_state()
|
2007-08-16 14:41:46 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
torrent_state ret;
|
|
|
|
ret.is_paused = is_paused();
|
|
|
|
ret.is_finished = is_finished();
|
|
|
|
ret.allow_multiple_connections_per_ip = settings().get_bool(settings_pack::allow_multiple_connections_per_ip);
|
|
|
|
ret.max_peerlist_size = is_paused()
|
|
|
|
? settings().get_int(settings_pack::max_paused_peerlist_size)
|
|
|
|
: settings().get_int(settings_pack::max_peerlist_size);
|
|
|
|
ret.min_reconnect_time = settings().get_int(settings_pack::min_reconnect_time);
|
|
|
|
|
|
|
|
ret.peer_allocator = m_ses.get_peer_allocator();
|
2016-12-04 21:58:51 +01:00
|
|
|
ret.ip = m_ses.external_address();
|
2014-07-06 21:18:00 +02:00
|
|
|
ret.port = m_ses.listen_port();
|
2015-01-20 07:26:22 +01:00
|
|
|
ret.max_failcount = settings().get_int(settings_pack::max_failcount);
|
2008-03-29 20:39:24 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool torrent::try_connect_peer()
|
2010-07-14 06:16:38 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(want_peers());
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
torrent_state st = get_peer_list_state();
|
|
|
|
need_peer_list();
|
2014-10-26 08:34:31 +01:00
|
|
|
torrent_peer* p = m_peer_list->connect_one_peer(m_ses.session_time(), &st);
|
2014-07-06 21:18:00 +02:00
|
|
|
peers_erased(st.erased);
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::connection_attempt_loops, st.loop_counter);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-06-20 17:32:06 +02:00
|
|
|
if (p == nullptr)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
update_want_peers();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!connect_to_peer(p))
|
|
|
|
{
|
2014-10-26 08:34:31 +01:00
|
|
|
m_peer_list->inc_failcount(p);
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
update_want_peers();
|
|
|
|
|
|
|
|
return true;
|
2010-07-14 06:16:38 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
torrent_peer* torrent::add_peer(tcp::endpoint const& adr, int source, int flags)
|
2003-12-07 06:53:04 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#if !TORRENT_USE_IPV6
|
2015-07-12 20:08:04 +02:00
|
|
|
if (!adr.address().is_v4())
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
error_code ec;
|
|
|
|
debug_log("add_peer() %s unsupported address family"
|
|
|
|
, adr.address().to_string(ec).c_str());
|
|
|
|
#endif
|
2016-06-20 17:32:06 +02:00
|
|
|
return nullptr;
|
2015-07-12 20:08:04 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
|
|
|
if (source != peer_info::resume_data)
|
2008-02-18 04:07:14 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// try to send a DHT ping to this peer
|
|
|
|
// as well, to figure out if it supports
|
2015-07-12 20:08:04 +02:00
|
|
|
// DHT (uTorrent and BitComet don't
|
2014-07-06 21:18:00 +02:00
|
|
|
// advertise support)
|
|
|
|
udp::endpoint node(adr.address(), adr.port());
|
|
|
|
session().add_dht_node(node);
|
2008-02-18 04:07:14 +01:00
|
|
|
}
|
|
|
|
#endif
|
2004-01-25 13:37:15 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_apply_ip_filter
|
2015-05-16 08:33:37 +02:00
|
|
|
&& m_ip_filter
|
|
|
|
&& m_ip_filter->access(adr.address()) & ip_filter::blocked)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (alerts().should_post<peer_blocked_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-05-23 01:56:14 +02:00
|
|
|
, adr, peer_blocked_alert::ip_filter);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
notify_extension_add_peer(adr, source, torrent_plugin::filtered);
|
|
|
|
#endif
|
2016-06-20 17:32:06 +02:00
|
|
|
return nullptr;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_ses.get_port_filter().access(adr.port()) & port_filter::blocked)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<peer_blocked_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-05-23 01:56:14 +02:00
|
|
|
, adr, peer_blocked_alert::port_filter);
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
notify_extension_add_peer(adr, source, torrent_plugin::filtered);
|
|
|
|
#endif
|
2016-06-20 17:32:06 +02:00
|
|
|
return nullptr;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
// if this is an i2p torrent, and we don't allow mixed mode
|
|
|
|
// no regular peers should ever be added!
|
|
|
|
if (!settings().get_bool(settings_pack::allow_i2p_mixed) && is_i2p())
|
|
|
|
{
|
|
|
|
if (alerts().should_post<peer_blocked_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-05-23 01:56:14 +02:00
|
|
|
, adr, peer_blocked_alert::i2p_mixed);
|
2016-06-20 17:32:06 +02:00
|
|
|
return nullptr;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (settings().get_bool(settings_pack::no_connect_privileged_ports) && adr.port() < 1024)
|
|
|
|
{
|
|
|
|
if (alerts().should_post<peer_blocked_alert>())
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-05-23 01:56:14 +02:00
|
|
|
, adr, peer_blocked_alert::privileged_ports);
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
notify_extension_add_peer(adr, source, torrent_plugin::filtered);
|
|
|
|
#endif
|
2016-06-20 17:32:06 +02:00
|
|
|
return nullptr;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
need_peer_list();
|
|
|
|
torrent_state st = get_peer_list_state();
|
2016-11-25 17:17:25 +01:00
|
|
|
torrent_peer* p = m_peer_list->add_peer(adr, source, char(flags), &st);
|
2014-07-06 21:18:00 +02:00
|
|
|
peers_erased(st.erased);
|
2015-07-12 20:08:04 +02:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
2015-07-12 20:08:04 +02:00
|
|
|
error_code ec;
|
|
|
|
debug_log("add_peer() %s connect-candidates: %d"
|
|
|
|
, adr.address().to_string(ec).c_str(), m_peer_list->num_connect_candidates());
|
2016-09-14 04:46:07 +02:00
|
|
|
}
|
2015-07-12 20:08:04 +02:00
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (p)
|
|
|
|
{
|
|
|
|
state_updated();
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
notify_extension_add_peer(adr, source, st.first_time_seen ? torrent_plugin::first_time : 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
notify_extension_add_peer(adr, source, torrent_plugin::filtered);
|
2007-10-18 02:32:16 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
update_want_peers();
|
|
|
|
state_updated();
|
|
|
|
return p;
|
2007-06-10 22:46:09 +02:00
|
|
|
}
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
bool torrent::ban_peer(torrent_peer* tp)
|
2007-06-10 22:46:09 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!settings().get_bool(settings_pack::ban_web_seeds) && tp->web_seed)
|
|
|
|
return false;
|
2008-04-13 00:08:07 +02:00
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
need_peer_list();
|
2014-10-26 08:34:31 +01:00
|
|
|
if (!m_peer_list->ban_peer(tp)) return false;
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
2008-04-13 00:08:07 +02:00
|
|
|
|
2014-07-13 06:56:53 +02:00
|
|
|
inc_stats_counter(counters::num_banned_peers);
|
2014-07-06 21:18:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
2011-12-19 06:55:38 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::set_seed(torrent_peer* p, bool s)
|
|
|
|
{
|
2014-09-24 11:03:57 +02:00
|
|
|
if (p->seed != s)
|
|
|
|
{
|
|
|
|
if (s)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_num_seeds < 0xffff);
|
|
|
|
++m_num_seeds;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_num_seeds > 0);
|
|
|
|
--m_num_seeds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
need_peer_list();
|
2014-10-26 08:34:31 +01:00
|
|
|
m_peer_list->set_seed(p, s);
|
2014-09-24 02:02:00 +02:00
|
|
|
update_auto_sequential();
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::clear_failcount(torrent_peer* p)
|
|
|
|
{
|
2015-06-30 01:56:39 +02:00
|
|
|
need_peer_list();
|
2014-10-26 08:34:31 +01:00
|
|
|
m_peer_list->set_failcount(p, 0);
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
|
|
|
}
|
|
|
|
|
2014-10-26 08:34:31 +01:00
|
|
|
std::pair<peer_list::iterator, peer_list::iterator> torrent::find_peers(address const& a)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2015-06-30 01:56:39 +02:00
|
|
|
need_peer_list();
|
2014-10-26 08:34:31 +01:00
|
|
|
return m_peer_list->find_peers(a);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::update_peer_port(int port, torrent_peer* p, int src)
|
|
|
|
{
|
2015-06-30 01:56:39 +02:00
|
|
|
need_peer_list();
|
|
|
|
torrent_state st = get_peer_list_state();
|
2014-10-26 08:34:31 +01:00
|
|
|
m_peer_list->update_peer_port(port, p, src, &st);
|
2014-07-06 21:18:00 +02:00
|
|
|
peers_erased(st.erased);
|
|
|
|
update_want_peers();
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// verify piece is used when checking resume data or when the user
|
|
|
|
// adds a piece
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::verify_piece(piece_index_t const piece)
|
2004-03-01 01:50:00 +01:00
|
|
|
{
|
2015-02-13 03:58:21 +01:00
|
|
|
// picker().mark_as_checking(piece);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-12-31 18:35:10 +01:00
|
|
|
TORRENT_ASSERT(m_storage);
|
2015-08-06 02:13:58 +02:00
|
|
|
|
2016-12-31 18:35:10 +01:00
|
|
|
m_ses.disk_thread().async_hash(m_storage, piece, 0
|
2016-11-26 07:51:47 +01:00
|
|
|
, std::bind(&torrent::on_piece_verified, shared_from_this(), _1, _2, _3)
|
2015-08-18 10:25:13 +02:00
|
|
|
, reinterpret_cast<void*>(1));
|
2004-03-01 01:50:00 +01:00
|
|
|
}
|
2016-11-21 05:58:48 +01:00
|
|
|
|
2016-10-07 21:36:40 +02:00
|
|
|
announce_entry* torrent::find_tracker(std::string const& url)
|
2008-11-29 09:38:40 +01:00
|
|
|
{
|
2016-10-07 21:36:40 +02:00
|
|
|
auto i = std::find_if(m_trackers.begin(), m_trackers.end()
|
|
|
|
, [&url](announce_entry const& ae) { return ae.url == url; });
|
2016-07-09 22:26:26 +02:00
|
|
|
if (i == m_trackers.end()) return nullptr;
|
2008-11-29 09:38:40 +01:00
|
|
|
return &*i;
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::ip_filter_updated()
|
|
|
|
{
|
|
|
|
if (!m_apply_ip_filter) return;
|
2014-10-26 08:34:31 +01:00
|
|
|
if (!m_peer_list) return;
|
2016-05-27 18:12:32 +02:00
|
|
|
if (!m_ip_filter) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
torrent_state st = get_peer_list_state();
|
2014-07-06 21:18:00 +02:00
|
|
|
std::vector<address> banned;
|
2015-05-16 08:33:37 +02:00
|
|
|
m_peer_list->apply_ip_filter(*m_ip_filter, &st, banned);
|
2015-08-18 10:25:13 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (alerts().should_post<peer_blocked_alert>())
|
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const& addr : banned)
|
2016-05-23 01:56:14 +02:00
|
|
|
alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-12-15 20:20:08 +01:00
|
|
|
, tcp::endpoint(addr, 0)
|
2015-04-03 22:15:48 +02:00
|
|
|
, peer_blocked_alert::ip_filter);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2015-08-18 10:25:13 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
peers_erased(st.erased);
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::port_filter_updated()
|
|
|
|
{
|
|
|
|
if (!m_apply_ip_filter) return;
|
2014-10-26 08:34:31 +01:00
|
|
|
if (!m_peer_list) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-06-30 01:56:39 +02:00
|
|
|
torrent_state st = get_peer_list_state();
|
2014-07-06 21:18:00 +02:00
|
|
|
std::vector<address> banned;
|
2014-10-26 08:34:31 +01:00
|
|
|
m_peer_list->apply_port_filter(m_ses.get_port_filter(), &st, banned);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (alerts().should_post<peer_blocked_alert>())
|
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const& addr : banned)
|
2016-05-23 01:56:14 +02:00
|
|
|
alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
2016-12-15 20:20:08 +01:00
|
|
|
, tcp::endpoint(addr, 0)
|
2015-04-03 22:15:48 +02:00
|
|
|
, peer_blocked_alert::port_filter);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
peers_erased(st.erased);
|
|
|
|
}
|
|
|
|
|
2014-10-26 08:34:31 +01:00
|
|
|
// this is called when torrent_peers are removed from the peer_list
|
2014-07-06 21:18:00 +02:00
|
|
|
// (peer-list). It removes any references we may have to those torrent_peers,
|
|
|
|
// so we don't leave then dangling
|
|
|
|
void torrent::peers_erased(std::vector<torrent_peer*> const& peers)
|
|
|
|
{
|
|
|
|
if (!has_picker()) return;
|
|
|
|
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const p : peers)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-12-15 20:20:08 +01:00
|
|
|
m_picker->clear_peer(p);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
|
|
|
m_picker->check_peers();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-07-19 06:59:27 +02:00
|
|
|
#if !TORRENT_NO_FPU
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::file_progress_float(aux::vector<float, file_index_t>& fp)
|
2008-07-12 15:38:22 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
|
|
|
fp.clear();
|
|
|
|
return;
|
|
|
|
}
|
2016-02-20 06:48:17 +01:00
|
|
|
|
2008-07-12 15:38:22 +02:00
|
|
|
fp.resize(m_torrent_file->num_files(), 1.f);
|
|
|
|
if (is_seed()) return;
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
aux::vector<std::int64_t, file_index_t> progress;
|
2008-07-12 15:38:22 +02:00
|
|
|
file_progress(progress);
|
2016-12-22 16:42:33 +01:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
|
|
|
for (file_index_t i(0); i < fs.end_file(); ++i)
|
2008-07-12 15:38:22 +02:00
|
|
|
{
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t file_size = m_torrent_file->files().file_size(i);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (file_size == 0) fp[i] = 1.f;
|
|
|
|
else fp[i] = float(progress[i]) / file_size;
|
2008-07-12 15:38:22 +02:00
|
|
|
}
|
|
|
|
}
|
2009-07-19 06:59:27 +02:00
|
|
|
#endif
|
2008-07-12 15:38:22 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent::file_progress(aux::vector<std::int64_t, file_index_t>& fp, int const flags)
|
2006-06-12 01:24:36 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2012-08-26 08:38:29 +02:00
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
|
|
|
fp.clear();
|
|
|
|
return;
|
|
|
|
}
|
2008-07-12 15:38:22 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// if we're a seed, we don't have an m_file_progress anyway
|
|
|
|
// since we don't need one. We know we have all files
|
2014-11-09 12:17:13 +01:00
|
|
|
// just fill in the full file sizes as a shortcut
|
|
|
|
if (is_seed())
|
2008-06-07 04:58:28 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
fp.resize(m_torrent_file->num_files());
|
|
|
|
file_storage const& fs = m_torrent_file->files();
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < fs.end_file(); ++i)
|
2014-07-06 21:18:00 +02:00
|
|
|
fp[i] = fs.file_size(i);
|
2008-06-07 04:58:28 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-11-09 12:17:13 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (num_have() == 0)
|
2014-04-16 09:12:30 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// if we don't have any pieces, just return zeroes
|
|
|
|
fp.clear();
|
|
|
|
fp.resize(m_torrent_file->num_files(), 0);
|
2014-04-16 09:12:30 +02:00
|
|
|
return;
|
|
|
|
}
|
2015-05-18 07:04:55 +02:00
|
|
|
|
2015-06-29 04:47:11 +02:00
|
|
|
m_file_progress.export_progress(fp);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (flags & torrent_handle::piece_granularity)
|
|
|
|
return;
|
|
|
|
|
|
|
|
TORRENT_ASSERT(has_picker());
|
|
|
|
|
|
|
|
std::vector<piece_picker::downloading_piece> q = m_picker->get_download_queue();
|
|
|
|
|
2013-08-12 09:30:57 +02:00
|
|
|
file_storage const& fs = m_torrent_file->files();
|
2016-12-15 20:20:08 +01:00
|
|
|
for (auto const& dp : q)
|
2008-07-09 12:45:07 +02:00
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
std::int64_t offset = std::int64_t(static_cast<int>(dp.index))
|
|
|
|
* m_torrent_file->piece_length();
|
|
|
|
file_index_t file = fs.file_index_at_offset(offset);
|
2016-12-15 20:20:08 +01:00
|
|
|
int num_blocks = m_picker->blocks_in_piece(dp.index);
|
|
|
|
piece_picker::block_info const* info = m_picker->blocks_for_piece(dp);
|
2008-07-09 12:45:07 +02:00
|
|
|
for (int k = 0; k < num_blocks; ++k)
|
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(file < fs.end_file());
|
|
|
|
TORRENT_ASSERT(offset == std::int64_t(static_cast<int>(dp.index))
|
|
|
|
* m_torrent_file->piece_length()
|
2010-02-14 02:39:55 +01:00
|
|
|
+ k * block_size());
|
2008-07-13 18:44:14 +02:00
|
|
|
TORRENT_ASSERT(offset < m_torrent_file->total_size());
|
2013-08-12 09:30:57 +02:00
|
|
|
while (offset >= fs.file_offset(file) + fs.file_size(file))
|
2008-07-13 18:44:14 +02:00
|
|
|
{
|
|
|
|
++file;
|
|
|
|
}
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(file < fs.end_file());
|
2008-07-11 16:10:33 +02:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t block = block_size();
|
2008-07-11 16:10:33 +02:00
|
|
|
|
|
|
|
if (info[k].state == piece_picker::block_info::state_none)
|
|
|
|
{
|
2010-02-14 02:39:55 +01:00
|
|
|
offset += block;
|
2008-07-09 12:45:07 +02:00
|
|
|
continue;
|
2008-07-11 16:10:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (info[k].state == piece_picker::block_info::state_requested)
|
|
|
|
{
|
2010-02-14 02:39:55 +01:00
|
|
|
block = 0;
|
2016-09-05 00:24:20 +02:00
|
|
|
torrent_peer* p = info[k].peer;
|
|
|
|
if (p != nullptr && p->connection)
|
2008-07-11 16:10:33 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
peer_connection* peer = static_cast<peer_connection*>(p->connection);
|
2016-09-04 18:31:02 +02:00
|
|
|
auto pbp = peer->downloading_piece_progress();
|
2016-12-22 16:42:33 +01:00
|
|
|
if (pbp.piece_index == dp.index && pbp.block_index == k)
|
2016-09-04 18:31:02 +02:00
|
|
|
block = pbp.bytes_downloaded;
|
2010-02-14 02:39:55 +01:00
|
|
|
TORRENT_ASSERT(block <= block_size());
|
2008-07-11 16:10:33 +02:00
|
|
|
}
|
|
|
|
|
2010-02-14 02:39:55 +01:00
|
|
|
if (block == 0)
|
2008-07-11 16:10:33 +02:00
|
|
|
{
|
2010-02-14 02:39:55 +01:00
|
|
|
offset += block_size();
|
2008-07-11 16:10:33 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-12 09:30:57 +02:00
|
|
|
if (offset + block > fs.file_offset(file) + fs.file_size(file))
|
2008-07-09 12:45:07 +02:00
|
|
|
{
|
2011-02-21 06:24:41 +01:00
|
|
|
int left_over = int(block_size() - block);
|
2008-07-09 12:45:07 +02:00
|
|
|
// split the block on multiple files
|
2010-02-14 02:39:55 +01:00
|
|
|
while (block > 0)
|
2008-07-09 12:45:07 +02:00
|
|
|
{
|
2013-08-12 09:30:57 +02:00
|
|
|
TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file));
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t slice = (std::min)(fs.file_offset(file) + fs.file_size(file) - offset
|
2010-02-14 02:39:55 +01:00
|
|
|
, block);
|
2013-08-12 09:30:57 +02:00
|
|
|
fp[file] += slice;
|
2008-07-09 12:45:07 +02:00
|
|
|
offset += slice;
|
2010-02-14 02:39:55 +01:00
|
|
|
block -= slice;
|
2013-08-12 09:30:57 +02:00
|
|
|
TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file));
|
|
|
|
if (offset == fs.file_offset(file) + fs.file_size(file))
|
2008-07-13 18:44:14 +02:00
|
|
|
{
|
|
|
|
++file;
|
2016-12-22 16:42:33 +01:00
|
|
|
if (file == fs.end_file())
|
2008-07-13 18:44:14 +02:00
|
|
|
{
|
2010-02-14 02:39:55 +01:00
|
|
|
offset += block;
|
2008-07-13 18:44:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-07-09 12:45:07 +02:00
|
|
|
}
|
2008-07-11 16:10:33 +02:00
|
|
|
offset += left_over;
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(offset == std::int64_t(static_cast<int>(dp.index))
|
|
|
|
* m_torrent_file->piece_length()
|
2016-12-15 20:20:08 +01:00
|
|
|
+ (k + 1) * block_size());
|
2008-07-09 12:45:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-08-12 09:30:57 +02:00
|
|
|
fp[file] += block;
|
2010-02-14 02:39:55 +01:00
|
|
|
offset += block_size();
|
2008-07-09 12:45:07 +02:00
|
|
|
}
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(file <= fs.end_file());
|
2008-07-09 12:45:07 +02:00
|
|
|
}
|
|
|
|
}
|
2006-06-12 01:24:36 +02:00
|
|
|
}
|
2015-05-25 23:46:42 +02:00
|
|
|
|
2014-01-23 04:31:36 +01:00
|
|
|
void torrent::new_external_ip()
|
|
|
|
{
|
2014-10-26 08:34:31 +01:00
|
|
|
if (m_peer_list) m_peer_list->clear_peer_prio();
|
2014-01-23 04:31:36 +01:00
|
|
|
}
|
|
|
|
|
2015-10-04 00:15:29 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
bool is_downloading_state(int st)
|
|
|
|
{
|
|
|
|
switch (st)
|
|
|
|
{
|
|
|
|
case torrent_status::checking_files:
|
|
|
|
case torrent_status::allocating:
|
|
|
|
case torrent_status::checking_resume_data:
|
|
|
|
return false;
|
|
|
|
case torrent_status::downloading_metadata:
|
|
|
|
case torrent_status::downloading:
|
|
|
|
case torrent_status::finished:
|
|
|
|
case torrent_status::seeding:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
// unexpected state
|
2016-05-02 18:36:21 +02:00
|
|
|
TORRENT_ASSERT_FAIL_VAL(st);
|
2015-10-04 00:15:29 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-13 17:26:41 +01:00
|
|
|
void torrent::stop_when_ready(bool b)
|
|
|
|
{
|
|
|
|
m_stop_when_ready = b;
|
|
|
|
|
|
|
|
// to avoid race condition, if we're already in a downloading state,
|
|
|
|
// trigger the stop-when-ready logic immediately.
|
|
|
|
if (m_stop_when_ready
|
|
|
|
&& is_downloading_state(m_state))
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("stop_when_ready triggered");
|
|
|
|
#endif
|
|
|
|
auto_managed(false);
|
|
|
|
pause();
|
|
|
|
m_stop_when_ready = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-23 22:51:20 +02:00
|
|
|
void torrent::set_state(torrent_status::state_t const s)
|
2008-07-03 12:05:51 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(s != 0); // this state isn't used anymore
|
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2012-10-06 19:50:06 +02:00
|
|
|
|
2008-10-05 03:44:57 +02:00
|
|
|
if (s == torrent_status::seeding)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2008-10-05 03:44:57 +02:00
|
|
|
TORRENT_ASSERT(is_seed());
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(is_finished());
|
|
|
|
}
|
2008-10-05 03:44:57 +02:00
|
|
|
if (s == torrent_status::finished)
|
|
|
|
TORRENT_ASSERT(is_finished());
|
|
|
|
if (s == torrent_status::downloading && m_state == torrent_status::finished)
|
|
|
|
TORRENT_ASSERT(!is_finished());
|
|
|
|
#endif
|
|
|
|
|
2011-02-21 06:24:41 +01:00
|
|
|
if (int(m_state) == s) return;
|
2013-03-16 23:16:58 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_ses.alerts().should_post<state_changed_alert>())
|
2013-03-16 23:16:58 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<state_changed_alert>(get_handle()
|
2015-08-14 05:52:25 +02:00
|
|
|
, s, static_cast<torrent_status::state_t>(m_state));
|
2013-03-16 23:16:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (s == torrent_status::finished
|
2014-07-06 21:18:00 +02:00
|
|
|
&& alerts().should_post<torrent_finished_alert>())
|
2013-03-16 23:16:58 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_finished_alert>(
|
|
|
|
get_handle());
|
2013-03-16 23:16:58 +01:00
|
|
|
}
|
|
|
|
|
2015-10-04 00:15:29 +02:00
|
|
|
if (m_stop_when_ready
|
|
|
|
&& !is_downloading_state(m_state)
|
|
|
|
&& is_downloading_state(s))
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
debug_log("stop_when_ready triggered");
|
|
|
|
#endif
|
|
|
|
// stop_when_ready is set, and we're transitioning from a downloading
|
|
|
|
// state to a non-downloading state. pause the torrent. Note that
|
|
|
|
// "downloading" is defined broadly to include any state where we
|
|
|
|
// either upload or download (for the purpose of this flag).
|
|
|
|
auto_managed(false);
|
|
|
|
pause();
|
2015-12-13 17:26:41 +01:00
|
|
|
m_stop_when_ready = false;
|
2015-10-04 00:15:29 +02:00
|
|
|
}
|
|
|
|
|
2008-12-01 01:19:05 +01:00
|
|
|
m_state = s;
|
2011-01-29 11:37:21 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("set_state() %d", m_state);
|
|
|
|
#endif
|
|
|
|
|
2016-10-23 04:00:47 +02:00
|
|
|
update_gauge();
|
2014-07-06 21:18:00 +02:00
|
|
|
update_want_peers();
|
2015-05-25 23:46:42 +02:00
|
|
|
update_state_list();
|
2012-06-21 05:51:39 +02:00
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
state_updated();
|
|
|
|
|
2011-01-29 11:37:21 +01:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2016-05-08 00:46:42 +02:00
|
|
|
for (auto& ext : m_extensions)
|
2011-01-29 11:37:21 +01:00
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
ext->on_state(m_state);
|
2011-01-29 11:37:21 +01:00
|
|
|
}
|
|
|
|
#endif
|
2008-07-03 12:05:51 +02:00
|
|
|
}
|
|
|
|
|
2011-05-02 03:45:56 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
void torrent::notify_extension_add_peer(tcp::endpoint const& ip
|
|
|
|
, int src, int flags)
|
|
|
|
{
|
2016-05-08 00:46:42 +02:00
|
|
|
for (auto& ext : m_extensions)
|
2011-05-02 03:45:56 +02:00
|
|
|
{
|
2016-10-23 04:00:47 +02:00
|
|
|
ext->on_add_peer(ip, src, flags);
|
2011-05-02 03:45:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
void torrent::state_updated()
|
|
|
|
{
|
2015-05-25 23:46:42 +02:00
|
|
|
// if this fails, this function is probably called
|
2012-03-02 00:18:32 +01:00
|
|
|
// from within the torrent constructor, which it
|
|
|
|
// shouldn't be. Whichever function ends up calling
|
|
|
|
// this should probably be moved to torrent::start()
|
|
|
|
TORRENT_ASSERT(shared_from_this());
|
|
|
|
|
2012-10-06 16:31:14 +02:00
|
|
|
// we can't call state_updated() while the session
|
|
|
|
// is building the status update alert
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(!m_ses.is_posting_torrent_updates());
|
2012-10-06 16:31:14 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// we're not subscribing to this torrent, don't add it
|
2012-01-20 06:40:32 +01:00
|
|
|
if (!m_state_subscription) return;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
std::vector<torrent*>& list = m_ses.torrent_list(aux::session_interface::torrent_state_updates);
|
|
|
|
|
|
|
|
// if it has already been updated this round, no need to
|
|
|
|
// add it to the list twice
|
|
|
|
if (m_links[aux::session_interface::torrent_state_updates].in_list())
|
2012-01-20 06:40:32 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
|
|
|
|
TORRENT_ASSERT(find(list.begin(), list.end(), this) != list.end());
|
2013-12-18 08:15:16 +01:00
|
|
|
#endif
|
2012-01-20 06:40:32 +01:00
|
|
|
return;
|
|
|
|
}
|
2011-11-15 03:34:00 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
|
|
|
|
TORRENT_ASSERT(find(list.begin(), list.end(), this) == list.end());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_links[aux::session_interface::torrent_state_updates].insert(list, this);
|
2011-11-15 03:34:00 +01:00
|
|
|
}
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
void torrent::status(torrent_status* st, std::uint32_t flags)
|
2003-10-26 18:35:23 +01:00
|
|
|
{
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point now = aux::time_now();
|
2008-05-19 06:06:25 +02:00
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->handle = get_handle();
|
2011-04-19 10:21:09 +02:00
|
|
|
st->info_hash = info_hash();
|
2016-10-15 19:32:59 +02:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
st->is_loaded = true;
|
|
|
|
#endif
|
2003-10-30 00:28:09 +01:00
|
|
|
|
2013-03-03 00:17:48 +01:00
|
|
|
if (flags & torrent_handle::query_name)
|
|
|
|
st->name = name();
|
|
|
|
|
|
|
|
if (flags & torrent_handle::query_save_path)
|
|
|
|
st->save_path = save_path();
|
|
|
|
|
|
|
|
if (flags & torrent_handle::query_torrent_file)
|
|
|
|
st->torrent_file = m_torrent_file;
|
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->has_incoming = m_has_incoming;
|
2015-11-03 06:12:30 +01:00
|
|
|
st->errc = m_error;
|
|
|
|
st->error_file = m_error_file;
|
|
|
|
|
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_error) st->error = convert_from_native(m_error.message())
|
|
|
|
+ ": " + resolve_filename(m_error_file);
|
2015-11-03 06:12:30 +01:00
|
|
|
#endif
|
2011-02-01 10:48:28 +01:00
|
|
|
st->seed_mode = m_seed_mode;
|
2014-01-01 03:04:26 +01:00
|
|
|
st->moving_storage = m_moving_storage;
|
2008-05-20 09:57:44 +02:00
|
|
|
|
2015-09-19 07:54:47 +02:00
|
|
|
st->announcing_to_trackers = m_announce_to_trackers;
|
|
|
|
st->announcing_to_lsd = m_announce_to_lsd;
|
|
|
|
st->announcing_to_dht = m_announce_to_dht;
|
2015-10-04 00:15:29 +02:00
|
|
|
st->stop_when_ready = m_stop_when_ready;
|
2015-09-19 07:54:47 +02:00
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->added_time = m_added_time;
|
|
|
|
st->completed_time = m_completed_time;
|
2010-03-09 04:21:35 +01:00
|
|
|
|
2016-11-10 23:08:32 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2016-06-18 20:01:38 +02:00
|
|
|
st->last_scrape = m_last_scrape == (std::numeric_limits<std::int16_t>::min)() ? -1
|
2016-11-25 17:17:25 +01:00
|
|
|
: clamped_subtract_u16(m_ses.session_time(), m_last_scrape);
|
2016-11-10 23:08:32 +01:00
|
|
|
#endif
|
2014-08-27 08:36:28 +02:00
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->share_mode = m_share_mode;
|
|
|
|
st->upload_mode = m_upload_mode;
|
|
|
|
st->up_bandwidth_queue = 0;
|
|
|
|
st->down_bandwidth_queue = 0;
|
2016-08-26 18:37:23 +02:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2014-07-06 21:18:00 +02:00
|
|
|
int priority = 0;
|
|
|
|
for (int i = 0; i < num_classes(); ++i)
|
|
|
|
{
|
|
|
|
int const* prio = m_ses.peer_classes().at(class_at(i))->priority;
|
|
|
|
if (priority < prio[peer_connection::upload_channel])
|
|
|
|
priority = prio[peer_connection::upload_channel];
|
|
|
|
if (priority < prio[peer_connection::download_channel])
|
|
|
|
priority = prio[peer_connection::download_channel];
|
|
|
|
}
|
|
|
|
st->priority = priority;
|
2016-08-26 18:37:23 +02:00
|
|
|
#endif
|
2008-01-17 18:40:46 +01:00
|
|
|
|
2012-05-07 00:46:21 +02:00
|
|
|
st->num_peers = int(m_connections.size()) - m_num_connecting;
|
2007-11-20 23:46:27 +01:00
|
|
|
|
2014-10-26 08:34:31 +01:00
|
|
|
st->list_peers = m_peer_list ? m_peer_list->num_peers() : 0;
|
|
|
|
st->list_seeds = m_peer_list ? m_peer_list->num_seeds() : 0;
|
|
|
|
st->connect_candidates = m_peer_list ? m_peer_list->num_connect_candidates() : 0;
|
2011-02-01 10:48:28 +01:00
|
|
|
st->seed_rank = seed_rank(settings());
|
2005-11-05 11:56:47 +01:00
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->all_time_upload = m_total_uploaded;
|
|
|
|
st->all_time_download = m_total_downloaded;
|
2008-04-16 08:31:05 +02:00
|
|
|
|
2010-07-08 21:29:38 +02:00
|
|
|
// activity time
|
2016-11-10 23:08:32 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2014-08-27 08:36:28 +02:00
|
|
|
st->finished_time = finished_time();
|
|
|
|
st->active_time = active_time();
|
|
|
|
st->seeding_time = seeding_time();
|
2016-11-10 23:08:32 +01:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
st->time_since_upload = m_last_upload == (std::numeric_limits<std::int16_t>::min)() ? -1
|
2016-11-25 17:17:25 +01:00
|
|
|
: clamped_subtract_u16(m_ses.session_time(), m_last_upload);
|
2016-06-18 20:01:38 +02:00
|
|
|
st->time_since_download = m_last_download == (std::numeric_limits<std::int16_t>::min)() ? -1
|
2016-11-25 17:17:25 +01:00
|
|
|
: clamped_subtract_u16(m_ses.session_time(), m_last_download);
|
2016-11-10 23:08:32 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
st->finished_duration = seconds{finished_time()};
|
|
|
|
st->active_duration = seconds{active_time()};
|
|
|
|
st->seeding_duration = seconds{seeding_time()};
|
|
|
|
|
|
|
|
st->last_upload = m_ses.session_start_time() + seconds(m_last_upload);
|
|
|
|
st->last_download = m_ses.session_start_time() + seconds(m_last_download);
|
2011-02-01 10:48:28 +01:00
|
|
|
|
2015-08-14 05:52:25 +02:00
|
|
|
st->storage_mode = static_cast<storage_mode_t>(m_storage_mode);
|
2011-02-01 10:48:28 +01:00
|
|
|
|
|
|
|
st->num_complete = (m_complete == 0xffffff) ? -1 : m_complete;
|
|
|
|
st->num_incomplete = (m_incomplete == 0xffffff) ? -1 : m_incomplete;
|
|
|
|
st->paused = is_torrent_paused();
|
|
|
|
st->auto_managed = m_auto_managed;
|
|
|
|
st->sequential_download = m_sequential_download;
|
|
|
|
st->is_seeding = is_seed();
|
|
|
|
st->is_finished = is_finished();
|
2012-03-01 10:01:48 +01:00
|
|
|
st->super_seeding = m_super_seeding;
|
2011-02-01 10:48:28 +01:00
|
|
|
st->has_metadata = valid_metadata();
|
2015-11-20 05:37:45 +01:00
|
|
|
bytes_done(*st, (flags & torrent_handle::query_accurate_download_counters) != 0);
|
2011-02-01 10:48:28 +01:00
|
|
|
TORRENT_ASSERT(st->total_wanted_done >= 0);
|
|
|
|
TORRENT_ASSERT(st->total_done >= st->total_wanted_done);
|
2003-10-31 13:07:07 +01:00
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
// payload transfer
|
2011-02-01 10:48:28 +01:00
|
|
|
st->total_payload_download = m_stat.total_payload_download();
|
|
|
|
st->total_payload_upload = m_stat.total_payload_upload();
|
2003-12-22 08:14:35 +01:00
|
|
|
|
|
|
|
// total transfer
|
2011-02-01 10:48:28 +01:00
|
|
|
st->total_download = m_stat.total_payload_download()
|
2003-12-22 08:14:35 +01:00
|
|
|
+ m_stat.total_protocol_download();
|
2011-02-01 10:48:28 +01:00
|
|
|
st->total_upload = m_stat.total_payload_upload()
|
2003-12-22 08:14:35 +01:00
|
|
|
+ m_stat.total_protocol_upload();
|
|
|
|
|
2004-04-18 14:28:02 +02:00
|
|
|
// failed bytes
|
2011-02-01 10:48:28 +01:00
|
|
|
st->total_failed_bytes = m_total_failed_bytes;
|
|
|
|
st->total_redundant_bytes = m_total_redundant_bytes;
|
2004-04-18 14:28:02 +02:00
|
|
|
|
2003-12-22 08:14:35 +01:00
|
|
|
// transfer rate
|
2011-02-01 10:48:28 +01:00
|
|
|
st->download_rate = m_stat.download_rate();
|
|
|
|
st->upload_rate = m_stat.upload_rate();
|
|
|
|
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
|
|
|
|
2008-12-27 08:22:57 +01:00
|
|
|
if (m_waiting_tracker && !is_paused())
|
2015-04-25 03:40:39 +02:00
|
|
|
st->next_announce = next_announce() - now;
|
2008-12-27 08:22:57 +01:00
|
|
|
else
|
2015-04-25 03:40:39 +02:00
|
|
|
st->next_announce = seconds(0);
|
2008-12-27 08:22:57 +01:00
|
|
|
|
2015-04-25 03:40:39 +02:00
|
|
|
if (st->next_announce.count() < 0)
|
|
|
|
st->next_announce = seconds(0);
|
2007-04-05 00:27:36 +02:00
|
|
|
|
2015-04-26 15:38:56 +02:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2015-04-25 03:40:39 +02:00
|
|
|
st->announce_interval = seconds(0);
|
|
|
|
#endif
|
2004-01-17 21:04:19 +01:00
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->current_tracker.clear();
|
2005-03-11 18:21:56 +01:00
|
|
|
if (m_last_working_tracker >= 0)
|
|
|
|
{
|
2009-06-10 10:42:05 +02:00
|
|
|
TORRENT_ASSERT(m_last_working_tracker < int(m_trackers.size()));
|
2015-04-25 03:40:39 +02:00
|
|
|
const int i = m_last_working_tracker;
|
|
|
|
st->current_tracker = m_trackers[i].url;
|
2005-03-11 18:21:56 +01:00
|
|
|
}
|
2008-11-29 09:38:40 +01:00
|
|
|
else
|
2008-11-26 02:31:54 +01:00
|
|
|
{
|
2016-09-23 22:49:39 +02:00
|
|
|
for (auto const& t : m_trackers)
|
2010-03-04 20:15:23 +01:00
|
|
|
{
|
2016-09-23 22:49:39 +02:00
|
|
|
if (t.updating) continue;
|
|
|
|
if (!t.verified) continue;
|
|
|
|
st->current_tracker = t.url;
|
2010-03-04 20:15:23 +01:00
|
|
|
break;
|
|
|
|
}
|
2008-11-26 02:31:54 +01:00
|
|
|
}
|
2005-03-11 18:21:56 +01:00
|
|
|
|
2011-08-07 09:19:18 +02:00
|
|
|
if ((flags & torrent_handle::query_verified_pieces))
|
|
|
|
{
|
|
|
|
st->verified_pieces = m_verified;
|
|
|
|
}
|
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->num_uploads = m_num_uploads;
|
2016-12-15 20:20:08 +01:00
|
|
|
st->uploads_limit = m_max_uploads == (1 << 24) - 1 ? -1 : m_max_uploads;
|
2011-02-01 10:48:28 +01:00
|
|
|
st->num_connections = int(m_connections.size());
|
2016-12-15 20:20:08 +01:00
|
|
|
st->connections_limit = m_max_connections == (1 << 24) - 1 ? -1 : m_max_connections;
|
2004-06-14 01:30:42 +02:00
|
|
|
// if we don't have any metadata, stop here
|
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->queue_position = queue_position();
|
|
|
|
st->need_save_resume = need_save_resume_data();
|
2011-02-27 18:26:57 +01:00
|
|
|
st->ip_filter_applies = m_apply_ip_filter;
|
2010-11-06 19:04:07 +01:00
|
|
|
|
2015-08-14 05:52:25 +02:00
|
|
|
st->state = static_cast<torrent_status::state_t>(m_state);
|
2008-03-08 07:06:31 +01:00
|
|
|
|
2016-06-18 14:31:07 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2014-07-06 21:18:00 +02:00
|
|
|
if (st->state == torrent_status::finished
|
|
|
|
|| st->state == torrent_status::seeding)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(st->is_finished);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
if (!valid_metadata())
|
|
|
|
{
|
2011-02-01 10:48:28 +01:00
|
|
|
st->state = torrent_status::downloading_metadata;
|
|
|
|
st->progress_ppm = m_progress_ppm;
|
2009-07-19 06:59:27 +02:00
|
|
|
#if !TORRENT_NO_FPU
|
2011-02-01 10:48:28 +01:00
|
|
|
st->progress = m_progress_ppm / 1000000.f;
|
2009-07-19 06:59:27 +02:00
|
|
|
#endif
|
2011-02-01 10:48:28 +01:00
|
|
|
st->block_size = 0;
|
|
|
|
return;
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
|
|
|
|
2011-02-01 10:48:28 +01:00
|
|
|
st->block_size = block_size();
|
2006-04-25 23:04:48 +02:00
|
|
|
|
2008-03-08 07:06:31 +01:00
|
|
|
if (m_state == torrent_status::checking_files)
|
2009-07-19 06:59:27 +02:00
|
|
|
{
|
2011-02-01 10:48:28 +01:00
|
|
|
st->progress_ppm = m_progress_ppm;
|
2009-07-19 06:59:27 +02:00
|
|
|
#if !TORRENT_NO_FPU
|
2011-02-01 10:48:28 +01:00
|
|
|
st->progress = m_progress_ppm / 1000000.f;
|
2009-07-19 06:59:27 +02:00
|
|
|
#endif
|
|
|
|
}
|
2011-02-01 10:48:28 +01:00
|
|
|
else if (st->total_wanted == 0)
|
2009-07-19 06:59:27 +02:00
|
|
|
{
|
2011-02-01 10:48:28 +01:00
|
|
|
st->progress_ppm = 1000000;
|
|
|
|
st->progress = 1.f;
|
2009-07-19 06:59:27 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-12-05 14:39:53 +01:00
|
|
|
st->progress_ppm = int(st->total_wanted_done * 1000000
|
|
|
|
/ st->total_wanted);
|
2009-07-19 06:59:27 +02:00
|
|
|
#if !TORRENT_NO_FPU
|
2011-02-01 10:48:28 +01:00
|
|
|
st->progress = st->progress_ppm / 1000000.f;
|
2009-07-19 06:59:27 +02:00
|
|
|
#endif
|
|
|
|
}
|
2004-01-15 02:01:09 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int num_pieces = m_torrent_file->num_pieces();
|
2013-01-21 17:01:48 +01:00
|
|
|
if (has_picker() && (flags & torrent_handle::query_pieces))
|
2008-06-07 04:58:28 +02:00
|
|
|
{
|
2011-02-01 10:48:28 +01:00
|
|
|
st->pieces.resize(num_pieces, false);
|
2016-12-22 16:42:33 +01:00
|
|
|
for (piece_index_t i(0); i < piece_index_t(num_pieces); ++i)
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_picker->has_piece_passed(i)) st->pieces.set_bit(i);
|
2008-06-07 04:58:28 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
else if (m_have_all)
|
2011-07-26 03:52:51 +02:00
|
|
|
{
|
|
|
|
st->pieces.resize(num_pieces, true);
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
st->pieces.resize(num_pieces, false);
|
|
|
|
}
|
2011-02-01 10:48:28 +01:00
|
|
|
st->num_pieces = num_have();
|
|
|
|
st->num_seeds = num_seeds();
|
2010-03-04 20:15:23 +01:00
|
|
|
if ((flags & torrent_handle::query_distributed_copies) && m_picker.get())
|
2009-07-19 06:59:27 +02:00
|
|
|
{
|
2016-06-20 17:32:06 +02:00
|
|
|
std::tie(st->distributed_full_copies, st->distributed_fraction) =
|
2009-07-19 06:59:27 +02:00
|
|
|
m_picker->distributed_copies();
|
|
|
|
#if TORRENT_NO_FPU
|
2011-02-01 10:48:28 +01:00
|
|
|
st->distributed_copies = -1.f;
|
2009-07-19 06:59:27 +02:00
|
|
|
#else
|
2011-02-01 10:48:28 +01:00
|
|
|
st->distributed_copies = st->distributed_full_copies
|
|
|
|
+ float(st->distributed_fraction) / 1000;
|
2009-07-19 06:59:27 +02:00
|
|
|
#endif
|
|
|
|
}
|
2006-12-04 13:20:34 +01:00
|
|
|
else
|
2009-07-19 06:59:27 +02:00
|
|
|
{
|
2011-02-01 10:48:28 +01:00
|
|
|
st->distributed_full_copies = -1;
|
|
|
|
st->distributed_fraction = -1;
|
|
|
|
st->distributed_copies = -1.f;
|
2009-07-19 06:59:27 +02:00
|
|
|
}
|
2010-03-19 19:39:51 +01:00
|
|
|
|
2012-05-07 00:46:21 +02:00
|
|
|
st->last_seen_complete = m_swarm_last_seen_complete;
|
2003-10-26 18:35:23 +01:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:27:38 +02:00
|
|
|
void torrent::add_redundant_bytes(int b, waste_reason reason)
|
2008-07-11 09:30:04 +02:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-07-11 09:30:04 +02:00
|
|
|
TORRENT_ASSERT(b > 0);
|
|
|
|
m_total_redundant_bytes += b;
|
2014-07-29 07:59:00 +02:00
|
|
|
|
|
|
|
TORRENT_ASSERT(b > 0);
|
2016-09-02 14:27:38 +02:00
|
|
|
TORRENT_ASSERT(static_cast<int>(reason) >= 0);
|
|
|
|
TORRENT_ASSERT(static_cast<int>(reason) < static_cast<int>(waste_reason::max));
|
2014-07-29 07:59:00 +02:00
|
|
|
m_stats_counters.inc_stats_counter(counters::recv_redundant_bytes, b);
|
2016-09-02 14:27:38 +02:00
|
|
|
m_stats_counters.inc_stats_counter(counters::waste_piece_timed_out + static_cast<int>(reason), b);
|
2008-07-11 09:30:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void torrent::add_failed_bytes(int b)
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-07-11 09:30:04 +02:00
|
|
|
TORRENT_ASSERT(b > 0);
|
|
|
|
m_total_failed_bytes += b;
|
2014-07-29 07:59:00 +02:00
|
|
|
m_stats_counters.inc_stats_counter(counters::recv_failed_bytes, b);
|
2008-07-11 09:30:04 +02:00
|
|
|
}
|
|
|
|
|
2004-08-05 15:56:26 +02:00
|
|
|
int torrent::num_seeds() const
|
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2006-04-25 23:04:48 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2014-09-24 11:03:57 +02:00
|
|
|
return m_num_seeds;
|
2004-09-12 15:53:00 +02:00
|
|
|
}
|
|
|
|
|
2014-09-24 02:02:00 +02:00
|
|
|
int torrent::num_downloaders() const
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(is_single_thread());
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2014-10-06 07:02:00 +02:00
|
|
|
return (std::max)(0, int(m_connections.size()) - m_num_seeds - m_num_connecting);
|
2014-09-24 02:02:00 +02:00
|
|
|
}
|
|
|
|
|
2007-11-20 23:46:27 +01:00
|
|
|
void torrent::tracker_request_error(tracker_request const& r
|
2015-02-21 23:45:45 +01:00
|
|
|
, int response_code, error_code const& ec, std::string const& msg
|
2009-12-21 10:47:32 +01:00
|
|
|
, int retry_interval)
|
2003-12-22 08:14:35 +01:00
|
|
|
{
|
2014-09-22 19:41:06 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-03-31 00:00:26 +02:00
|
|
|
|
2006-05-28 21:03:54 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
debug_log("*** tracker error: (%d) %s %s", ec.value()
|
|
|
|
, ec.message().c_str(), msg.c_str());
|
|
|
|
}
|
2003-12-22 08:14:35 +01:00
|
|
|
#endif
|
2015-06-27 23:30:00 +02:00
|
|
|
if (0 == (r.kind & tracker_request::scrape_request))
|
2003-12-22 08:14:35 +01:00
|
|
|
{
|
2016-01-17 03:24:04 +01:00
|
|
|
// announce request
|
2016-10-07 21:36:40 +02:00
|
|
|
announce_entry* ae = find_tracker(r.url);
|
2008-11-29 09:38:40 +01:00
|
|
|
if (ae)
|
2007-11-20 23:46:27 +01:00
|
|
|
{
|
2016-08-21 01:46:55 +02:00
|
|
|
ae->failed(seconds(settings().get_int(settings_pack::tracker_backoff))
|
|
|
|
, retry_interval);
|
2010-04-13 06:37:39 +02:00
|
|
|
ae->last_error = ec;
|
|
|
|
ae->message = msg;
|
2016-12-05 14:39:53 +01:00
|
|
|
int tracker_index = int(ae - &m_trackers[0]);
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2012-09-28 01:04:51 +02:00
|
|
|
debug_log("*** increment tracker fail count [%d]", ae->fails);
|
2011-08-18 01:01:35 +02:00
|
|
|
#endif
|
2013-01-21 11:16:34 +01:00
|
|
|
// never talk to this tracker again
|
|
|
|
if (response_code == 410) ae->fail_limit = 1;
|
|
|
|
|
2008-11-29 09:38:40 +01:00
|
|
|
deprioritize_tracker(tracker_index);
|
2007-11-20 23:46:27 +01:00
|
|
|
}
|
2016-01-17 03:24:04 +01:00
|
|
|
if (m_ses.alerts().should_post<tracker_error_alert>()
|
|
|
|
|| r.triggered_manually)
|
2008-11-27 21:51:59 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<tracker_error_alert>(get_handle()
|
2016-08-21 01:46:55 +02:00
|
|
|
, ae ? ae->fails : 0, response_code, r.url, ec, msg);
|
2008-11-27 21:51:59 +01:00
|
|
|
}
|
2008-07-06 14:22:56 +02:00
|
|
|
}
|
2016-01-17 03:24:04 +01:00
|
|
|
else
|
2008-07-06 14:22:56 +02:00
|
|
|
{
|
2016-01-17 03:24:04 +01:00
|
|
|
// scrape request
|
2013-01-21 11:16:34 +01:00
|
|
|
if (response_code == 410)
|
|
|
|
{
|
|
|
|
// never talk to this tracker again
|
2016-10-07 21:36:40 +02:00
|
|
|
announce_entry* ae = find_tracker(r.url);
|
2013-01-21 11:16:34 +01:00
|
|
|
if (ae) ae->fail_limit = 1;
|
|
|
|
}
|
|
|
|
|
2016-01-17 03:24:04 +01:00
|
|
|
// if this was triggered manually we need to post this unconditionally,
|
|
|
|
// since the client expects a response from its action, regardless of
|
|
|
|
// whether all tracker events have been enabled by the alert mask
|
|
|
|
if (m_ses.alerts().should_post<scrape_failed_alert>()
|
|
|
|
|| r.triggered_manually)
|
2007-11-20 23:46:27 +01:00
|
|
|
{
|
2015-04-03 22:15:48 +02:00
|
|
|
m_ses.alerts().emplace_alert<scrape_failed_alert>(get_handle(), r.url, ec);
|
2007-11-20 23:46:27 +01:00
|
|
|
}
|
2003-12-22 08:14:35 +01:00
|
|
|
}
|
2010-05-17 04:06:30 +02:00
|
|
|
// announce to the next working tracker
|
2011-09-13 04:38:50 +02:00
|
|
|
if ((!m_abort && !is_paused()) || r.event == tracker_request::stopped)
|
2011-09-13 04:23:18 +02:00
|
|
|
announce_with_tracker(r.event);
|
2015-03-12 05:34:54 +01:00
|
|
|
update_tracker_timer(aux::time_now());
|
2003-12-22 08:14:35 +01:00
|
|
|
}
|
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
bool torrent::should_log() const
|
|
|
|
{
|
|
|
|
return alerts().should_post<torrent_log_alert>();
|
|
|
|
}
|
|
|
|
|
2015-05-10 07:11:51 +02:00
|
|
|
TORRENT_FORMAT(2,3)
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent::debug_log(char const* fmt, ...) const
|
2003-11-20 20:58:29 +01:00
|
|
|
{
|
2014-12-09 10:08:26 +01:00
|
|
|
if (!alerts().should_post<torrent_log_alert>()) return;
|
2012-09-28 01:04:51 +02:00
|
|
|
|
2015-07-06 01:32:13 +02:00
|
|
|
va_list v;
|
2012-09-28 01:04:51 +02:00
|
|
|
va_start(v, fmt);
|
2015-04-03 22:15:48 +02:00
|
|
|
alerts().emplace_alert<torrent_log_alert>(
|
2016-06-29 00:52:51 +02:00
|
|
|
const_cast<torrent*>(this)->get_handle(), fmt, v);
|
|
|
|
va_end(v);
|
2003-11-20 20:58:29 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-06-15 14:54:35 +02:00
|
|
|
}
|