diff --git a/ChangeLog b/ChangeLog index a92695bf8..54d6be462 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ + * added new read_resume_data() function, initializing add_torrent_params + * removed deprecated fields from add_torrent_params * improved support for bind-to-device * deprecated ssl_listen, SSL sockets are specified in listen_interfaces now * improved support for listening on multiple sockets and interfaces diff --git a/Jamfile b/Jamfile index e29f87d29..2db64f262 100644 --- a/Jamfile +++ b/Jamfile @@ -606,6 +606,7 @@ SOURCES = proxy_base puff random + read_resume_data receive_buffer resolve_links rss diff --git a/include/libtorrent/add_torrent_params.hpp b/include/libtorrent/add_torrent_params.hpp index 00d1f8c7b..ef10e6643 100644 --- a/include/libtorrent/add_torrent_params.hpp +++ b/include/libtorrent/add_torrent_params.hpp @@ -35,7 +35,10 @@ POSSIBILITY OF SUCH DAMAGE. #include #include + +#include "libtorrent/aux_/disable_warnings_push.hpp" #include +#include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/storage_defs.hpp" #include "libtorrent/peer_id.hpp" // sha1_hash @@ -75,53 +78,26 @@ namespace libtorrent // data for the torrent. For more information, see the ``storage`` field. add_torrent_params(storage_constructor_type sc = default_storage_constructor) : version(LIBTORRENT_VERSION_NUM) -#ifndef TORRENT_NO_DEPRECATE - , tracker_url(0) -#endif , storage_mode(storage_mode_sparse) , storage(sc) , userdata(0) -#ifndef TORRENT_NO_DEPRECATE - , flags(flag_ignore_flags | default_flags) -#else , flags(default_flags) -#endif , max_uploads(-1) , max_connections(-1) , upload_limit(-1) , download_limit(-1) -#ifndef TORRENT_NO_DEPRECATE - , seed_mode(false) - , override_resume_data(false) - , upload_mode(false) - , share_mode(false) - , apply_ip_filter(true) - , paused(true) - , auto_managed(true) - , duplicate_is_error(false) - , merge_resume_trackers(false) -#endif - { - } - -#ifndef TORRENT_NO_DEPRECATE - void update_flags() const - { - if (flags != (flag_ignore_flags | default_flags)) return; - - boost::uint64_t& f = const_cast(flags); - f = flag_update_subscribe; - if (seed_mode) f |= flag_seed_mode; - if (override_resume_data) f |= flag_override_resume_data; - if (upload_mode) f |= flag_upload_mode; - if (share_mode) f |= flag_share_mode; - if (apply_ip_filter) f |= flag_apply_ip_filter; - if (paused) f |= flag_paused; - if (auto_managed) f |= flag_auto_managed; - if (duplicate_is_error) f |= flag_duplicate_is_error; - if (merge_resume_trackers) f |= flag_merge_resume_trackers; - } -#endif + , total_uploaded(0) + , total_downloaded(0) + , active_time(0) + , finished_time(0) + , seeding_time(0) + , added_time(0) + , completed_time(0) + , last_seen_complete(0) + , num_complete(-1) + , num_incomplete(-1) + , num_downloaded(-1) + {} // values for the ``flags`` field enum flags_t @@ -230,6 +206,8 @@ namespace libtorrent // replaced by any trackers in the resume data. The default behavior is // to have the resume data override the .torrent file _and_ the // trackers added in add_torrent_params. +//#error this needs to change slightly. Should trackers be extracted from the \ + .torrent file on the client side? when in that case? flag_merge_resume_trackers = 0x100, // on by default and means that this torrent will be part of state @@ -247,10 +225,12 @@ namespace libtorrent // the torrent handle immediately after adding it. flag_sequential_download = 0x800, +#ifndef TORRENT_NO_DEPRECATE // if this flag is set, the save path from the resume data file, if // present, is honored. This defaults to not being set, in which // case the save_path specified in add_torrent_params is always used. flag_use_resume_save_path = 0x1000, +#endif // indicates that this torrent should never be unloaded from RAM, even // if unloading torrents are allowed in general. Setting this makes @@ -265,6 +245,8 @@ namespace libtorrent // add_torrent_params are also replaced. The default behavior is to // have any web seeds in the resume data take precedence over whatever // is passed in here as well as the .torrent file. +//#error this needs to change slightly. Should web seeds be extracted from the \ + .torrent file on the client side? when in that case? flag_merge_resume_http_seeds = 0x2000, // the stop when ready flag. Setting this flag is equivalent to calling @@ -273,11 +255,7 @@ namespace libtorrent flag_stop_when_ready = 0x4000, // internal - default_flags = flag_pinned | flag_update_subscribe | flag_auto_managed | flag_paused | flag_apply_ip_filter - -#ifndef TORRENT_NO_DEPRECATE - , flag_ignore_flags = 0x80000000 -#endif + default_flags = flag_merge_resume_http_seeds | flag_merge_resume_trackers | flag_pinned | flag_update_subscribe | flag_auto_managed | flag_paused | flag_apply_ip_filter }; // filled in by the constructor and should be left untouched. It is used @@ -287,13 +265,17 @@ namespace libtorrent // info_hash is set, this is required to be initialized. boost::shared_ptr ti; -#ifndef TORRENT_NO_DEPRECATE - char const* tracker_url; -#endif // If the torrent doesn't have a tracker, but relies on the DHT to find // peers, the ``trackers`` can specify tracker URLs for the torrent. std::vector trackers; + // the tiers the URLs in ``trackers`` belong to. Trackers belonging to + // different tiers may be treated differently, as defined by the multi + // tracker extension. This is optional, if not specified trackers are + // assumed to be part of tier 0, or whichever the last tier was as + // iterating over the trackers. + std::vector tracker_tiers; + // url seeds to be added to the torrent (`BEP 17`_). std::vector url_seeds; @@ -302,10 +284,7 @@ namespace libtorrent std::vector > dht_nodes; std::string name; - // the path where the torrent is or will be stored. Note that this may - // also be stored in resume data. If you want the save path saved in - // the resume data to be used, you need to set the - // flag_use_resume_save_path flag. + // the path where the torrent is or will be stored. // // .. note:: // On windows this path (and other paths) are interpreted as UNC @@ -313,6 +292,9 @@ namespace libtorrent // and may not contain the special directories "." or "..". std::string save_path; +#error do we still need this? what's left in the resume data that isn't parsed \ + out into add_torrent_params? should it be moved to add_torrent_params? + // The optional parameter, ``resume_data`` can be given if up to date // fast-resume data is available. The fast-resume data can be acquired // from a running torrent by calling save_resume_data() on @@ -398,18 +380,45 @@ namespace libtorrent int upload_limit; int download_limit; -#ifndef TORRENT_NO_DEPRECATE - bool seed_mode; - bool override_resume_data; - bool upload_mode; - bool share_mode; - bool apply_ip_filter; - bool paused; - bool auto_managed; - bool duplicate_is_error; - bool merge_resume_trackers; -#endif + // the total number of bytes uploaded and downloaded by this torrent so + // far. + boost::int64_t total_uploaded; + boost::int64_t total_downloaded; + // the numeber of seconds this torrent has spent in started, finished and + // seeding state so far, respectively. + int active_time; + int finished_time; + int seeding_time; + + // if set to a non-zero value, this is the posix time of when this torrent + // was first added, including previous runs/sessions. If set to zero, the + // internal added_time will be set to the time of when add_torrent() is + // called. + time_t added_time; + time_t completed_time; + + // if set to non-zero, initializes the time (expressed in posix time) when + // we last saw a seed or peers that together formed a complete copy of the + // torrent. If left set to zero, the internal counterpart to this field + // will be updated when we see a seed or a distributed copies >= 1.0. + time_t last_seen_complete; + + // these field can be used to initialize the torrent's cached scrape data. + // The scrape data is high level metadata about the current state of the + // swarm, as returned by the tracker (either when announcing to it or by + // sending a specific scrape request). ``num_complete`` is the number of + // peers in the swarm that are seeds, or have every piece in the torrent. + // ``num_inomplete`` is the number of peers in the swarm that do not have + // every piece. ``num_downloaded`` is the number of times the torrent has + // been downloaded (not initiated, but the number of times a download has + // completed). + // + // Leaving any of these values set to -1 indicates we don't know, or we + // have not received any scrape data. + int num_complete; + int num_incomplete; + int num_downloaded; }; } diff --git a/include/libtorrent/bdecode.hpp b/include/libtorrent/bdecode.hpp index 34dea075a..975135088 100644 --- a/include/libtorrent/bdecode.hpp +++ b/include/libtorrent/bdecode.hpp @@ -411,7 +411,7 @@ TORRENT_EXPORT std::string print_entry(bdecode_node const& e // produced by this function does not copy any data out of the buffer, but // simply produces references back into it. TORRENT_EXPORT int bdecode(char const* start, char const* end, bdecode_node& ret - , error_code& ec, int* error_pos = 0, int depth_limit = 100 + , error_code& ec, int* error_pos = NULL, int depth_limit = 100 , int token_limit = 1000000); } diff --git a/include/libtorrent/read_resume_data.hpp b/include/libtorrent/read_resume_data.hpp new file mode 100644 index 000000000..7784f9279 --- /dev/null +++ b/include/libtorrent/read_resume_data.hpp @@ -0,0 +1,55 @@ +/* + +Copyright (c) 2015, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_READ_RESUME_DATA_HPP_INCLUDE +#define TORRENT_READ_RESUME_DATA_HPP_INCLUDE + +#include "libtorrent/error_code.hpp" +#include "libtorrent/export.hpp" + +namespace libtorrent +{ + struct add_torrent_params; + struct bdecode_node; + + // these functions are used to parse resume data and populate the appropriate + // fields in an add_torrent_param object. This object can then be used to add + // the actual torrent_info object to and pass to session::add_torrent() or + // session::async_add_torrent() + TORRENT_EXPORT add_torrent_params read_resume_data(bdecode_node const& rd + , error_code& ec); + TORRENT_EXPORT add_torrent_params read_resume_data(char const* buffer + , int size, error_code& ec); +} + +#endif + diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 1eb88126a..47452155a 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -989,7 +989,6 @@ namespace libtorrent torrent_handle get_handle(); void write_resume_data(entry& rd) const; - void read_resume_data(bdecode_node const& rd); void seen_complete() { m_last_seen_complete = time(0); } int time_since_complete() const { return int(time(0) - m_last_seen_complete); } @@ -1306,6 +1305,7 @@ namespace libtorrent error_code m_error; // used if there is any resume data +#error do we still need this? boost::scoped_ptr m_resume_data; // if the torrent is started without metadata, it may @@ -1494,9 +1494,7 @@ namespace libtorrent // torrent. bool m_super_seeding:1; - // this is set when we don't want to load seed_mode, - // paused or auto_managed from the resume data - const bool m_override_resume_data:1; +#error a 1 bit hole here #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES @@ -1689,14 +1687,7 @@ namespace libtorrent // quarantine bool m_pending_active_change:1; - // if this is set, accept the save path saved in the resume data, if - // present - bool m_use_resume_save_path:1; - - // if set to true, add web seed URLs loaded from resume - // data into this torrent instead of replacing the ones from the .torrent - // file - bool m_merge_resume_http_seeds:1; +#error 2 missing bit here // if this is set, whenever transitioning into a downloading/seeding state // from a non-downloading/seeding state, the torrent is paused. diff --git a/src/read_resume_data.cpp b/src/read_resume_data.cpp new file mode 100644 index 000000000..0765c5a22 --- /dev/null +++ b/src/read_resume_data.cpp @@ -0,0 +1,276 @@ +/* + +Copyright (c) 2015, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/aux_/disable_warnings_push.hpp" + +#include +#include + +#include "libtorrent/aux_/disable_warnings_pop.hpp" + +#include "libtorrent/bdecode.hpp" +#include "libtorrent/read_resume_data.hpp" +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/announce_entry.hpp" + +namespace libtorrent +{ + namespace + { + void apply_flag(boost::uint64_t& current_flags + , bdecode_node const& n + , char const* name + , boost::uint64_t const flag) + { + if (n.dict_find_int_value(name, -1) == -1) + { + current_flags &= ~flag; + } + else + { + current_flags |= flag; + } + } + } + + add_torrent_params read_resume_data(bdecode_node const& rd, error_code& ec) + { + add_torrent_params ret; + + ret.total_uploaded = rd.dict_find_int_value("total_uploaded"); + ret.total_downloaded = rd.dict_find_int_value("total_downloaded"); + ret.active_time = rd.dict_find_int_value("active_time"); + ret.finished_time = rd.dict_find_int_value("finished_time"); + ret.seeding_time = rd.dict_find_int_value("seeding_time"); + + ret.last_seen_complete = rd.dict_find_int_value("last_seen_complete"); + + // scrape data cache + ret.num_complete = rd.dict_find_int_value("num_complete", -1); + ret.num_incomplete = rd.dict_find_int_value("num_incomplete", -1); + ret.num_downloaded = rd.dict_find_int_value("num_downloaded", -1); + + // torrent settings + ret.max_uploads = rd.dict_find_int_value("max_uploads", -1); + ret.max_connections = rd.dict_find_int_value("max_connections", -1); + ret.upload_limit = rd.dict_find_int_value("upload_rate_limit", -1); + ret.download_limit = rd.dict_find_int_value("download_rate_limit", -1); + + // torrent state + apply_flag(ret.flags, rd, "seed_mode", add_torrent_params::flag_seed_mode); + apply_flag(ret.flags, rd, "super_seeding", add_torrent_params::flag_super_seeding); + apply_flag(ret.flags, rd, "auto_managed", add_torrent_params::flag_auto_managed); + apply_flag(ret.flags, rd, "sequential_download", add_torrent_params::flag_sequential_download); + apply_flag(ret.flags, rd, "paused", add_torrent_params::flag_paused); + + ret.save_path = rd.dict_find_string_value("save_path"); + + ret.url = rd.dict_find_string_value("url"); + ret.uuid = rd.dict_find_string_value("uuid"); + ret.source_feed_url = rd.dict_find_string_value("feed"); + +#error add a field for this. The mapping has to happen in the torrent \ + constructor probably, and passed on to the storage. possibly, the \ + mapped_storage should be passed directly in when the storage is constructed + + bdecode_node mapped_files = rd.dict_find_list("mapped_files"); + if (mapped_files && mapped_files.list_size() == m_torrent_file->num_files()) + { + for (int i = 0; i < m_torrent_file->num_files(); ++i) + { + std::string new_filename = mapped_files.list_string_value_at(i); + if (new_filename.empty()) continue; + m_torrent_file->rename_file(i, new_filename); + } + } + + ret.added_time = rd.dict_find_int_value("added_time", 0); + ret.completed_time = rd.dict_find_int_value("completed_time", 0); + + // load file priorities except if the add_torrent_param file was set to + // override resume data + bdecode_node file_priority = rd.dict_find_list("file_priority"); + if (file_priority) + { + const int num_files = file_priority.list_size(); + ret.file_priorities.resize(num_files, 4); + for (int i = 0; i < num_files; ++i) + { + ret.file_priorities[i] = file_priority.list_int_value_at(i, 1); + // this is suspicious, leave seed mode + if (ret.file_priorities[i] == 0) + { + ret.flags &= ~add_torrent_params::flag_seed_mode; + } + } + } + + bdecode_node trackers = rd.dict_find_list("trackers"); + if (trackers) + { + // it's possible to delete the trackers from a torrent and then save + // resume data with an empty trackers list. Since we found a trackers + // list here, these should replace whatever we find in the .torrent + // file. + ret.flags &= ~add_torrent_params::flag_merge_resume_trackers; + + int tier = 0; + for (int i = 0; i < trackers.list_size(); ++i) + { + bdecode_node tier_list = trackers.list_at(i); + if (!tier_list || tier_list.type() != bdecode_node::list_t) + continue; + + for (int j = 0; j < tier_list.list_size(); ++j) + { + ret.trackers.push_back(tier_list.list_string_value_at(j)); + ret.tracker_tiers.push_back(tier); + } + ++tier; + } + std::sort(m_trackers.begin(), m_trackers.end(), boost::bind(&announce_entry::tier, _1) + < boost::bind(&announce_entry::tier, _2)); + + if (settings().get_bool(settings_pack::prefer_udp_trackers)) + prioritize_udp_trackers(); + } + + // if merge resume http seeds is not set, we need to clear whatever web + // seeds we loaded from the .torrent file, because we want whatever's in + // the resume file to take precedence. If there aren't even any fields in + // the resume data though, keep the ones from the torrent + bdecode_node url_list = rd.dict_find_list("url-list"); + bdecode_node httpseeds = rd.dict_find_list("httpseeds"); + if ((url_list || httpseeds) && !m_merge_resume_http_seeds) + { + // since we found http seeds in the resume data, they should replace + // whatever web seeds are specified in the .torrent + ret.flags &= ~add_torrent_params::flag_merge_resume_http_seeds; + } + + if (url_list) + { + for (int i = 0; i < url_list.list_size(); ++i) + { + std::string url = url_list.list_string_value_at(i); + if (url.empty()) continue; + ret.url_seeds.push_back(url); + +#error this correction logic has to be moved to the torrent constructor now + if (m_torrent_file->num_files() > 1 && url[url.size()-1] != '/') url += '/'; + } + } + + if (httpseeds) + { + for (int i = 0; i < httpseeds.list_size(); ++i) + { + std::string url = httpseeds.list_string_value_at(i); + if (url.empty()) continue; +#error add this field (merge with url_seeds?) + ret.http_seeds.push_back(url); + } + } + + bdecode_node mt = rd.dict_find_string("merkle tree"); + if (mt) + { +#error add field for this + std::vector tree; + tree.resize(m_torrent_file->merkle_tree().size()); + std::memcpy(&tree[0], mt.string_ptr() + , (std::min)(mt.string_length(), int(tree.size()) * 20)); + if (mt.string_length() < int(tree.size()) * 20) + std::memset(&tree[0] + mt.string_length() / 20, 0 + , tree.size() - mt.string_length() / 20); + m_torrent_file->set_merkle_tree(tree); + } + + +#error this is the case where the torrent is a merkle torrent but the resume \ + data does not contain the merkle tree, we need some kind of check in the \ + torrent constructor and error reporting + { + // 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 + TORRENT_ASSERT(false); + } + +#error add fields to add_torrent_params for these + // some sanity checking. Maybe we shouldn't be in seed mode anymore + bdecode_node pieces = rd.dict_find("pieces"); + if (pieces && pieces.type() == bdecode_node::string_t + && int(pieces.string_length()) == m_torrent_file->num_pieces()) + { + char const* pieces_str = pieces.string_ptr(); + for (int i = 0, end(pieces.string_length()); i < end; ++i) + { + // being in seed mode and missing a piece is not compatible. + // Leave seed mode if that happens + if ((pieces_str[i] & 1)) continue; + m_seed_mode = false; + break; + } + } + + bdecode_node piece_priority = rd.dict_find_string("piece_priority"); + if (piece_priority && piece_priority.string_length() + == m_torrent_file->num_pieces()) + { + char const* p = piece_priority.string_ptr(); + for (int i = 0; i < piece_priority.string_length(); ++i) + { + if (p[i] > 0) continue; + m_seed_mode = false; + break; + } + } + + m_verified.resize(m_torrent_file->num_pieces(), false); + + return ret; + } + + add_torrent_params read_resume_data(char const* buffer, int size, error_code& ec) + { + bdecode_node rd; + bdecode(buffer, buffer + size, rd, ec); + if (ec) return add_torrent_params(); + + return read_resume_data(rd, ec); + } +} + diff --git a/src/session_handle.cpp b/src/session_handle.cpp index 6e160f4df..2766ef40b 100644 --- a/src/session_handle.cpp +++ b/src/session_handle.cpp @@ -152,7 +152,7 @@ namespace libtorrent return TORRENT_SYNC_CALL_RET(std::vector, get_torrents); } - #ifndef BOOST_NO_EXCEPTIONS +#ifndef BOOST_NO_EXCEPTIONS torrent_handle session_handle::add_torrent(add_torrent_params const& params) { error_code ec; @@ -171,13 +171,11 @@ namespace libtorrent void session_handle::async_add_torrent(add_torrent_params const& params) { add_torrent_params* p = new add_torrent_params(params); -#ifndef TORRENT_NO_DEPRECATE - if (params.tracker_url) - { - p->trackers.push_back(params.tracker_url); - p->tracker_url = NULL; - } -#endif +#error all add_torrent() and async_add_torrent() function need to pass the \ + add_torrent_params through a function that parses the resume vector and \ + blends the results into the params object (unless deprecated functions \ + are disabled) + TORRENT_ASYNC_CALL1(async_add_torrent, p); } @@ -217,11 +215,14 @@ namespace libtorrent , void* userdata) { add_torrent_params p(sc); - p.tracker_url = tracker_url; + p.trackers.push_back(tracker_url); p.info_hash = info_hash; p.save_path = save_path; p.storage_mode = storage_mode; - p.paused = paused; + + if (paused) p.flags |= add_torrent_params::flag_paused; + else p.flags &= ~add_torrent_params::flag_paused; + p.userdata = userdata; p.name = name; if (resume_data.type() != entry::undefined_t) diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 6a1f0ece5..84e18708d 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -4615,10 +4615,6 @@ namespace aux { { TORRENT_ASSERT(!p.save_path.empty()); -#ifndef TORRENT_NO_DEPRECATE - p.update_flags(); -#endif - add_torrent_params params = p; if (string_begins_no_case("magnet:", params.url.c_str())) { diff --git a/src/storage.cpp b/src/storage.cpp index 5dc86ce7f..6517f9985 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -820,6 +820,18 @@ namespace libtorrent #endif } + namespace + { + bool is_seed(char const* pieces, int const len) + { + for (int i = 0; i < len; ++i) + { + if ((pieces[i] & 1) == 0) return false; + } + return true; + } + } + bool default_storage::verify_resume_data(bdecode_node const& rd , std::vector const* links , storage_error& ec) @@ -868,6 +880,10 @@ namespace libtorrent && int(pieces.string_length()) == fs.num_pieces()) { char const* pieces_str = pieces.string_ptr(); + + // TODO: this should just be a std::none_of() + bool const seed = is_seed(pieces_str, pieces.string_length()); + // parse have bitmask. Verify that the files we expect to have // actually do exist for (int i = 0; i < fs.num_pieces(); ++i) @@ -900,6 +916,16 @@ namespace libtorrent } } + if (seed && size != fs.file_size(i)) + { + // the resume data indicates we're a seed, but this file has + // the wrong size. Reject the resume data + ec.ec = errors::mismatching_file_size; + ec.file = i; + ec.operation = storage_error::check_resume; + return false; + } + // OK, this file existed, good. Now, skip all remaining pieces in // this file. We're just sanity-checking whether the files exist // or not. @@ -908,6 +934,13 @@ namespace libtorrent i = (std::max)(i + 1, pr.piece); } } + else + { + ec.ec = errors::missing_pieces; + ec.file = -1; + ec.operation = storage_error::check_resume; + return false; + } } return true; diff --git a/src/torrent.cpp b/src/torrent.cpp index c4a7936f8..e49c32912 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -237,7 +237,6 @@ namespace libtorrent , m_auto_sequential(false) , m_seed_mode(false) , m_super_seeding(false) - , m_override_resume_data((p.flags & add_torrent_params::flag_override_resume_data) != 0) #ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES , m_resolving_country(false) @@ -278,8 +277,6 @@ namespace libtorrent , m_last_scrape((std::numeric_limits::min)()) , m_progress_ppm(0) , m_pending_active_change(false) - , m_use_resume_save_path((p.flags & add_torrent_params::flag_use_resume_save_path) != 0) - , m_merge_resume_http_seeds((p.flags & add_torrent_params::flag_merge_resume_http_seeds) != 0) , m_stop_when_ready((p.flags & add_torrent_params::flag_stop_when_ready) != 0) { // we cannot log in the constructor, because it relies on shared_from_this @@ -357,6 +354,40 @@ namespace libtorrent m_resume_data.reset(new resume_data_t); m_resume_data->buf = p.resume_data; } + + int tier = 0; + std::vector::const_iterator tier_iter = p.tracker_tiers.begin(); + for (std::vector::const_iterator i = p.trackers.begin() + , end(p.trackers.end()); i != end; ++i) + { + if (tier_iter != p.tracker_tiers.end()) + tier = *tier_iter++; + + announce_entry e(*i); + e.fail_limit = 0; + e.source = announce_entry::source_magnet_link; + e.tier = tier; + m_trackers.push_back(e); + m_torrent_file->add_tracker(*i, tier); + } + + if (settings().get_bool(settings_pack::prefer_udp_trackers)) + prioritize_udp_trackers(); + + + m_total_uploaded = p.total_uploaded; + m_total_downloaded = p.total_downloaded; + + // the numeber of seconds this torrent has spent in started, finished and + // seeding state so far, respectively. + m_active_time = p.active_time; + m_finished_time = p.finished_time; + m_seeding_time = p.seeding_time; + + m_added_time = p.added_time ? p.added_time : time(0); + m_completed_time = p.completed_time; + if (m_completed_time != 0 && m_completed_time < m_added_time) + m_completed_time = m_added_time; } void torrent::inc_stats_counter(int c, int value) @@ -718,6 +749,8 @@ namespace libtorrent { TORRENT_ASSERT(is_single_thread()); +#error why isn't this done in the constructor? + #ifndef TORRENT_DISABLE_LOGGING debug_log("creating torrent: %s max-uploads: %d max-connections: %d " "upload-limit: %d download-limit: %d flags: %s%s%s%s%s%s%s%s%s%s%s%s" @@ -770,28 +803,6 @@ namespace libtorrent if (!m_name && !m_url.empty()) m_name.reset(new std::string(m_url)); -#ifndef TORRENT_NO_DEPRECATE - if (p.tracker_url && std::strlen(p.tracker_url) > 0) - { - m_trackers.push_back(announce_entry(p.tracker_url)); - m_trackers.back().fail_limit = 0; - m_trackers.back().source = announce_entry::source_magnet_link; - m_torrent_file->add_tracker(p.tracker_url); - } -#endif - - for (std::vector::const_iterator i = p.trackers.begin() - , end(p.trackers.end()); i != end; ++i) - { - m_trackers.push_back(announce_entry(*i)); - m_trackers.back().fail_limit = 0; - m_trackers.back().source = announce_entry::source_magnet_link; - m_torrent_file->add_tracker(*i); - } - - if (settings().get_bool(settings_pack::prefer_udp_trackers)) - prioritize_udp_trackers(); - // if we don't have metadata, make this torrent pinned. The // client may unpin it once we have metadata and it has had // a chance to save it on the metadata_received_alert @@ -1808,7 +1819,8 @@ namespace libtorrent } // this may not be called from a constructor because of the call to - // shared_from_this() + // 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 void torrent::init() { INVARIANT_CHECK; @@ -1883,10 +1895,6 @@ namespace libtorrent #endif m_resume_data.reset(); } - else - { - read_resume_data(m_resume_data->node); - } } #if TORRENT_USE_ASSERTS @@ -1927,6 +1935,7 @@ namespace libtorrent // if we've already loaded file priorities, don't load piece priorities, // they will interfere. +#error we should get the piece priority from add_torrent_params instead if (!m_seed_mode && m_resume_data && m_file_priority.empty()) { bdecode_node piece_priority = m_resume_data->node @@ -6784,278 +6793,6 @@ namespace libtorrent #endif #endif // TORRENT_NO_DEPRECATE - void torrent::read_resume_data(bdecode_node const& rd) - { - m_total_uploaded = rd.dict_find_int_value("total_uploaded"); - m_total_downloaded = rd.dict_find_int_value("total_downloaded"); - m_active_time = rd.dict_find_int_value("active_time"); - m_finished_time = rd.dict_find_int_value("finished_time"); - m_seeding_time = rd.dict_find_int_value("seeding_time"); - m_last_seen_complete = rd.dict_find_int_value("last_seen_complete"); - m_complete = rd.dict_find_int_value("num_complete", 0xffffff); - m_incomplete = rd.dict_find_int_value("num_incomplete", 0xffffff); - m_downloaded = rd.dict_find_int_value("num_downloaded", 0xffffff); - - if (!m_override_resume_data) - { - int up_limit_ = rd.dict_find_int_value("upload_rate_limit", -1); - if (up_limit_ != -1) set_upload_limit(up_limit_); - - int down_limit_ = rd.dict_find_int_value("download_rate_limit", -1); - if (down_limit_ != -1) set_download_limit(down_limit_); - - int max_connections_ = rd.dict_find_int_value("max_connections", -1); - if (max_connections_ != -1) set_max_connections(max_connections_); - - int max_uploads_ = rd.dict_find_int_value("max_uploads", -1); - if (max_uploads_ != -1) set_max_uploads(max_uploads_); - - int seed_mode_ = rd.dict_find_int_value("seed_mode", -1); - if (seed_mode_ != -1) m_seed_mode = seed_mode_ && m_torrent_file->is_valid(); - - int super_seeding_ = rd.dict_find_int_value("super_seeding", -1); - if (super_seeding_ != -1) super_seeding(super_seeding_ != 0); - - int auto_managed_ = rd.dict_find_int_value("auto_managed", -1); - if (auto_managed_ != -1) - { - m_auto_managed = auto_managed_ != 0; - - update_want_scrape(); - update_state_list(); - } - - int sequential_ = rd.dict_find_int_value("sequential_download", -1); - if (sequential_ != -1) set_sequential_download(sequential_ != 0); - - int paused_ = rd.dict_find_int_value("paused", -1); - if (paused_ != -1) - { - set_allow_peers(paused_ == 0); - m_announce_to_dht = (paused_ == 0); - m_announce_to_trackers = (paused_ == 0); - m_announce_to_lsd = (paused_ == 0); - - update_gauge(); - update_want_peers(); - update_want_scrape(); - update_state_list(); - } - int dht_ = rd.dict_find_int_value("announce_to_dht", -1); - if (dht_ != -1) m_announce_to_dht = (dht_ != 0); - int lsd_ = rd.dict_find_int_value("announce_to_lsd", -1); - if (lsd_ != -1) m_announce_to_lsd = (lsd_ != 0); - int track_ = rd.dict_find_int_value("announce_to_trackers", -1); - if (track_ != -1) m_announce_to_trackers = (track_ != 0); - -#ifndef TORRENT_DISABLE_LOGGING - debug_log("loaded resume data: max-uploads: %d max-connections: %d " - "upload-limit: %d download-limit: %d paused: %d sequential-download: %d " - "super-seeding: %d auto-managed: %d" - , max_uploads_, max_connections_, up_limit_, down_limit_ - , paused_, sequential_, super_seeding_, auto_managed_); -#endif - } - - int now = m_ses.session_time(); - int tmp = rd.dict_find_int_value("last_scrape", -1); - m_last_scrape = tmp == -1 ? (std::numeric_limits::min)() : now - tmp; - tmp = rd.dict_find_int_value("last_download", -1); - m_last_download = tmp == -1 ? (std::numeric_limits::min)() : now - tmp; - tmp = rd.dict_find_int_value("last_upload", -1); - m_last_upload = tmp == -1 ? (std::numeric_limits::min)() : now - tmp; - - if (m_use_resume_save_path) - { - std::string p = rd.dict_find_string_value("save_path"); - if (!p.empty()) - { - m_save_path = p; -#ifndef TORRENT_DISABLE_LOGGING - debug_log("loaded resume data: save-path: %s", m_save_path.c_str()); -#endif - } - } - - m_url = rd.dict_find_string_value("url"); - m_uuid = rd.dict_find_string_value("uuid"); - m_source_feed_url = rd.dict_find_string_value("feed"); - - if (!m_uuid.empty() || !m_url.empty()) - { - boost::shared_ptr me(shared_from_this()); - - // insert this torrent in the uuid index - m_ses.insert_uuid_torrent(m_uuid.empty() ? m_url : m_uuid, me); - } - - // The mapped_files needs to be read both in the network thread - // and in the disk thread, since they both have their own mapped files structures - // which are kept in sync - bdecode_node mapped_files = rd.dict_find_list("mapped_files"); - if (mapped_files && mapped_files.list_size() == m_torrent_file->num_files()) - { - for (int i = 0; i < m_torrent_file->num_files(); ++i) - { - std::string new_filename = mapped_files.list_string_value_at(i); - if (new_filename.empty()) continue; - m_torrent_file->rename_file(i, new_filename); - } - } - - m_added_time = rd.dict_find_int_value("added_time", m_added_time); - m_completed_time = rd.dict_find_int_value("completed_time", m_completed_time); - if (m_completed_time != 0 && m_completed_time < m_added_time) - m_completed_time = m_added_time; - - // load file priorities except if the add_torrent_param file was set to - // override resume data - if (!m_override_resume_data || m_file_priority.empty()) - { - bdecode_node file_priority = rd.dict_find_list("file_priority"); - if (file_priority) - { - const int num_files = (std::min)(file_priority.list_size() - , m_torrent_file->num_files()); - std::vector file_prio(num_files); - for (int i = 0; i < num_files; ++i) - { - file_prio[i] = file_priority.list_int_value_at(i, 1); - // this is suspicious, leave seed mode - if (file_prio[i] == 0) m_seed_mode = false; - } - - prioritize_files(file_prio); - } - } - - bdecode_node trackers = rd.dict_find_list("trackers"); - if (trackers) - { - if (!m_merge_resume_trackers) m_trackers.clear(); - int tier = 0; - for (int i = 0; i < trackers.list_size(); ++i) - { - bdecode_node tier_list = trackers.list_at(i); - if (!tier_list || tier_list.type() != bdecode_node::list_t) - continue; - for (int j = 0; j < tier_list.list_size(); ++j) - { - announce_entry e(tier_list.list_string_value_at(j)); - if (std::find_if(m_trackers.begin(), m_trackers.end() - , boost::bind(&announce_entry::url, _1) == e.url) != m_trackers.end()) - continue; - e.tier = tier; - e.fail_limit = 0; - m_trackers.push_back(e); - } - ++tier; - } - std::sort(m_trackers.begin(), m_trackers.end(), boost::bind(&announce_entry::tier, _1) - < boost::bind(&announce_entry::tier, _2)); - - if (settings().get_bool(settings_pack::prefer_udp_trackers)) - prioritize_udp_trackers(); - } - - // if merge resume http seeds is not set, we need to clear whatever web - // seeds we loaded from the .torrent file, because we want whatever's in - // the resume file to take precedence. If there aren't even any fields in - // the resume data though, keep the ones from the torrent - bdecode_node url_list = rd.dict_find_list("url-list"); - bdecode_node httpseeds = rd.dict_find_list("httpseeds"); - if ((url_list || httpseeds) && !m_merge_resume_http_seeds) - { - m_web_seeds.clear(); - } - - if (url_list) - { - for (int i = 0; i < url_list.list_size(); ++i) - { - std::string url = url_list.list_string_value_at(i); - if (url.empty()) continue; - if (m_torrent_file->num_files() > 1 && url[url.size()-1] != '/') url += '/'; - add_web_seed(url, web_seed_entry::url_seed); - } - } - - if (httpseeds) - { - for (int i = 0; i < httpseeds.list_size(); ++i) - { - std::string url = httpseeds.list_string_value_at(i); - if (url.empty()) continue; - add_web_seed(url, web_seed_entry::http_seed); - } - } - - if (m_torrent_file->is_merkle_torrent()) - { - bdecode_node mt = rd.dict_find_string("merkle tree"); - if (mt) - { - std::vector tree; - tree.resize(m_torrent_file->merkle_tree().size()); - std::memcpy(&tree[0], mt.string_ptr() - , (std::min)(mt.string_length(), int(tree.size()) * 20)); - if (mt.string_length() < int(tree.size()) * 20) - std::memset(&tree[0] + mt.string_length() / 20, 0 - , tree.size() - mt.string_length() / 20); - 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 - TORRENT_ASSERT(false); - } - } - - // updating some of the torrent state may have set need_save_resume_data. - // clear it here since we've just restored the resume data we already - // have. Nothing has changed from that state yet. - m_need_save_resume_data = false; - - if (m_seed_mode) - { - // some sanity checking. Maybe we shouldn't be in seed mode anymore - bdecode_node pieces = rd.dict_find("pieces"); - if (pieces && pieces.type() == bdecode_node::string_t - && int(pieces.string_length()) == m_torrent_file->num_pieces()) - { - char const* pieces_str = pieces.string_ptr(); - for (int i = 0, end(pieces.string_length()); i < end; ++i) - { - // being in seed mode and missing a piece is not compatible. - // Leave seed mode if that happens - if ((pieces_str[i] & 1)) continue; - m_seed_mode = false; - break; - } - } - - bdecode_node piece_priority = rd.dict_find_string("piece_priority"); - if (piece_priority && piece_priority.string_length() - == m_torrent_file->num_pieces()) - { - char const* p = piece_priority.string_ptr(); - for (int i = 0; i < piece_priority.string_length(); ++i) - { - if (p[i] > 0) continue; - m_seed_mode = false; - break; - } - } - - m_verified.resize(m_torrent_file->num_pieces(), false); - } - } - boost::shared_ptr torrent::get_torrent_copy() { if (!m_torrent_file->is_valid()) return boost::shared_ptr(); @@ -7372,9 +7109,6 @@ namespace libtorrent ret["max_connections"] = max_connections(); ret["max_uploads"] = max_uploads(); ret["paused"] = is_torrent_paused(); - ret["announce_to_dht"] = m_announce_to_dht; - ret["announce_to_trackers"] = m_announce_to_trackers; - ret["announce_to_lsd"] = m_announce_to_lsd; ret["auto_managed"] = m_auto_managed; // piece priorities and file priorities are mutually exclusive. If there