2007-12-03 07:03:16 +01:00
|
|
|
/*
|
|
|
|
|
2018-04-09 09:04:33 +02:00
|
|
|
Copyright (c) 2007-2018, Arvid Norberg
|
2007-12-03 07:03:16 +01:00
|
|
|
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/magnet_uri.hpp"
|
|
|
|
#include "libtorrent/session.hpp"
|
2015-03-15 00:10:20 +01:00
|
|
|
#include "libtorrent/aux_/escape_string.hpp"
|
2017-01-29 21:37:42 +01:00
|
|
|
#include "libtorrent/aux_/throw.hpp"
|
2015-09-18 06:23:45 +02:00
|
|
|
#include "libtorrent/torrent_status.hpp"
|
|
|
|
#include "libtorrent/torrent_info.hpp"
|
|
|
|
#include "libtorrent/announce_entry.hpp"
|
2016-05-23 14:15:39 +02:00
|
|
|
#include "libtorrent/hex.hpp" // to_hex, from_hex
|
2016-05-26 19:34:13 +02:00
|
|
|
#include "libtorrent/socket_io.hpp"
|
2007-12-03 07:03:16 +01:00
|
|
|
|
2017-04-12 19:00:57 +02:00
|
|
|
namespace libtorrent {
|
|
|
|
|
2007-12-03 07:03:16 +01:00
|
|
|
std::string make_magnet_uri(torrent_handle const& handle)
|
|
|
|
{
|
2009-04-13 06:22:03 +02:00
|
|
|
if (!handle.is_valid()) return "";
|
|
|
|
|
2013-04-28 00:35:06 +02:00
|
|
|
std::string ret;
|
2009-11-24 19:42:58 +01:00
|
|
|
sha1_hash const& ih = handle.info_hash();
|
2013-04-28 00:35:06 +02:00
|
|
|
ret += "magnet:?xt=urn:btih:";
|
2016-07-29 08:36:15 +02:00
|
|
|
ret += aux::to_hex(ih);
|
2007-12-03 07:03:16 +01:00
|
|
|
|
2013-03-04 04:24:53 +01:00
|
|
|
torrent_status st = handle.status(torrent_handle::query_name);
|
2013-04-28 00:35:06 +02:00
|
|
|
if (!st.name.empty())
|
|
|
|
{
|
|
|
|
ret += "&dn=";
|
2016-08-30 04:37:19 +02:00
|
|
|
ret += escape_string(st.name);
|
2013-04-28 00:35:06 +02:00
|
|
|
}
|
2009-04-13 06:22:03 +02:00
|
|
|
|
2016-08-30 04:37:19 +02:00
|
|
|
for (auto const& tr : handle.trackers())
|
2007-12-03 07:03:16 +01:00
|
|
|
{
|
2013-04-28 00:35:06 +02:00
|
|
|
ret += "&tr=";
|
2016-08-30 04:37:19 +02:00
|
|
|
ret += escape_string(tr.url);
|
2012-11-23 17:10:26 +01:00
|
|
|
}
|
2009-04-13 06:22:03 +02:00
|
|
|
|
2016-08-30 04:37:19 +02:00
|
|
|
for (auto const& s : handle.url_seeds())
|
2014-04-15 11:31:28 +02:00
|
|
|
{
|
|
|
|
ret += "&ws=";
|
2016-08-30 04:37:19 +02:00
|
|
|
ret += escape_string(s);
|
2014-04-15 11:31:28 +02:00
|
|
|
}
|
|
|
|
|
2009-04-13 06:22:03 +02:00
|
|
|
return ret;
|
2007-12-03 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2008-08-27 20:44:35 +02:00
|
|
|
std::string make_magnet_uri(torrent_info const& info)
|
|
|
|
{
|
2013-04-28 00:35:06 +02:00
|
|
|
std::string ret;
|
2009-11-24 19:42:58 +01:00
|
|
|
sha1_hash const& ih = info.info_hash();
|
2013-04-28 00:35:06 +02:00
|
|
|
ret += "magnet:?xt=urn:btih:";
|
2016-07-29 08:36:15 +02:00
|
|
|
ret += aux::to_hex(ih);
|
2008-08-27 20:44:35 +02:00
|
|
|
|
2009-04-13 06:22:03 +02:00
|
|
|
std::string const& name = info.name();
|
2008-08-27 20:44:35 +02:00
|
|
|
|
2013-04-28 00:35:06 +02:00
|
|
|
if (!name.empty())
|
|
|
|
{
|
|
|
|
ret += "&dn=";
|
2016-08-30 04:37:19 +02:00
|
|
|
ret += escape_string(name);
|
2013-04-28 00:35:06 +02:00
|
|
|
}
|
2009-04-13 06:22:03 +02:00
|
|
|
|
2016-08-30 04:37:19 +02:00
|
|
|
for (auto const& tr : info.trackers())
|
2008-08-27 20:44:35 +02:00
|
|
|
{
|
2013-04-28 00:35:06 +02:00
|
|
|
ret += "&tr=";
|
2016-08-30 04:37:19 +02:00
|
|
|
ret += escape_string(tr.url);
|
2008-08-27 20:44:35 +02:00
|
|
|
}
|
2009-04-13 06:22:03 +02:00
|
|
|
|
2016-08-30 04:37:19 +02:00
|
|
|
for (auto const& s : info.web_seeds())
|
2014-04-15 11:31:28 +02:00
|
|
|
{
|
2016-08-30 04:37:19 +02:00
|
|
|
if (s.type != web_seed_entry::url_seed) continue;
|
2014-04-15 11:31:28 +02:00
|
|
|
|
|
|
|
ret += "&ws=";
|
2016-08-30 04:37:19 +02:00
|
|
|
ret += escape_string(s.url);
|
2014-04-15 11:31:28 +02:00
|
|
|
}
|
|
|
|
|
2009-04-13 06:22:03 +02:00
|
|
|
return ret;
|
2008-08-27 20:44:35 +02:00
|
|
|
}
|
|
|
|
|
2018-04-26 09:01:14 +02:00
|
|
|
#if TORRENT_ABI_VERSION == 1
|
2014-07-05 16:10:25 +02:00
|
|
|
|
2015-08-16 18:17:23 +02:00
|
|
|
namespace {
|
|
|
|
torrent_handle add_magnet_uri_deprecated(session& ses, std::string const& uri
|
2018-11-12 01:51:05 +01:00
|
|
|
, add_torrent_params const& p, error_code& ec)
|
2015-08-16 18:17:23 +02:00
|
|
|
{
|
2018-11-12 01:51:05 +01:00
|
|
|
add_torrent_params params(p);
|
|
|
|
parse_magnet_uri(uri, params, ec);
|
2015-08-16 18:17:23 +02:00
|
|
|
if (ec) return torrent_handle();
|
2018-11-12 01:51:05 +01:00
|
|
|
return ses.add_torrent(std::move(params), ec);
|
2015-08-16 18:17:23 +02:00
|
|
|
}
|
2014-07-05 16:10:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
torrent_handle add_magnet_uri(session& ses, std::string const& uri
|
2018-11-12 01:51:05 +01:00
|
|
|
, add_torrent_params const& p, error_code& ec)
|
2014-07-05 16:10:25 +02:00
|
|
|
{
|
2018-11-12 01:51:05 +01:00
|
|
|
return add_magnet_uri_deprecated(ses, uri, p, ec);
|
2014-07-05 16:10:25 +02:00
|
|
|
}
|
|
|
|
|
2012-06-14 17:16:59 +02:00
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
2007-12-03 07:03:16 +01:00
|
|
|
torrent_handle add_magnet_uri(session& ses, std::string const& uri
|
2009-10-26 02:29:39 +01:00
|
|
|
, std::string const& save_path
|
2007-12-03 07:03:16 +01:00
|
|
|
, storage_mode_t storage_mode
|
|
|
|
, bool paused
|
|
|
|
, storage_constructor_type sc
|
|
|
|
, void* userdata)
|
|
|
|
{
|
2018-01-11 01:35:15 +01:00
|
|
|
add_torrent_params params(std::move(sc));
|
2018-10-05 02:19:27 +02:00
|
|
|
error_code ec;
|
|
|
|
parse_magnet_uri(uri, params, ec);
|
2014-07-05 16:10:25 +02:00
|
|
|
params.storage_mode = storage_mode;
|
|
|
|
params.userdata = userdata;
|
|
|
|
params.save_path = save_path;
|
|
|
|
|
|
|
|
if (paused) params.flags |= add_torrent_params::flag_paused;
|
|
|
|
else params.flags &= ~add_torrent_params::flag_paused;
|
2007-12-03 07:03:16 +01:00
|
|
|
|
2017-03-26 20:54:16 +02:00
|
|
|
return ses.add_torrent(std::move(params));
|
2007-12-03 07:03:16 +01:00
|
|
|
}
|
2008-08-03 17:14:08 +02:00
|
|
|
|
|
|
|
torrent_handle add_magnet_uri(session& ses, std::string const& uri
|
2018-11-12 01:51:05 +01:00
|
|
|
, add_torrent_params const& p)
|
2009-02-26 08:09:56 +01:00
|
|
|
{
|
|
|
|
error_code ec;
|
2018-11-12 01:51:05 +01:00
|
|
|
torrent_handle ret = add_magnet_uri_deprecated(ses, uri, p, ec);
|
2017-01-29 21:37:42 +01:00
|
|
|
if (ec) aux::throw_ex<system_error>(ec);
|
2009-02-26 08:09:56 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2012-06-14 17:16:59 +02:00
|
|
|
#endif // BOOST_NO_EXCEPTIONS
|
2018-04-26 09:01:14 +02:00
|
|
|
#endif // TORRENT_ABI_VERSION
|
2012-06-14 17:16:59 +02:00
|
|
|
|
2017-12-13 17:42:55 +01:00
|
|
|
add_torrent_params parse_magnet_uri(string_view uri, error_code& ec)
|
2008-08-03 17:14:08 +02:00
|
|
|
{
|
2017-12-13 17:42:55 +01:00
|
|
|
add_torrent_params ret;
|
|
|
|
parse_magnet_uri(uri, ret, ec);
|
|
|
|
return ret;
|
2017-10-14 16:40:48 +02:00
|
|
|
}
|
|
|
|
|
2017-12-13 17:42:55 +01:00
|
|
|
void parse_magnet_uri(string_view uri, add_torrent_params& p, error_code& ec)
|
2017-10-14 16:40:48 +02:00
|
|
|
{
|
2013-02-28 05:31:55 +01:00
|
|
|
ec.clear();
|
2018-10-05 02:19:27 +02:00
|
|
|
std::string display_name;
|
2008-08-03 17:14:08 +02:00
|
|
|
|
2018-10-05 02:19:27 +02:00
|
|
|
string_view sv(uri);
|
|
|
|
if (sv.substr(0, 8) != "magnet:?"_sv)
|
2015-08-16 18:17:23 +02:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
ec = errors::unsupported_url_protocol;
|
|
|
|
return;
|
2015-08-16 18:17:23 +02:00
|
|
|
}
|
2018-10-05 02:19:27 +02:00
|
|
|
sv = sv.substr(8);
|
2012-03-08 10:54:44 +01:00
|
|
|
|
2016-11-10 01:53:55 +01:00
|
|
|
int tier = 0;
|
2018-10-05 02:19:27 +02:00
|
|
|
bool has_ih = false;
|
|
|
|
while (!sv.empty())
|
2012-03-08 10:54:44 +01:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
string_view name;
|
|
|
|
std::tie(name, sv) = split_string(sv, '=');
|
|
|
|
string_view value;
|
|
|
|
std::tie(value, sv) = split_string(sv, '&');
|
|
|
|
|
2019-02-15 14:34:27 +01:00
|
|
|
// parameter names are allowed to have a .<number>-suffix.
|
|
|
|
// the number has no meaning, just strip it
|
|
|
|
// if the characters after the period are not digits, don't strip
|
|
|
|
// anything
|
|
|
|
string_view number;
|
|
|
|
string_view stripped_name;
|
|
|
|
std::tie(stripped_name, number) = split_string(name, '.');
|
|
|
|
if (std::all_of(number.begin(), number.end(), [](char const c) { return is_digit(c); } ))
|
|
|
|
name = stripped_name;
|
|
|
|
|
2018-10-05 02:19:27 +02:00
|
|
|
if (name == "dn"_sv) // display name
|
2017-10-11 11:19:02 +02:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
error_code e;
|
|
|
|
display_name = unescape_string(value, e);
|
2017-10-11 11:19:02 +02:00
|
|
|
}
|
2018-10-05 02:19:27 +02:00
|
|
|
else if (name == "tr"_sv) // tracker
|
2017-12-01 12:40:19 +01:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
// 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)
|
2017-12-01 12:40:19 +01:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
p.trackers.push_back(std::move(tracker));
|
|
|
|
p.tracker_tiers.push_back(tier++);
|
2017-12-01 12:40:19 +01:00
|
|
|
}
|
2018-10-05 02:19:27 +02:00
|
|
|
}
|
|
|
|
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)
|
2017-12-01 12:40:19 +01:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
unescaped_btih = unescape_string(value, ec);
|
|
|
|
if (ec) return;
|
|
|
|
value = unescaped_btih;
|
2017-12-01 12:40:19 +01:00
|
|
|
}
|
|
|
|
|
2018-10-05 02:19:27 +02:00
|
|
|
if (value.substr(0, 9) != "urn:btih:") continue;
|
|
|
|
value = value.substr(9);
|
2017-12-01 12:40:19 +01:00
|
|
|
|
2018-10-05 02:19:27 +02:00
|
|
|
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(value.begin(), value.end(), [](char c)
|
|
|
|
{ return !is_digit(c) && c != '-' && c != ','; }))
|
|
|
|
continue;
|
2016-05-26 19:34:13 +02:00
|
|
|
|
2018-10-05 02:19:27 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
string_view token;
|
|
|
|
std::tie(token, value) = split_string(value, ',');
|
|
|
|
|
|
|
|
if (token.empty()) continue;
|
|
|
|
|
|
|
|
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 (!value.empty());
|
|
|
|
}
|
|
|
|
else if (name == "x.pe")
|
2010-11-07 20:18:16 +01:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
error_code e;
|
|
|
|
tcp::endpoint endp = parse_endpoint(value, e);
|
|
|
|
if (!e) p.peers.push_back(std::move(endp));
|
2010-11-07 20:18:16 +01:00
|
|
|
}
|
2018-10-05 02:19:27 +02:00
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
|
|
|
else if (name == "dht"_sv)
|
2015-10-01 07:05:00 +02:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
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);
|
|
|
|
}
|
2015-10-01 07:05:00 +02:00
|
|
|
}
|
2018-10-05 02:19:27 +02:00
|
|
|
#endif
|
2015-10-01 07:05:00 +02:00
|
|
|
}
|
2018-10-05 02:19:27 +02:00
|
|
|
|
|
|
|
if (!has_ih)
|
2015-10-01 07:05:00 +02:00
|
|
|
{
|
2018-10-05 02:19:27 +02:00
|
|
|
ec = errors::missing_info_hash_in_uri;
|
2017-12-13 17:42:55 +01:00
|
|
|
return;
|
2015-10-01 07:05:00 +02:00
|
|
|
}
|
2008-08-03 17:14:08 +02:00
|
|
|
|
2018-10-05 02:19:27 +02:00
|
|
|
if (!display_name.empty()) p.name = display_name;
|
2008-08-03 17:14:08 +02:00
|
|
|
}
|
2017-11-05 17:11:28 +01:00
|
|
|
|
|
|
|
add_torrent_params parse_magnet_uri(string_view uri)
|
|
|
|
{
|
|
|
|
error_code ec;
|
2017-12-13 17:42:55 +01:00
|
|
|
add_torrent_params ret;
|
|
|
|
parse_magnet_uri(uri, ret, ec);
|
2017-11-05 17:11:28 +01:00
|
|
|
if (ec) aux::throw_ex<system_error>(ec);
|
|
|
|
return ret;
|
|
|
|
}
|
2007-12-03 07:03:16 +01:00
|
|
|
}
|