implemented support magnet URI extension, select specific file indices for download, BEP53 (#2578)

This commit is contained in:
Alden Torres 2017-12-01 06:40:19 -05:00 committed by Arvid Norberg
parent 28f096e94e
commit 163d13e1d2
5 changed files with 194 additions and 7 deletions

View File

@ -1,3 +1,5 @@
* implemented support magnet URI extension, select specific file indices
for download, BEP53
* make tracker keys multi-homed. remove set_key() function on session.
* add API to query whether alerts have been dropped or not
* add flags()/set_flags()/unset_flags() to torrent_handle, deprecate individual functions

View File

@ -51,6 +51,8 @@ extensions
scale well with the size of the content.
* share-mode. This is a special mode torrents can be put in to optimize share
ratio rather than downloading the torrent.
* supports the Magnet URI extension - Select specific file indices for
download. `BEP 53`_.
.. _article: utp.html
.. _extensions: manual-ref.html#extensions

View File

@ -48,4 +48,3 @@ constexpr download_priority_t top_priority{7};
}
#endif

View File

@ -258,6 +258,65 @@ namespace libtorrent {
return p;
}
auto select_pos = std::string::npos;
string_view select = url_has_argument(uri, "so", &select_pos);
while (!select.empty())
{
// parse the ranges or indices
do
{
// accept only digits, '-' and ','
if (std::any_of(select.begin(), select.end(), [](char c)
{ return !is_digit(c) && c != '-' && c != ','; }))
break;
string_view token;
std::tie(token, select) = split_string(select, ',');
int idx1, idx2;
// TODO: what's the right number here?
constexpr int max_index = 10000; // can't risk out of memory
auto const divider = token.find_first_of('-');
if (divider != std::string::npos) // it's a range
{
if (divider == 0) // no start index
continue;
if (divider == token.size() - 1) // no end index
continue;
idx1 = std::atoi(token.substr(0, divider).to_string().c_str());
if (idx1 < 0 || idx1 > max_index) // invalid index
continue;
idx2 = std::atoi(token.substr(divider + 1).to_string().c_str());
if (idx2 < 0 || idx2 > max_index) // invalid index
continue;
if (idx1 > idx2) // wrong range limits
continue;
}
else // it's an index
{
idx1 = std::atoi(token.to_string().c_str());
if (idx1 < 0 || idx1 > max_index) // invalid index
continue;
idx2 = idx1;
}
if (int(p.file_priorities.size()) <= idx2)
p.file_priorities.resize(std::size_t(idx2 + 1), dont_download);
for (int i = idx1; i <= idx2; i++)
p.file_priorities[std::size_t(i)] = default_priority;
} while (!select.empty());
select_pos = find(uri, "&so=", select_pos);
if (select_pos == std::string::npos) break;
select_pos += 4;
select = uri.substr(select_pos, find(uri, "&", select_pos) - select_pos);
}
std::string::size_type peer_pos = std::string::npos;
string_view peer = url_has_argument(uri, "x.pe", &peer_pos);
while (!peer.empty())
@ -265,7 +324,7 @@ namespace libtorrent {
error_code e;
tcp::endpoint endp = parse_endpoint(peer, e);
if (!e)
p.peers.push_back(endp);
p.peers.push_back(std::move(endp));
peer_pos = find(uri, "&x.pe=", peer_pos);
if (peer_pos == std::string::npos) break;
@ -278,12 +337,12 @@ namespace libtorrent {
string_view node = url_has_argument(uri, "dht", &node_pos);
while (!node.empty())
{
std::string::size_type divider = node.find_last_of(':');
std::string::size_type const divider = node.find_last_of(':');
if (divider != std::string::npos)
{
int port = atoi(node.substr(divider + 1).to_string().c_str());
if (port != 0)
p.dht_nodes.push_back(std::make_pair(node.substr(0, divider).to_string(), port));
int const port = std::atoi(node.substr(divider + 1).to_string().c_str());
if (port > 0 && port < int(std::numeric_limits<std::uint16_t>::max()))
p.dht_nodes.emplace_back(node.substr(0, divider).to_string(), port);
}
node_pos = find(uri, "&dht=", node_pos);
@ -297,7 +356,7 @@ namespace libtorrent {
if (btih.size() == 40 + 9) aux::from_hex({&btih[9], 40}, info_hash.data());
else if (btih.size() == 32 + 9)
{
std::string ih = base32decode(btih.substr(9));
std::string const ih = base32decode(btih.substr(9));
if (ih.size() != 20)
{
ec = errors::invalid_info_hash;

View File

@ -476,3 +476,128 @@ TORRENT_TEST(invalid_web_seed_escaping)
TEST_CHECK(ec);
}
TORRENT_TEST(parse_magnet_select_only)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=0,2,4,6-8", ec);
TEST_CHECK(!ec);
auto const yes = default_priority;
auto const no = dont_download;
std::vector<download_priority_t> result = std::vector<download_priority_t>
{yes, no, yes, no, yes, no, yes, yes, yes};
TEST_CHECK(p.file_priorities == result);
}
TORRENT_TEST(parse_magnet_select_only_overlap_range)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=0,2-4,3-5&dht=10.0.0.1:1337", ec);
TEST_CHECK(!ec);
auto const yes = default_priority;
auto const no = dont_download;
std::vector<download_priority_t> result = std::vector<download_priority_t>
{yes, no, yes, yes, yes, yes};
TEST_CHECK(p.file_priorities == result);
}
TORRENT_TEST(parse_magnet_select_only_multiple)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=2-4&dht=10.0.0.1:1337&so=1", ec);
TEST_CHECK(!ec);
auto const yes = default_priority;
auto const no = dont_download;
std::vector<download_priority_t> result = std::vector<download_priority_t>
{no, yes, yes, yes, yes};
TEST_CHECK(p.file_priorities == result);
}
TORRENT_TEST(parse_magnet_select_only_invalid_index_and_range)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=-4,3-,7-4,a,100000000&dht=10.0.0.1:1337&so=10", ec);
TEST_CHECK(!ec);
auto const yes = default_priority;
auto const no = dont_download;
std::vector<download_priority_t> result = std::vector<download_priority_t>
{no, no, no, no, no, no, no, no, no, no, yes};
TEST_CHECK(p.file_priorities == result);
}
TORRENT_TEST(parse_magnet_select_only_invalid_range1)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=-4", ec);
TEST_CHECK(!ec);
TEST_CHECK(p.file_priorities.empty());
}
TORRENT_TEST(parse_magnet_select_only_invalid_range2)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=3-", ec);
TEST_CHECK(!ec);
TEST_CHECK(p.file_priorities.empty());
}
TORRENT_TEST(parse_magnet_select_only_invalid_range3)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=7-4", ec);
TEST_CHECK(!ec);
TEST_CHECK(p.file_priorities.empty());
}
TORRENT_TEST(parse_magnet_select_only_invalid_index_character)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=a", ec);
TEST_CHECK(!ec);
TEST_CHECK(p.file_priorities.empty());
}
TORRENT_TEST(parse_magnet_select_only_invalid_index_value)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=100000000", ec);
TEST_CHECK(!ec);
TEST_CHECK(p.file_priorities.empty());
}
TORRENT_TEST(parse_magnet_select_only_invalid_no_values)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=&dht=10.0.0.1:1337&so=", ec);
TEST_CHECK(!ec);
TEST_CHECK(p.file_priorities.empty());
}
TORRENT_TEST(parse_magnet_select_only_invalid_quotes)
{
error_code ec;
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
"&dn=foo&so=\"1,2\"", ec);
TEST_CHECK(!ec);
TEST_CHECK(p.file_priorities.empty());
}