forked from premiere/premiere-libtorrent
made torrent_info not rely on exception support
This commit is contained in:
parent
501611173e
commit
eb8ea0f461
|
@ -536,7 +536,8 @@ namespace libtorrent
|
|||
// bencoded tree and moves the torrent
|
||||
// to the checker thread for initial checking
|
||||
// of the storage.
|
||||
void set_metadata(entry const&);
|
||||
// a return value of false indicates an error
|
||||
bool set_metadata(entry const& metadata, std::string& error);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ namespace libtorrent
|
|||
|
||||
void add_node(std::pair<std::string, int> const& node);
|
||||
|
||||
void parse_info_section(entry const& e);
|
||||
bool parse_info_section(entry const& e, std::string& error);
|
||||
|
||||
entry const* extra(char const* key) const
|
||||
{ return m_extra_info.find_key(key); }
|
||||
|
@ -257,7 +257,7 @@ namespace libtorrent
|
|||
|
||||
private:
|
||||
|
||||
void read_torrent_info(const entry& libtorrent);
|
||||
bool read_torrent_info(const entry& libtorrent, std::string& error);
|
||||
|
||||
// the urls to the trackers
|
||||
std::vector<announce_entry> m_urls;
|
||||
|
|
|
@ -185,7 +185,16 @@ namespace libtorrent { namespace
|
|||
}
|
||||
|
||||
entry metadata = bdecode(m_metadata.begin(), m_metadata.end());
|
||||
m_torrent.set_metadata(metadata);
|
||||
std::string error;
|
||||
if (!m_torrent.set_metadata(metadata, error))
|
||||
{
|
||||
// this means the metadata is correct, since we
|
||||
// verified it against the info-hash, but we
|
||||
// failed to parse it. Pause the torrent
|
||||
// TODO: Post an alert!
|
||||
m_torrent.pause();
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear the storage for the bitfield
|
||||
std::vector<bool>().swap(m_have_metadata);
|
||||
|
|
|
@ -2126,12 +2126,16 @@ namespace libtorrent
|
|||
return true;
|
||||
}
|
||||
|
||||
void torrent::set_metadata(entry const& metadata)
|
||||
bool torrent::set_metadata(entry const& metadata, std::string& error)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
TORRENT_ASSERT(!m_torrent_file->is_valid());
|
||||
m_torrent_file->parse_info_section(metadata);
|
||||
if (!m_torrent_file->parse_info_section(metadata, error))
|
||||
{
|
||||
// parse failed
|
||||
return false;
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/entry.hpp"
|
||||
|
||||
namespace pt = boost::posix_time;
|
||||
namespace gr = boost::gregorian;
|
||||
|
||||
using namespace libtorrent;
|
||||
|
@ -160,10 +159,13 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
void extract_single_file(const entry& dict, file_entry& target
|
||||
bool extract_single_file(entry const& dict, file_entry& target
|
||||
, std::string const& root_dir)
|
||||
{
|
||||
target.size = dict["length"].integer();
|
||||
entry const* length = dict.find_key("length");
|
||||
if (length == 0 || length->type() != entry::int_t)
|
||||
return false;
|
||||
target.size = length->integer();
|
||||
target.path = root_dir;
|
||||
target.file_base = 0;
|
||||
|
||||
|
@ -172,38 +174,46 @@ namespace
|
|||
// likely to be correctly encoded
|
||||
|
||||
const entry::list_type* list = 0;
|
||||
if (entry const* p = dict.find_key("path.utf-8"))
|
||||
entry const* p8 = dict.find_key("path.utf-8");
|
||||
if (p8 != 0 && p8->type() == entry::list_t)
|
||||
{
|
||||
list = &p->list();
|
||||
list = &p8->list();
|
||||
}
|
||||
else
|
||||
{
|
||||
list = &dict["path"].list();
|
||||
entry const* p = dict.find_key("path");
|
||||
if (p == 0 || p->type() != entry::list_t)
|
||||
return false;
|
||||
list = &p->list();
|
||||
}
|
||||
|
||||
for (entry::list_type::const_iterator i = list->begin();
|
||||
i != list->end(); ++i)
|
||||
{
|
||||
if (i->type() != entry::string_t)
|
||||
return false;
|
||||
if (i->string() != "..")
|
||||
target.path /= i->string();
|
||||
}
|
||||
verify_encoding(target);
|
||||
if (target.path.is_complete()) throw std::runtime_error("torrent contains "
|
||||
"a file with an absolute path: '"
|
||||
+ target.path.native_file_string() + "'");
|
||||
if (target.path.is_complete())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void extract_files(const entry::list_type& list, std::vector<file_entry>& target
|
||||
bool extract_files(const entry::list_type& list, std::vector<file_entry>& target
|
||||
, std::string const& root_dir)
|
||||
{
|
||||
size_type offset = 0;
|
||||
for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
target.push_back(file_entry());
|
||||
extract_single_file(*i, target.back(), root_dir);
|
||||
if (!extract_single_file(*i, target.back(), root_dir))
|
||||
return false;
|
||||
target.back().offset = offset;
|
||||
offset += target.back().size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
void remove_dir(fs::path& p)
|
||||
|
@ -231,14 +241,13 @@ namespace libtorrent
|
|||
, m_half_metadata(false)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
read_torrent_info(torrent_file);
|
||||
}
|
||||
catch(type_error&)
|
||||
{
|
||||
std::string error;
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (!read_torrent_info(torrent_file, error))
|
||||
throw invalid_torrent_file();
|
||||
}
|
||||
#else
|
||||
read_torrent_info(torrent_file, error);
|
||||
#endif
|
||||
}
|
||||
|
||||
// constructor used for creating new torrents
|
||||
|
@ -330,8 +339,14 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
void torrent_info::parse_info_section(entry const& info)
|
||||
bool torrent_info::parse_info_section(entry const& info, std::string& error)
|
||||
{
|
||||
if (info.type() != entry::dictionary_t)
|
||||
{
|
||||
error = "'info' entry is not a dictionary";
|
||||
return false;
|
||||
}
|
||||
|
||||
// encode the info-field in order to calculate it's sha1-hash
|
||||
std::vector<char> buf;
|
||||
bencode(std::back_inserter(buf), info);
|
||||
|
@ -340,36 +355,71 @@ namespace libtorrent
|
|||
m_info_hash = h.final();
|
||||
|
||||
// extract piece length
|
||||
m_piece_length = (int)info["piece length"].integer();
|
||||
if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0");
|
||||
entry const* piece_length = info.find_key("piece length");
|
||||
if (piece_length == 0 || piece_length->type() != entry::int_t)
|
||||
{
|
||||
error = "invalid or missing 'piece length' entry in torrent file";
|
||||
return false;
|
||||
}
|
||||
m_piece_length = (int)piece_length->integer();
|
||||
if (m_piece_length <= 0)
|
||||
{
|
||||
error = "invalid torrent. piece length <= 0";
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract file name (or the directory name if it's a multifile libtorrent)
|
||||
if (entry const* e = info.find_key("name.utf-8"))
|
||||
entry const* e = info.find_key("name.utf-8");
|
||||
if (e && e->type() == entry::string_t)
|
||||
{ m_name = e->string(); }
|
||||
else
|
||||
{ m_name = info["name"].string(); }
|
||||
{
|
||||
entry const* e = info.find_key("name");
|
||||
if (e == 0 || e->type() != entry::string_t)
|
||||
{
|
||||
error = "invalid name in torrent file";
|
||||
return false;
|
||||
}
|
||||
m_name = e->string();
|
||||
}
|
||||
|
||||
fs::path tmp = m_name;
|
||||
if (tmp.is_complete()) throw std::runtime_error("torrent contains "
|
||||
"a file with an absolute path: '" + m_name + "'");
|
||||
if (tmp.has_branch_path()) throw std::runtime_error(
|
||||
"torrent contains name with directories: '" + m_name + "'");
|
||||
if (tmp.is_complete())
|
||||
{
|
||||
error = "torrent contains a file with an absolute path: '" + m_name + "'";
|
||||
return false;
|
||||
}
|
||||
if (tmp.has_branch_path())
|
||||
{
|
||||
error = "torrent contains name with directories: '" + m_name + "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract file list
|
||||
entry const* i = info.find_key("files");
|
||||
if (i == 0)
|
||||
{
|
||||
entry const* length = info.find_key("length");
|
||||
if (length == 0 || length->type() != entry::int_t)
|
||||
{
|
||||
error = "invalid length of torrent";
|
||||
return false;
|
||||
}
|
||||
// if there's no list of files, there has to be a length
|
||||
// field.
|
||||
file_entry e;
|
||||
e.path = m_name;
|
||||
e.offset = 0;
|
||||
e.size = info["length"].integer();
|
||||
e.size = length->integer();
|
||||
m_files.push_back(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
extract_files(i->list(), m_files, m_name);
|
||||
if (!extract_files(i->list(), m_files, m_name))
|
||||
{
|
||||
error = "failed to parse files from torrent file";
|
||||
return false;
|
||||
}
|
||||
m_multifile = true;
|
||||
}
|
||||
|
||||
|
@ -382,12 +432,22 @@ namespace libtorrent
|
|||
// we want this division to round upwards, that's why we have the
|
||||
// extra addition
|
||||
|
||||
entry const* pieces_ent = info.find_key("pieces");
|
||||
if (pieces_ent == 0 || pieces_ent->type() != entry::string_t)
|
||||
{
|
||||
error = "invalid or missing 'pieces' entry in torrent file";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
|
||||
m_piece_hash.resize(m_num_pieces);
|
||||
const std::string& hash_string = info["pieces"].string();
|
||||
const std::string& hash_string = pieces_ent->string();
|
||||
|
||||
if ((int)hash_string.length() != m_num_pieces * 20)
|
||||
throw invalid_torrent_file();
|
||||
{
|
||||
error = "incorrect number of piece hashes in torrent file";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_num_pieces; ++i)
|
||||
std::copy(
|
||||
|
@ -423,34 +483,37 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(hasher(&info_section_buf[0], info_section_buf.size()).final()
|
||||
== m_info_hash);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// extracts information from a libtorrent file and fills in the structures in
|
||||
// the torrent object
|
||||
void torrent_info::read_torrent_info(const entry& torrent_file)
|
||||
bool torrent_info::read_torrent_info(const entry& torrent_file, std::string& error)
|
||||
{
|
||||
if (torrent_file.type() != entry::dictionary_t)
|
||||
{
|
||||
error = "torrent file is not a dictionary";
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract the url of the tracker
|
||||
if (entry const* i = torrent_file.find_key("announce-list"))
|
||||
entry const* i = torrent_file.find_key("announce-list");
|
||||
if (i && i->type() == entry::list_t)
|
||||
{
|
||||
const entry::list_type& l = i->list();
|
||||
for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
|
||||
{
|
||||
if (j->type() != entry::list_t) break;
|
||||
const entry::list_type& ll = j->list();
|
||||
for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k)
|
||||
{
|
||||
if (k->type() != entry::string_t) break;
|
||||
announce_entry e(k->string());
|
||||
e.tier = (int)std::distance(l.begin(), j);
|
||||
m_urls.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_urls.size() == 0)
|
||||
{
|
||||
// the announce-list is empty
|
||||
// fall back to look for announce
|
||||
m_urls.push_back(announce_entry(
|
||||
torrent_file["announce"].string()));
|
||||
}
|
||||
// shuffle each tier
|
||||
std::vector<announce_entry>::iterator start = m_urls.begin();
|
||||
std::vector<announce_entry>::iterator stop;
|
||||
|
@ -466,14 +529,17 @@ namespace libtorrent
|
|||
}
|
||||
std::random_shuffle(start, stop);
|
||||
}
|
||||
else if (entry const* i = torrent_file.find_key("announce"))
|
||||
|
||||
entry const* announce = torrent_file.find_key("announce");
|
||||
if (m_urls.empty() && announce && announce->type() == entry::string_t)
|
||||
{
|
||||
m_urls.push_back(announce_entry(i->string()));
|
||||
m_urls.push_back(announce_entry(announce->string()));
|
||||
}
|
||||
|
||||
if (entry const* i = torrent_file.find_key("nodes"))
|
||||
entry const* nodes = torrent_file.find_key("nodes");
|
||||
if (nodes && nodes->type() == entry::list_t)
|
||||
{
|
||||
entry::list_type const& list = i->list();
|
||||
entry::list_type const& list = nodes->list();
|
||||
for (entry::list_type::const_iterator i(list.begin())
|
||||
, end(list.end()); i != end; ++i)
|
||||
{
|
||||
|
@ -481,41 +547,40 @@ namespace libtorrent
|
|||
entry::list_type const& l = i->list();
|
||||
entry::list_type::const_iterator iter = l.begin();
|
||||
if (l.size() < 1) continue;
|
||||
if (iter->type() != entry::string_t) continue;
|
||||
std::string const& hostname = iter->string();
|
||||
++iter;
|
||||
int port = 6881;
|
||||
if (iter->type() != entry::int_t) continue;
|
||||
if (l.end() != iter) port = iter->integer();
|
||||
m_nodes.push_back(std::make_pair(hostname, port));
|
||||
}
|
||||
}
|
||||
|
||||
// extract creation date
|
||||
try
|
||||
entry const* creation_date = torrent_file.find_key("creation date");
|
||||
if (creation_date && creation_date->type() == entry::int_t)
|
||||
{
|
||||
m_creation_date = pt::ptime(gr::date(1970, gr::Jan, 1))
|
||||
+ pt::seconds(long(torrent_file["creation date"].integer()));
|
||||
+ pt::seconds(long(creation_date->integer()));
|
||||
}
|
||||
catch (type_error) {}
|
||||
|
||||
// if there are any url-seeds, extract them
|
||||
try
|
||||
entry const* url_seeds = torrent_file.find_key("url-list");
|
||||
if (url_seeds && url_seeds->type() == entry::string_t)
|
||||
{
|
||||
entry const& url_seeds = torrent_file["url-list"];
|
||||
if (url_seeds.type() == entry::string_t)
|
||||
m_url_seeds.push_back(url_seeds->string());
|
||||
}
|
||||
else if (url_seeds && url_seeds->type() == entry::list_t)
|
||||
{
|
||||
entry::list_type const& l = url_seeds->list();
|
||||
for (entry::list_type::const_iterator i = l.begin();
|
||||
i != l.end(); ++i)
|
||||
{
|
||||
m_url_seeds.push_back(url_seeds.string());
|
||||
}
|
||||
else if (url_seeds.type() == entry::list_t)
|
||||
{
|
||||
entry::list_type const& l = url_seeds.list();
|
||||
for (entry::list_type::const_iterator i = l.begin();
|
||||
i != l.end(); ++i)
|
||||
{
|
||||
m_url_seeds.push_back(i->string());
|
||||
}
|
||||
if (i->type() != entry::string_t) continue;
|
||||
m_url_seeds.push_back(i->string());
|
||||
}
|
||||
}
|
||||
catch (type_error&) {}
|
||||
|
||||
// extract comment
|
||||
if (entry const* e = torrent_file.find_key("comment.utf-8"))
|
||||
|
@ -528,7 +593,13 @@ namespace libtorrent
|
|||
else if (entry const* e = torrent_file.find_key("created by"))
|
||||
{ m_created_by = e->string(); }
|
||||
|
||||
parse_info_section(torrent_file["info"]);
|
||||
entry const* info = torrent_file.find_key("info");
|
||||
if (info == 0 || info->type() != entry::dictionary_t)
|
||||
{
|
||||
error = "missing or invalid 'info' section in torrent file";
|
||||
return false;
|
||||
}
|
||||
return parse_info_section(*info, error);
|
||||
}
|
||||
|
||||
boost::optional<pt::ptime>
|
||||
|
@ -920,3 +991,4 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,16 @@ namespace libtorrent { namespace
|
|||
}
|
||||
|
||||
entry metadata = bdecode(m_metadata.begin(), m_metadata.end());
|
||||
m_torrent.set_metadata(metadata);
|
||||
std::string error;
|
||||
if (!m_torrent.set_metadata(metadata, error))
|
||||
{
|
||||
// this means the metadata is correct, since we
|
||||
// verified it against the info-hash, but we
|
||||
// failed to parse it. Pause the torrent
|
||||
// TODO: Post an alert!
|
||||
m_torrent.pause();
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear the storage for the bitfield
|
||||
std::vector<int>().swap(m_requested_metadata);
|
||||
|
|
Loading…
Reference in New Issue