support saving metadata in resume file, enable it by default for magnet links

This commit is contained in:
Arvid Norberg 2011-02-26 07:48:05 +00:00
parent 56937edf56
commit 7288f77ec9
6 changed files with 144 additions and 14 deletions

View File

@ -1,3 +1,4 @@
* support saving metadata in resume file, enable it by default for magnet links
* support for receiving multi announce messages for local peer discovery
* added session::listen_no_system_port flag to prevent libtorrent from ever binding the listen socket to port 0
* added option to not recheck on missing or incomplete resume data

View File

@ -2134,6 +2134,7 @@ Its declaration looks like this::
std::string name() const;
enum save_resume_flags_t { flush_disk_cache = 1, save_info_dict = 2 };
void save_resume_data(int flags = 0) const;
bool need_save_resume_data() const;
void force_reannounce() const;
@ -2933,14 +2934,19 @@ save_resume_data()
::
enum save_resume_flags_t { flush_disk_cache = 1, save_info_dict = 2 };
void save_resume_data(int flags = 0) const;
``save_resume_data()`` generates fast-resume data and returns it as an entry_. This entry_
is suitable for being bencoded. For more information about how fast-resume works, see `fast resume`_.
The ``flags`` argument may be set to ``torrent_handle::flush_cache``. Doing so will flush the disk
cache before creating the resume data. This avoids a problem with file timestamps in the resume
data in case the cache hasn't been flushed yet.
The ``flags`` argument is a bitmask of flags ORed together. If the flag ``torrent_handle::flush_cache``
is set, the disk cache will be flushed before creating the resume data. This avoids a problem with
file timestamps in the resume data in case the cache hasn't been flushed yet.
If the flag ``torrent_handle::save_info_dict`` is set, the resume data will contain the metadata
from the torrent file as well. This is default for any torrent that's added without a torrent
file (such as a magnet link or a URL).
This operation is asynchronous, ``save_resume_data`` will return immediately. The resume data
is delivered when it's done through an `save_resume_data_alert`_.

View File

@ -671,7 +671,7 @@ void add_torrent(libtorrent::session& ses
p.share_mode = share_mode;
lazy_entry resume_data;
std::string filename = combine_path(save_path, ".resume/" + t->name() + ".resume");
std::string filename = combine_path(save_path, ".resume/" + to_hex(t->info_hash().to_string()) + ".resume");
std::vector<char> buf;
if (load_file(filename.c_str(), buf, ec) == 0)
@ -845,7 +845,7 @@ void handle_alert(libtorrent::session& ses, libtorrent::alert* a
{
std::vector<char> out;
bencode(std::back_inserter(out), *p->resume_data);
save_file(combine_path(h.save_path(), ".resume/" + h.name() + ".resume"), out);
save_file(combine_path(h.save_path(), ".resume/" + to_hex(h.info_hash().to_string()) + ".resume"), out);
if (non_files.find(h) == non_files.end()
&& std::find_if(files.begin(), files.end()
, boost::bind(&handles_t::value_type::second, _1) == h) == files.end())
@ -1211,6 +1211,26 @@ int main(int argc, char* argv[])
p.storage_mode = (storage_mode_t)allocation_mode;
p.url = *i;
std::vector<char> buf;
if (std::strstr(i->c_str(), "magnet:") == i->c_str())
{
std::string btih = url_has_argument(*i, "xt");
if (btih.empty()) continue;
if (btih.compare(0, 9, "urn:btih:") != 0)
continue;
sha1_hash info_hash;
if (btih.size() == 40 + 9) from_hex(&btih[9], 40, (char*)&info_hash[0]);
else info_hash.assign(base32decode(btih.substr(9)));
std::string filename = combine_path(save_path, ".resume/"
+ to_hex(info_hash.to_string()) + ".resume");
if (load_file(filename.c_str(), buf, ec) == 0)
p.resume_data = &buf;
}
printf("adding URL: %s\n", i->c_str());
error_code ec;
torrent_handle h = ses.add_torrent(p, ec);
@ -1985,7 +2005,7 @@ int main(int argc, char* argv[])
torrent_handle h = rd->handle;
std::vector<char> out;
bencode(std::back_inserter(out), *rd->resume_data);
save_file(combine_path(h.save_path(), ".resume/" + h.name() + ".resume"), out);
save_file(combine_path(h.save_path(), ".resume/" + to_hex(h.info_hash().to_string()) + ".resume"), out);
}
if (g_log_file) fclose(g_log_file);
printf("\nsaving session state\n");

View File

@ -1275,6 +1275,10 @@ namespace libtorrent
// round-robin index into m_interfaces
mutable boost::uint8_t m_interface_index;
// these are the flags sent in on a call to save_resume_data
// we need to save them to check them in write_resume_data
boost::uint8_t m_save_resume_flags;
// set to true when this torrent has been paused but
// is waiting to finish all current download requests
// before actually closing all connections
@ -1290,6 +1294,12 @@ namespace libtorrent
// rotating sequence number for LSD announces sent out.
// used to only use IP broadcast for every 8th lsd announce
boost::uint8_t m_lsd_seq:3;
// this is set to true if the torrent was started without
// metadata. It is used to save metadata in the resume file
// by default for such torrents. It does not necessarily
// have to be a magnet link.
bool m_magnet_link:1;
};
}

View File

@ -230,7 +230,7 @@ namespace libtorrent
void force_recheck() const;
enum save_resume_flags_t { flush_disk_cache = 1 };
enum save_resume_flags_t { flush_disk_cache = 1, save_info_dict = 2 };
void save_resume_data(int flags = 0) const;
bool need_save_resume_data() const;

View File

@ -332,11 +332,9 @@ namespace libtorrent
, m_total_uploaded(0)
, m_total_downloaded(0)
, m_started(time_now())
, m_torrent_file(p.ti ? p.ti : new torrent_info(info_hash))
, m_storage(0)
, m_tracker_timer(ses.m_io_service)
, m_ses(ses)
, m_trackers(m_torrent_file->trackers())
, m_trackerid(p.trackerid)
, m_save_path(complete(p.save_path))
, m_url(p.url)
@ -358,13 +356,13 @@ namespace libtorrent
, m_storage_mode(p.storage_mode)
, m_announcing(false)
, m_waiting_tracker(false)
, m_seed_mode(p.seed_mode && m_torrent_file->is_valid())
, m_seed_mode(false)
, m_active_time(0)
, m_last_working_tracker(-1)
, m_finished_time(0)
, m_sequential_download(false)
, m_got_tracker_response(false)
, m_connections_initialized(p.ti && p.ti->is_valid())
, m_connections_initialized(false)
, m_super_seeding(false)
, m_override_resume_data(p.override_resume_data)
#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
@ -377,8 +375,7 @@ namespace libtorrent
, m_max_uploads(~0)
, m_deficit_counter(0)
, m_num_uploads(0)
, m_block_size_shift(root2((p.ti && p.ti->is_valid())
? (std::min)(block_size, m_torrent_file->piece_length()) : block_size))
, m_block_size_shift(root2(block_size))
, m_has_incoming(false)
, m_files_checked(false)
, m_queued_for_checking(false)
@ -403,10 +400,99 @@ namespace libtorrent
, m_last_upload(0)
, m_downloaders(0xffffff)
, m_interface_index(0)
, m_save_resume_flags(0)
, m_graceful_pause_mode(false)
, m_need_connect_boost(true)
, m_lsd_seq(0)
, m_magnet_link(false)
{
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;
// did the user provide resume data?
// maybe the metadata is in there
if (p.resume_data)
{
int pos;
error_code ec;
lazy_entry tmp;
lazy_entry const* info = 0;
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << time_now_string() << " adding magnet link with resume data\n";
#endif
if (lazy_bdecode(&(*p.resume_data)[0], &(*p.resume_data)[0]
+ p.resume_data->size(), tmp, ec, &pos) == 0
&& tmp.type() == lazy_entry::dict_t
&& (info = tmp.dict_find_dict("info")))
{
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << time_now_string() << " found metadata in resume data\n";
#endif
// verify the info-hash of the metadata stored in the resume file matches
// the torrent we're loading
std::pair<char const*, int> buf = info->data_section();
sha1_hash resume_ih = hasher(buf.first, buf.second).final();
// if url is set, the info_hash is not actually the info-hash of the
// torrent, but the hash of the URL, until we have the full torrent
if (resume_ih == info_hash || !p.url.empty())
{
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << time_now_string() << " info-hash matched\n";
#endif
m_torrent_file = (p.ti ? p.ti : new torrent_info(resume_ih));
if (!m_torrent_file->parse_info_section(*info, ec, 0))
{
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << time_now_string() << " failed to load metadata from resume file: "
<< ec.message() << "\n";
#endif
}
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
else
{
(*m_ses.m_logger) << time_now_string() << " successfully loaded metadata from resume file\n";
}
#endif
}
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
else
{
(*m_ses.m_logger) << time_now_string() << " metadata info-hash failed\n";
}
#endif
}
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
else
{
(*m_ses.m_logger) << time_now_string() << " no metadata found\n";
}
#endif
}
}
if (!m_torrent_file)
m_torrent_file = (p.ti ? p.ti : new torrent_info(info_hash));
m_trackers = m_torrent_file->trackers();
if (m_torrent_file->is_valid())
{
m_seed_mode = p.seed_mode;
m_connections_initialized = true;
m_block_size_shift = root2((std::min)(block_size, m_torrent_file->piece_length()));
}
else
{
if (p.name) m_name.reset(new std::string(p.name));
}
if (!m_url.empty() && m_uuid.empty()) m_uuid = m_url;
TORRENT_ASSERT(m_ses.is_network_thread());
@ -439,7 +525,6 @@ namespace libtorrent
#endif
INVARIANT_CHECK;
if (p.name && (!p.ti || !p.ti->is_valid())) m_name.reset(new std::string(p.name));
if (!m_name && !m_url.empty()) m_name.reset(new std::string(m_url));
if (p.tracker_url && std::strlen(p.tracker_url) > 0)
@ -4353,6 +4438,13 @@ namespace libtorrent
const sha1_hash& info_hash = torrent_file().info_hash();
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
if (valid_metadata())
{
if (m_magnet_link || (m_save_resume_flags & torrent_handle::save_info_dict))
ret["info"] = bdecode(&torrent_file().metadata()[0]
, &torrent_file().metadata()[0] + torrent_file().metadata_size());
}
// blocks per piece
int num_blocks_per_piece =
static_cast<int>(torrent_file().piece_length()) / block_size();
@ -5893,6 +5985,7 @@ namespace libtorrent
m_need_save_resume_data = false;
m_last_saved_resume = time(0);
m_save_resume_flags = boost::uint8_t(flags);
TORRENT_ASSERT(m_storage);
if (m_state == torrent_status::queued_for_checking