parse_magnet_uri simplification
This commit is contained in:
parent
761384bcf1
commit
1a27ff7107
|
@ -84,9 +84,6 @@ namespace libtorrent {
|
|||
#endif
|
||||
TORRENT_EXTRA_EXPORT std::string base32decode(string_view s);
|
||||
|
||||
TORRENT_EXTRA_EXPORT string_view url_has_argument(
|
||||
string_view url, std::string argument, std::string::size_type* out_pos = nullptr);
|
||||
|
||||
// replaces \ with /
|
||||
TORRENT_EXTRA_EXPORT void convert_path_to_posix(std::string& path);
|
||||
#ifdef TORRENT_WINDOWS
|
||||
|
|
|
@ -473,29 +473,6 @@ namespace libtorrent {
|
|||
return pos + p;
|
||||
}
|
||||
|
||||
string_view url_has_argument(
|
||||
string_view url, std::string argument, std::string::size_type* out_pos)
|
||||
{
|
||||
auto i = url.find('?');
|
||||
if (i == std::string::npos) return {};
|
||||
++i;
|
||||
|
||||
argument += '=';
|
||||
|
||||
if (url.substr(i, argument.size()) == argument)
|
||||
{
|
||||
auto const pos = i + argument.size();
|
||||
if (out_pos) *out_pos = pos;
|
||||
return url.substr(pos, url.substr(pos).find('&'));
|
||||
}
|
||||
argument.insert(0, "&");
|
||||
i = find(url, argument, i);
|
||||
if (i == std::string::npos) return {};
|
||||
auto const pos = i + argument.size();
|
||||
if (out_pos) *out_pos = pos;
|
||||
return url.substr(pos, find(url, "&", pos) - pos);
|
||||
}
|
||||
|
||||
#if defined TORRENT_WINDOWS
|
||||
std::wstring convert_to_wstring(std::string const& s)
|
||||
{
|
||||
|
|
|
@ -132,6 +132,8 @@ namespace libtorrent {
|
|||
, void* userdata)
|
||||
{
|
||||
add_torrent_params params(std::move(sc));
|
||||
error_code ec;
|
||||
parse_magnet_uri(uri, params, ec);
|
||||
params.storage_mode = storage_mode;
|
||||
params.userdata = userdata;
|
||||
params.save_path = save_path;
|
||||
|
@ -139,20 +141,6 @@ namespace libtorrent {
|
|||
if (paused) params.flags |= add_torrent_params::flag_paused;
|
||||
else params.flags &= ~add_torrent_params::flag_paused;
|
||||
|
||||
error_code ec;
|
||||
string_view display_name = url_has_argument(uri, "dn");
|
||||
if (!display_name.empty()) params.name = unescape_string(display_name, ec);
|
||||
string_view tracker_string = url_has_argument(uri, "tr");
|
||||
if (!tracker_string.empty()) params.trackers.push_back(unescape_string(tracker_string, ec));
|
||||
|
||||
string_view btih = url_has_argument(uri, "xt");
|
||||
if (btih.empty()) return torrent_handle();
|
||||
|
||||
if (btih.substr(0, 9) != "urn:btih:") return torrent_handle();
|
||||
|
||||
if (btih.size() == 40 + 9) aux::from_hex({&btih[9], 40}, params.info_hash.data());
|
||||
else params.info_hash.assign(base32decode(btih.substr(9)).c_str());
|
||||
|
||||
return ses.add_torrent(std::move(params));
|
||||
}
|
||||
|
||||
|
@ -177,185 +165,162 @@ namespace libtorrent {
|
|||
void parse_magnet_uri(string_view uri, add_torrent_params& p, error_code& ec)
|
||||
{
|
||||
ec.clear();
|
||||
std::string name;
|
||||
std::string display_name;
|
||||
|
||||
string_view sv(uri);
|
||||
if (sv.substr(0, 8) != "magnet:?"_sv)
|
||||
{
|
||||
error_code e;
|
||||
string_view display_name = url_has_argument(uri, "dn");
|
||||
if (!display_name.empty()) name = unescape_string(display_name, e);
|
||||
ec = errors::unsupported_url_protocol;
|
||||
return;
|
||||
}
|
||||
sv = sv.substr(8);
|
||||
|
||||
// parse trackers out of the magnet link
|
||||
auto pos = std::string::npos;
|
||||
string_view url = url_has_argument(uri, "tr", &pos);
|
||||
int tier = 0;
|
||||
while (pos != std::string::npos)
|
||||
bool has_ih = false;
|
||||
while (!sv.empty())
|
||||
{
|
||||
// since we're about to assign tiers to the trackers, make sure the two
|
||||
// vectors are aligned
|
||||
if (p.tracker_tiers.size() != p.trackers.size())
|
||||
p.tracker_tiers.resize(p.trackers.size(), 0);
|
||||
string_view name;
|
||||
std::tie(name, sv) = split_string(sv, '=');
|
||||
string_view value;
|
||||
std::tie(value, sv) = split_string(sv, '&');
|
||||
|
||||
error_code e;
|
||||
std::string tracker = unescape_string(url, e);
|
||||
if (!e)
|
||||
if (name == "dn"_sv) // display name
|
||||
{
|
||||
p.trackers.push_back(std::move(tracker));
|
||||
p.tracker_tiers.push_back(tier++);
|
||||
error_code e;
|
||||
display_name = unescape_string(value, e);
|
||||
}
|
||||
pos = find(uri, "&tr=", pos);
|
||||
if (pos == std::string::npos) break;
|
||||
pos += 4;
|
||||
url = uri.substr(pos, find(uri, "&", pos) - pos);
|
||||
}
|
||||
else if (name == "tr"_sv) // tracker
|
||||
{
|
||||
// since we're about to assign tiers to the trackers, make sure the two
|
||||
// vectors are aligned
|
||||
if (p.tracker_tiers.size() != p.trackers.size())
|
||||
p.tracker_tiers.resize(p.trackers.size(), 0);
|
||||
error_code e;
|
||||
std::string tracker = unescape_string(value, e);
|
||||
if (!e)
|
||||
{
|
||||
p.trackers.push_back(std::move(tracker));
|
||||
p.tracker_tiers.push_back(tier++);
|
||||
}
|
||||
}
|
||||
else if (name == "ws"_sv) // web seed
|
||||
{
|
||||
error_code e;
|
||||
std::string webseed = unescape_string(value, e);
|
||||
if (!e) p.url_seeds.push_back(std::move(webseed));
|
||||
}
|
||||
else if (name == "xt"_sv)
|
||||
{
|
||||
std::string unescaped_btih;
|
||||
if (value.find('%') != string_view::npos)
|
||||
{
|
||||
unescaped_btih = unescape_string(value, ec);
|
||||
if (ec) return;
|
||||
value = unescaped_btih;
|
||||
}
|
||||
|
||||
// parse web seeds out of the magnet link
|
||||
pos = std::string::npos;
|
||||
url = url_has_argument(uri, "ws", &pos);
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
error_code e;
|
||||
std::string webseed = unescape_string(url, e);
|
||||
if (!e) p.url_seeds.push_back(std::move(webseed));
|
||||
pos = find(uri, "&ws=", pos);
|
||||
if (pos == std::string::npos) break;
|
||||
pos += 4;
|
||||
url = uri.substr(pos, find(uri, "&", pos) - pos);
|
||||
}
|
||||
if (value.substr(0, 9) != "urn:btih:") continue;
|
||||
value = value.substr(9);
|
||||
|
||||
string_view btih = url_has_argument(uri, "xt");
|
||||
std::string unescaped_btih;
|
||||
if (btih.empty())
|
||||
{
|
||||
ec = errors::missing_info_hash_in_uri;
|
||||
return;
|
||||
}
|
||||
if (btih.find('%') != string_view::npos)
|
||||
{
|
||||
unescaped_btih = unescape_string(btih, ec);
|
||||
if (ec) return;
|
||||
btih = unescaped_btih;
|
||||
}
|
||||
if (btih.substr(0, 9) != "urn:btih:")
|
||||
{
|
||||
ec = errors::missing_info_hash_in_uri;
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
sha1_hash info_hash;
|
||||
if (value.size() == 40) aux::from_hex({value.data(), 40}, info_hash.data());
|
||||
else if (value.size() == 32)
|
||||
{
|
||||
std::string const ih = base32decode(value);
|
||||
if (ih.size() != 20)
|
||||
{
|
||||
ec = errors::invalid_info_hash;
|
||||
return;
|
||||
}
|
||||
info_hash.assign(ih);
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = errors::invalid_info_hash;
|
||||
return;
|
||||
}
|
||||
p.info_hash = info_hash;
|
||||
has_ih = true;
|
||||
}
|
||||
else if (name == "so"_sv) // select-only (files)
|
||||
{
|
||||
// accept only digits, '-' and ','
|
||||
if (std::any_of(select.begin(), select.end(), [](char c)
|
||||
if (std::any_of(value.begin(), value.end(), [](char c)
|
||||
{ return !is_digit(c) && c != '-' && c != ','; }))
|
||||
break;
|
||||
continue;
|
||||
|
||||
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
|
||||
do
|
||||
{
|
||||
if (divider == 0) // no start index
|
||||
continue;
|
||||
if (divider == token.size() - 1) // no end index
|
||||
continue;
|
||||
string_view token;
|
||||
std::tie(token, value) = split_string(value, ',');
|
||||
|
||||
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 (token.empty()) 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;
|
||||
}
|
||||
int idx1, idx2;
|
||||
// TODO: what's the right number here?
|
||||
constexpr int max_index = 10000; // can't risk out of memory
|
||||
|
||||
if (int(p.file_priorities.size()) <= idx2)
|
||||
p.file_priorities.resize(std::size_t(idx2 + 1), dont_download);
|
||||
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;
|
||||
|
||||
for (int i = idx1; i <= idx2; i++)
|
||||
p.file_priorities[std::size_t(i)] = default_priority;
|
||||
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;
|
||||
|
||||
} while (!select.empty());
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (int(p.file_priorities.size()) <= idx2)
|
||||
p.file_priorities.resize(std::size_t(idx2 + 1), dont_download);
|
||||
|
||||
std::string::size_type peer_pos = std::string::npos;
|
||||
string_view peer = url_has_argument(uri, "x.pe", &peer_pos);
|
||||
while (!peer.empty())
|
||||
{
|
||||
error_code e;
|
||||
tcp::endpoint endp = parse_endpoint(peer, e);
|
||||
if (!e)
|
||||
p.peers.push_back(std::move(endp));
|
||||
|
||||
peer_pos = find(uri, "&x.pe=", peer_pos);
|
||||
if (peer_pos == std::string::npos) break;
|
||||
peer_pos += 6;
|
||||
peer = uri.substr(peer_pos, find(uri, "&", peer_pos) - peer_pos);
|
||||
}
|
||||
for (int i = idx1; i <= idx2; i++)
|
||||
p.file_priorities[std::size_t(i)] = default_priority;
|
||||
|
||||
} while (!value.empty());
|
||||
}
|
||||
else if (name == "x.pe")
|
||||
{
|
||||
error_code e;
|
||||
tcp::endpoint endp = parse_endpoint(value, e);
|
||||
if (!e) p.peers.push_back(std::move(endp));
|
||||
}
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
std::string::size_type node_pos = std::string::npos;
|
||||
string_view node = url_has_argument(uri, "dht", &node_pos);
|
||||
while (!node.empty())
|
||||
{
|
||||
std::string::size_type const divider = node.find_last_of(':');
|
||||
if (divider != std::string::npos)
|
||||
else if (name == "dht"_sv)
|
||||
{
|
||||
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);
|
||||
auto const divider = value.find_last_of(':');
|
||||
if (divider != std::string::npos)
|
||||
{
|
||||
int const port = std::atoi(value.substr(divider + 1).to_string().c_str());
|
||||
if (port > 0 && port < int(std::numeric_limits<std::uint16_t>::max()))
|
||||
p.dht_nodes.emplace_back(value.substr(0, divider).to_string(), port);
|
||||
}
|
||||
}
|
||||
|
||||
node_pos = find(uri, "&dht=", node_pos);
|
||||
if (node_pos == std::string::npos) break;
|
||||
node_pos += 5;
|
||||
node = uri.substr(node_pos, find(uri, "&", node_pos) - node_pos);
|
||||
}
|
||||
#endif
|
||||
|
||||
sha1_hash info_hash;
|
||||
if (btih.size() == 40 + 9) aux::from_hex({&btih[9], 40}, info_hash.data());
|
||||
else if (btih.size() == 32 + 9)
|
||||
{
|
||||
std::string const ih = base32decode(btih.substr(9));
|
||||
if (ih.size() != 20)
|
||||
{
|
||||
ec = errors::invalid_info_hash;
|
||||
return;
|
||||
}
|
||||
info_hash.assign(ih);
|
||||
}
|
||||
else
|
||||
|
||||
if (!has_ih)
|
||||
{
|
||||
ec = errors::invalid_info_hash;
|
||||
ec = errors::missing_info_hash_in_uri;
|
||||
return;
|
||||
}
|
||||
|
||||
p.info_hash = info_hash;
|
||||
if (!name.empty()) p.name = name;
|
||||
if (!display_name.empty()) p.name = display_name;
|
||||
}
|
||||
|
||||
add_torrent_params parse_magnet_uri(string_view uri)
|
||||
|
|
|
@ -145,6 +145,7 @@ TORRENT_TEST(magnet)
|
|||
"&tr=http://2"
|
||||
"&dn=foo"
|
||||
"&dht=127.0.0.1:43"
|
||||
"&xt=urn:ed2k:a0a9277894123b27945224fbac8366c9"
|
||||
"&xt=urn:btih:c352cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd");
|
||||
p.flags &= ~torrent_flags::paused;
|
||||
p.flags &= ~torrent_flags::auto_managed;
|
||||
|
@ -481,49 +482,57 @@ TORRENT_TEST(parse_magnet_select_only_multiple)
|
|||
, {no, yes, yes, yes, yes});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_index_and_range)
|
||||
TORRENT_TEST(parse_magnet_select_only_inverted_range)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=-4,3-,7-4,a,100000000&dht=10.0.0.1:1337&so=10"
|
||||
"&dn=foo&so=7-4,100000000&dht=10.0.0.1:1337&so=10"
|
||||
, {no, no, no, no, no, no, no, no, no, no, yes});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_index_bounds)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=100000000&dht=10.0.0.1:1337&so=10"
|
||||
, {no, no, no, no, no, no, no, no, no, no, yes});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_range1)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=-4", {});
|
||||
"&dn=foo&so=-4&so=1", {no, yes});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_range2)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=3-", {});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_range3)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=7-4", {});
|
||||
"&dn=foo&so=3-&so=1", {no, yes});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_index_character)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=a", {});
|
||||
"&dn=foo&so=a&so=1", {no, yes});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_index_value)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=100000000", {});
|
||||
"&dn=foo&so=100000000&so=1", {no, yes});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_no_value)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=&dht=10.0.0.1:1337&so=", {});
|
||||
}
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_no_values)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"&dn=foo&so=&dht=10.0.0.1:1337&so=", {});
|
||||
"&dn=foo&so=&dht=10.0.0.1:1337&so=,,1", {no, yes});
|
||||
}
|
||||
|
||||
|
||||
TORRENT_TEST(parse_magnet_select_only_invalid_quotes)
|
||||
{
|
||||
test_select_only("magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
|
|
|
@ -320,18 +320,6 @@ TORRENT_TEST(read_until)
|
|||
== "abcdesdf sdgf");
|
||||
}
|
||||
|
||||
TORRENT_TEST(url_has_argument)
|
||||
{
|
||||
TEST_CHECK(url_has_argument("http://127.0.0.1/test", "test") == "");
|
||||
TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24", "bar") == "");
|
||||
TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24", "foo") == "24");
|
||||
TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23", "foo") == "24");
|
||||
TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23", "bar") == "23");
|
||||
TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "bar") == "23");
|
||||
TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "a") == "e");
|
||||
TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "b") == "");
|
||||
}
|
||||
|
||||
TORRENT_TEST(path)
|
||||
{
|
||||
std::string path = "a\\b\\c";
|
||||
|
|
|
@ -609,7 +609,7 @@ TORRENT_TEST(test_move_storage_no_metadata)
|
|||
{
|
||||
lt::session ses(settings());
|
||||
error_code ec;
|
||||
add_torrent_params p = parse_magnet_uri("magnet?xt=urn:btih:abababababababababababababababababababab", ec);
|
||||
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab", ec);
|
||||
p.save_path = "save_path";
|
||||
torrent_handle h = ses.add_torrent(p);
|
||||
|
||||
|
@ -624,7 +624,7 @@ TORRENT_TEST(test_have_piece_no_metadata)
|
|||
{
|
||||
lt::session ses(settings());
|
||||
error_code ec;
|
||||
add_torrent_params p = parse_magnet_uri("magnet?xt=urn:btih:abababababababababababababababababababab", ec);
|
||||
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab", ec);
|
||||
p.save_path = "save_path";
|
||||
torrent_handle h = ses.add_torrent(p);
|
||||
|
||||
|
@ -655,7 +655,7 @@ TORRENT_TEST(test_read_piece_no_metadata)
|
|||
{
|
||||
lt::session ses(settings());
|
||||
error_code ec;
|
||||
add_torrent_params p = parse_magnet_uri("magnet?xt=urn:btih:abababababababababababababababababababab", ec);
|
||||
add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab", ec);
|
||||
p.save_path = "save_path";
|
||||
torrent_handle h = ses.add_torrent(p);
|
||||
|
||||
|
|
Loading…
Reference in New Issue