mirror of
https://github.com/transmission/transmission
synced 2025-01-30 10:52:00 +00:00
refactor: misc-use-anonymous-namespace pt. 4 (#4550)
This commit is contained in:
parent
9678b26984
commit
cb10255ef1
12 changed files with 1495 additions and 1455 deletions
|
@ -41,16 +41,9 @@
|
|||
|
||||
using namespace std::literals;
|
||||
|
||||
/****
|
||||
***** ANNOUNCE
|
||||
****/
|
||||
|
||||
[[nodiscard]] static constexpr std::string_view get_event_string(tr_announce_request const& req)
|
||||
namespace
|
||||
{
|
||||
return req.partial_seed && (req.event != TR_ANNOUNCE_EVENT_STOPPED) ? "paused"sv : tr_announce_event_get_string(req.event);
|
||||
}
|
||||
|
||||
static void verboseLog(std::string_view description, tr_direction direction, std::string_view message)
|
||||
void verboseLog(std::string_view description, tr_direction direction, std::string_view message)
|
||||
{
|
||||
auto& out = std::cerr;
|
||||
static bool const verbose = tr_env_key_exists("TR_CURL_VERBOSE");
|
||||
|
@ -76,7 +69,270 @@ static void verboseLog(std::string_view description, tr_direction direction, std
|
|||
out << std::endl << "[b64]"sv << direction_sv << tr_base64_encode(message) << std::endl;
|
||||
}
|
||||
|
||||
static auto constexpr MaxBencDepth = 8;
|
||||
auto constexpr MaxBencDepth = 8;
|
||||
} // namespace
|
||||
|
||||
/****
|
||||
***** ANNOUNCE
|
||||
****/
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace announce_helpers
|
||||
{
|
||||
[[nodiscard]] constexpr std::string_view get_event_string(tr_announce_request const& req)
|
||||
{
|
||||
return req.partial_seed && (req.event != TR_ANNOUNCE_EVENT_STOPPED) ? "paused"sv : tr_announce_event_get_string(req.event);
|
||||
}
|
||||
|
||||
struct http_announce_data
|
||||
{
|
||||
http_announce_data(tr_sha1_digest_t info_hash_in, tr_announce_response_func on_response_in, std::string_view log_name_in)
|
||||
: info_hash{ info_hash_in }
|
||||
, on_response{ std::move(on_response_in) }
|
||||
, log_name{ log_name_in }
|
||||
{
|
||||
}
|
||||
|
||||
tr_sha1_digest_t info_hash = {};
|
||||
std::optional<tr_announce_response> previous_response;
|
||||
|
||||
tr_announce_response_func on_response;
|
||||
bool http_success = false;
|
||||
|
||||
uint8_t requests_sent_count = {};
|
||||
uint8_t requests_answered_count = {};
|
||||
|
||||
std::string log_name;
|
||||
};
|
||||
|
||||
bool handleAnnounceResponse(tr_web::FetchResponse const& web_response, tr_announce_response* const response)
|
||||
{
|
||||
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
||||
auto const& log_name = static_cast<http_announce_data const*>(vdata)->log_name;
|
||||
|
||||
response->did_connect = did_connect;
|
||||
response->did_timeout = did_timeout;
|
||||
tr_logAddTrace("Got announce response", log_name);
|
||||
|
||||
if (status != HTTP_OK)
|
||||
{
|
||||
auto const* const response_str = tr_webGetResponseStr(status);
|
||||
response->errmsg = fmt::format(FMT_STRING("Tracker HTTP response {:d} ({:s}"), status, response_str);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
tr_announcerParseHttpAnnounceResponse(*response, body, log_name);
|
||||
|
||||
if (!std::empty(response->pex6))
|
||||
{
|
||||
tr_logAddTrace(fmt::format("got a peers6 length of {}", std::size(response->pex6)), log_name);
|
||||
}
|
||||
|
||||
if (!std::empty(response->pex))
|
||||
{
|
||||
tr_logAddTrace(fmt::format("got a peers length of {}", std::size(response->pex)), log_name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void onAnnounceDone(tr_web::FetchResponse const& web_response)
|
||||
{
|
||||
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
||||
auto* data = static_cast<struct http_announce_data*>(vdata);
|
||||
|
||||
++data->requests_answered_count;
|
||||
|
||||
// If another request already succeeded (or we don't have a registered callback),
|
||||
// skip processing this response:
|
||||
if (!data->http_success && data->on_response)
|
||||
{
|
||||
tr_announce_response response;
|
||||
response.info_hash = data->info_hash;
|
||||
|
||||
data->http_success = handleAnnounceResponse(web_response, &response);
|
||||
|
||||
if (data->http_success)
|
||||
{
|
||||
data->on_response(response);
|
||||
}
|
||||
else if (data->requests_answered_count == data->requests_sent_count)
|
||||
{
|
||||
auto const* response_used = &response;
|
||||
|
||||
// All requests have been answered, but none were successful.
|
||||
// Choose the one that went further to report.
|
||||
if (data->previous_response && !response.did_connect && !response.did_timeout)
|
||||
{
|
||||
response_used = &*data->previous_response;
|
||||
}
|
||||
|
||||
data->on_response(*response_used);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is still one request pending that might succeed, so store
|
||||
// the response for later. There is only room for 1 previous response,
|
||||
// because there can be at most 2 requests.
|
||||
TR_ASSERT(!data->previous_response);
|
||||
data->previous_response = std::move(response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_logAddTrace("Ignoring redundant announce response", data->log_name);
|
||||
}
|
||||
|
||||
// Free data if no more responses are expected:
|
||||
if (data->requests_answered_count == data->requests_sent_count)
|
||||
{
|
||||
delete data;
|
||||
}
|
||||
}
|
||||
|
||||
void announce_url_new(tr_urlbuf& url, tr_session const* session, tr_announce_request const& req)
|
||||
{
|
||||
url.clear();
|
||||
auto out = std::back_inserter(url);
|
||||
|
||||
auto escaped_info_hash = tr_urlbuf{};
|
||||
tr_urlPercentEncode(std::back_inserter(escaped_info_hash), req.info_hash);
|
||||
|
||||
fmt::format_to(
|
||||
out,
|
||||
"{url}"
|
||||
"{sep}info_hash={info_hash}"
|
||||
"&peer_id={peer_id}"
|
||||
"&port={port}"
|
||||
"&uploaded={uploaded}"
|
||||
"&downloaded={downloaded}"
|
||||
"&left={left}"
|
||||
"&numwant={numwant}"
|
||||
"&key={key}"
|
||||
"&compact=1"
|
||||
"&supportcrypto=1",
|
||||
fmt::arg("url", req.announce_url),
|
||||
fmt::arg("sep", tr_strvContains(req.announce_url.sv(), '?') ? '&' : '?'),
|
||||
fmt::arg("info_hash", std::data(escaped_info_hash)),
|
||||
fmt::arg("peer_id", std::string_view{ std::data(req.peer_id), std::size(req.peer_id) }),
|
||||
fmt::arg("port", req.port.host()),
|
||||
fmt::arg("uploaded", req.up),
|
||||
fmt::arg("downloaded", req.down),
|
||||
fmt::arg("left", req.leftUntilComplete),
|
||||
fmt::arg("numwant", req.numwant),
|
||||
fmt::arg("key", req.key));
|
||||
|
||||
if (session->encryptionMode() == TR_ENCRYPTION_REQUIRED)
|
||||
{
|
||||
fmt::format_to(out, "&requirecrypto=1");
|
||||
}
|
||||
|
||||
if (req.corrupt != 0)
|
||||
{
|
||||
fmt::format_to(out, "&corrupt={}", req.corrupt);
|
||||
}
|
||||
|
||||
if (auto const str = get_event_string(req); !std::empty(str))
|
||||
{
|
||||
fmt::format_to(out, "&event={}", str);
|
||||
}
|
||||
|
||||
if (!std::empty(req.tracker_id))
|
||||
{
|
||||
fmt::format_to(out, "&trackerid={}", req.tracker_id);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string format_ip_arg(std::string_view ip)
|
||||
{
|
||||
return fmt::format("&ip={:s}", ip);
|
||||
}
|
||||
|
||||
} // namespace announce_helpers
|
||||
} // namespace
|
||||
|
||||
void tr_tracker_http_announce(
|
||||
tr_session const* session,
|
||||
tr_announce_request const& request,
|
||||
tr_announce_response_func on_response)
|
||||
{
|
||||
using namespace announce_helpers;
|
||||
|
||||
auto* const d = new http_announce_data{ request.info_hash, std::move(on_response), request.log_name };
|
||||
|
||||
/* There are two alternative techniques for announcing both IPv4 and
|
||||
IPv6 addresses. Previous version of BEP-7 suggests adding "ipv4="
|
||||
and "ipv6=" parameters to the announce URL, while OpenTracker and
|
||||
newer version of BEP-7 requires that peers announce once per each
|
||||
public address they want to use.
|
||||
|
||||
We should ensure that we send the announce both via IPv6 and IPv4,
|
||||
and to be safe we also add the "ipv6=" and "ipv4=" parameters, if
|
||||
we already have them. Our global IPv6 address is computed for the
|
||||
LTEP handshake, so this comes for free. Our public IPv4 address
|
||||
may have been returned from a previous announce and stored in the
|
||||
session.
|
||||
*/
|
||||
auto url = tr_urlbuf{};
|
||||
announce_url_new(url, session, request);
|
||||
auto options = tr_web::FetchOptions{ url.sv(), onAnnounceDone, d };
|
||||
options.timeout_secs = TR_ANNOUNCE_TIMEOUT_SEC;
|
||||
options.sndbuf = 4096;
|
||||
options.rcvbuf = 4096;
|
||||
|
||||
auto do_make_request = [&](std::string_view const& protocol_name, tr_web::FetchOptions&& opt)
|
||||
{
|
||||
tr_logAddTrace(fmt::format("Sending {} announce to libcurl: '{}'", protocol_name, opt.url), request.log_name);
|
||||
session->fetch(std::move(opt));
|
||||
};
|
||||
|
||||
auto const [ipv6, ipv6_is_any] = session->publicAddress(TR_AF_INET6);
|
||||
|
||||
/*
|
||||
* Before Curl 7.77.0, if we explicitly choose the IP version we want
|
||||
* to use, it is still possible that the wrong one is used. The workaround
|
||||
* is expensive (disabling DNS cache), so instead we have to make do with
|
||||
* a request that we don't know if will go through IPv6 or IPv4.
|
||||
*/
|
||||
static bool const use_curl_workaround = curl_version_info(CURLVERSION_NOW)->version_num < CURL_VERSION_BITS(7, 77, 0);
|
||||
if (use_curl_workaround)
|
||||
{
|
||||
if (session->useAnnounceIP())
|
||||
{
|
||||
options.url += format_ip_arg(session->announceIP());
|
||||
}
|
||||
|
||||
d->requests_sent_count = 1;
|
||||
do_make_request(""sv, std::move(options));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (session->useAnnounceIP() || ipv6_is_any)
|
||||
{
|
||||
if (session->useAnnounceIP())
|
||||
{
|
||||
options.url += format_ip_arg(session->announceIP());
|
||||
}
|
||||
d->requests_sent_count = 1;
|
||||
do_make_request(""sv, std::move(options));
|
||||
}
|
||||
else
|
||||
{
|
||||
d->requests_sent_count = 2;
|
||||
|
||||
// First try to send the announce via IPv4:
|
||||
auto ipv4_options = options;
|
||||
ipv4_options.ip_proto = tr_web::FetchOptions::IPProtocol::V4;
|
||||
do_make_request("IPv4"sv, std::move(ipv4_options));
|
||||
|
||||
// Then try to send via IPv6:
|
||||
options.ip_proto = tr_web::FetchOptions::IPProtocol::V6;
|
||||
do_make_request("IPv6"sv, std::move(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::string_view benc, std::string_view log_name)
|
||||
{
|
||||
|
@ -216,263 +472,113 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
|
|||
}
|
||||
}
|
||||
|
||||
struct http_announce_data
|
||||
// ---
|
||||
|
||||
namespace
|
||||
{
|
||||
http_announce_data(tr_sha1_digest_t info_hash_in, tr_announce_response_func on_response_in, std::string_view log_name_in)
|
||||
: info_hash{ info_hash_in }
|
||||
, on_response{ std::move(on_response_in) }
|
||||
, log_name{ log_name_in }
|
||||
namespace scrape_helpers
|
||||
{
|
||||
class scrape_data
|
||||
{
|
||||
public:
|
||||
scrape_data(tr_scrape_response_func response_func, std::string_view log_name)
|
||||
: response_func_{ std::move(response_func) }
|
||||
, log_name_{ log_name }
|
||||
{
|
||||
}
|
||||
|
||||
tr_sha1_digest_t info_hash = {};
|
||||
std::optional<tr_announce_response> previous_response;
|
||||
[[nodiscard]] constexpr auto& response() noexcept
|
||||
{
|
||||
return response_;
|
||||
}
|
||||
|
||||
tr_announce_response_func on_response;
|
||||
bool http_success = false;
|
||||
[[nodiscard]] constexpr auto const& log_name() const noexcept
|
||||
{
|
||||
return log_name_;
|
||||
}
|
||||
|
||||
uint8_t requests_sent_count = {};
|
||||
uint8_t requests_answered_count = {};
|
||||
void invoke_callback() const
|
||||
{
|
||||
if (response_func_)
|
||||
{
|
||||
response_func_(response_);
|
||||
}
|
||||
}
|
||||
|
||||
std::string log_name;
|
||||
private:
|
||||
tr_scrape_response response_ = {};
|
||||
tr_scrape_response_func response_func_ = {};
|
||||
std::string log_name_;
|
||||
};
|
||||
|
||||
static bool handleAnnounceResponse(tr_web::FetchResponse const& web_response, tr_announce_response* const response)
|
||||
void onScrapeDone(tr_web::FetchResponse const& web_response)
|
||||
{
|
||||
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
||||
auto const& log_name = static_cast<http_announce_data const*>(vdata)->log_name;
|
||||
auto* const data = static_cast<scrape_data*>(vdata);
|
||||
|
||||
response->did_connect = did_connect;
|
||||
response->did_timeout = did_timeout;
|
||||
tr_logAddTrace("Got announce response", log_name);
|
||||
auto& response = data->response();
|
||||
response.did_connect = did_connect;
|
||||
response.did_timeout = did_timeout;
|
||||
|
||||
auto const scrape_url_sv = response.scrape_url.sv();
|
||||
tr_logAddTrace(fmt::format("Got scrape response for '{}'", scrape_url_sv), data->log_name());
|
||||
|
||||
if (status != HTTP_OK)
|
||||
{
|
||||
auto const* const response_str = tr_webGetResponseStr(status);
|
||||
response->errmsg = fmt::format(FMT_STRING("Tracker HTTP response {:d} ({:s}"), status, response_str);
|
||||
|
||||
return false;
|
||||
response.errmsg = fmt::format(FMT_STRING("Tracker HTTP response {:d} ({:s})"), status, response_str);
|
||||
}
|
||||
|
||||
tr_announcerParseHttpAnnounceResponse(*response, body, log_name);
|
||||
|
||||
if (!std::empty(response->pex6))
|
||||
else if (!std::empty(body))
|
||||
{
|
||||
tr_logAddTrace(fmt::format("got a peers6 length of {}", std::size(response->pex6)), log_name);
|
||||
tr_announcerParseHttpScrapeResponse(response, body, data->log_name());
|
||||
}
|
||||
|
||||
if (!std::empty(response->pex))
|
||||
{
|
||||
tr_logAddTrace(fmt::format("got a peers length of {}", std::size(response->pex)), log_name);
|
||||
}
|
||||
|
||||
return true;
|
||||
data->invoke_callback();
|
||||
delete data;
|
||||
}
|
||||
|
||||
static void onAnnounceDone(tr_web::FetchResponse const& web_response)
|
||||
void scrape_url_new(tr_pathbuf& scrape_url, tr_scrape_request const& req)
|
||||
{
|
||||
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
||||
auto* data = static_cast<struct http_announce_data*>(vdata);
|
||||
scrape_url = req.scrape_url.sv();
|
||||
char delimiter = tr_strvContains(scrape_url, '?') ? '&' : '?';
|
||||
|
||||
++data->requests_answered_count;
|
||||
|
||||
// If another request already succeeded (or we don't have a registered callback),
|
||||
// skip processing this response:
|
||||
if (!data->http_success && data->on_response)
|
||||
for (int i = 0; i < req.info_hash_count; ++i)
|
||||
{
|
||||
tr_announce_response response;
|
||||
response.info_hash = data->info_hash;
|
||||
|
||||
data->http_success = handleAnnounceResponse(web_response, &response);
|
||||
|
||||
if (data->http_success)
|
||||
{
|
||||
data->on_response(response);
|
||||
}
|
||||
else if (data->requests_answered_count == data->requests_sent_count)
|
||||
{
|
||||
auto const* response_used = &response;
|
||||
|
||||
// All requests have been answered, but none were successful.
|
||||
// Choose the one that went further to report.
|
||||
if (data->previous_response && !response.did_connect && !response.did_timeout)
|
||||
{
|
||||
response_used = &*data->previous_response;
|
||||
}
|
||||
|
||||
data->on_response(*response_used);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is still one request pending that might succeed, so store
|
||||
// the response for later. There is only room for 1 previous response,
|
||||
// because there can be at most 2 requests.
|
||||
TR_ASSERT(!data->previous_response);
|
||||
data->previous_response = std::move(response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_logAddTrace("Ignoring redundant announce response", data->log_name);
|
||||
}
|
||||
|
||||
// Free data if no more responses are expected:
|
||||
if (data->requests_answered_count == data->requests_sent_count)
|
||||
{
|
||||
delete data;
|
||||
scrape_url.append(delimiter, "info_hash=");
|
||||
tr_urlPercentEncode(std::back_inserter(scrape_url), req.info_hash[i]);
|
||||
delimiter = '&';
|
||||
}
|
||||
}
|
||||
} // namespace scrape_helpers
|
||||
} // namespace
|
||||
|
||||
namespace tr_tracker_announce_helpers
|
||||
void tr_tracker_http_scrape(tr_session const* session, tr_scrape_request const& request, tr_scrape_response_func on_response)
|
||||
{
|
||||
using namespace scrape_helpers;
|
||||
|
||||
void announce_url_new(tr_urlbuf& url, tr_session const* session, tr_announce_request const& req)
|
||||
{
|
||||
url.clear();
|
||||
auto out = std::back_inserter(url);
|
||||
auto* d = new scrape_data{ std::move(on_response), request.log_name };
|
||||
|
||||
auto escaped_info_hash = tr_urlbuf{};
|
||||
tr_urlPercentEncode(std::back_inserter(escaped_info_hash), req.info_hash);
|
||||
|
||||
fmt::format_to(
|
||||
out,
|
||||
"{url}"
|
||||
"{sep}info_hash={info_hash}"
|
||||
"&peer_id={peer_id}"
|
||||
"&port={port}"
|
||||
"&uploaded={uploaded}"
|
||||
"&downloaded={downloaded}"
|
||||
"&left={left}"
|
||||
"&numwant={numwant}"
|
||||
"&key={key}"
|
||||
"&compact=1"
|
||||
"&supportcrypto=1",
|
||||
fmt::arg("url", req.announce_url),
|
||||
fmt::arg("sep", tr_strvContains(req.announce_url.sv(), '?') ? '&' : '?'),
|
||||
fmt::arg("info_hash", std::data(escaped_info_hash)),
|
||||
fmt::arg("peer_id", std::string_view{ std::data(req.peer_id), std::size(req.peer_id) }),
|
||||
fmt::arg("port", req.port.host()),
|
||||
fmt::arg("uploaded", req.up),
|
||||
fmt::arg("downloaded", req.down),
|
||||
fmt::arg("left", req.leftUntilComplete),
|
||||
fmt::arg("numwant", req.numwant),
|
||||
fmt::arg("key", req.key));
|
||||
|
||||
if (session->encryptionMode() == TR_ENCRYPTION_REQUIRED)
|
||||
auto& response = d->response();
|
||||
response.scrape_url = request.scrape_url;
|
||||
response.row_count = request.info_hash_count;
|
||||
for (int i = 0; i < response.row_count; ++i)
|
||||
{
|
||||
fmt::format_to(out, "&requirecrypto=1");
|
||||
response.rows[i].info_hash = request.info_hash[i];
|
||||
response.rows[i].seeders = -1;
|
||||
response.rows[i].leechers = -1;
|
||||
response.rows[i].downloads = -1;
|
||||
}
|
||||
|
||||
if (req.corrupt != 0)
|
||||
{
|
||||
fmt::format_to(out, "&corrupt={}", req.corrupt);
|
||||
}
|
||||
|
||||
if (auto const str = get_event_string(req); !std::empty(str))
|
||||
{
|
||||
fmt::format_to(out, "&event={}", str);
|
||||
}
|
||||
|
||||
if (!std::empty(req.tracker_id))
|
||||
{
|
||||
fmt::format_to(out, "&trackerid={}", req.tracker_id);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string format_ip_arg(std::string_view ip)
|
||||
{
|
||||
return fmt::format("&ip={:s}", ip);
|
||||
}
|
||||
|
||||
} // namespace tr_tracker_announce_helpers
|
||||
|
||||
void tr_tracker_http_announce(
|
||||
tr_session const* session,
|
||||
tr_announce_request const& request,
|
||||
tr_announce_response_func on_response)
|
||||
{
|
||||
using namespace tr_tracker_announce_helpers;
|
||||
|
||||
auto* const d = new http_announce_data{ request.info_hash, std::move(on_response), request.log_name };
|
||||
|
||||
/* There are two alternative techniques for announcing both IPv4 and
|
||||
IPv6 addresses. Previous version of BEP-7 suggests adding "ipv4="
|
||||
and "ipv6=" parameters to the announce URL, while OpenTracker and
|
||||
newer version of BEP-7 requires that peers announce once per each
|
||||
public address they want to use.
|
||||
|
||||
We should ensure that we send the announce both via IPv6 and IPv4,
|
||||
and to be safe we also add the "ipv6=" and "ipv4=" parameters, if
|
||||
we already have them. Our global IPv6 address is computed for the
|
||||
LTEP handshake, so this comes for free. Our public IPv4 address
|
||||
may have been returned from a previous announce and stored in the
|
||||
session.
|
||||
*/
|
||||
auto url = tr_urlbuf{};
|
||||
announce_url_new(url, session, request);
|
||||
auto options = tr_web::FetchOptions{ url.sv(), onAnnounceDone, d };
|
||||
options.timeout_secs = TR_ANNOUNCE_TIMEOUT_SEC;
|
||||
auto scrape_url = tr_pathbuf{};
|
||||
scrape_url_new(scrape_url, request);
|
||||
tr_logAddTrace(fmt::format("Sending scrape to libcurl: '{}'", scrape_url), request.log_name);
|
||||
auto options = tr_web::FetchOptions{ scrape_url, onScrapeDone, d };
|
||||
options.timeout_secs = TR_SCRAPE_TIMEOUT_SEC;
|
||||
options.sndbuf = 4096;
|
||||
options.rcvbuf = 4096;
|
||||
|
||||
auto do_make_request = [&](std::string_view const& protocol_name, tr_web::FetchOptions&& opt)
|
||||
{
|
||||
tr_logAddTrace(fmt::format("Sending {} announce to libcurl: '{}'", protocol_name, opt.url), request.log_name);
|
||||
session->fetch(std::move(opt));
|
||||
};
|
||||
|
||||
auto const [ipv6, ipv6_is_any] = session->publicAddress(TR_AF_INET6);
|
||||
|
||||
/*
|
||||
* Before Curl 7.77.0, if we explicitly choose the IP version we want
|
||||
* to use, it is still possible that the wrong one is used. The workaround
|
||||
* is expensive (disabling DNS cache), so instead we have to make do with
|
||||
* a request that we don't know if will go through IPv6 or IPv4.
|
||||
*/
|
||||
static bool const use_curl_workaround = curl_version_info(CURLVERSION_NOW)->version_num < CURL_VERSION_BITS(7, 77, 0);
|
||||
if (use_curl_workaround)
|
||||
{
|
||||
if (session->useAnnounceIP())
|
||||
{
|
||||
options.url += format_ip_arg(session->announceIP());
|
||||
}
|
||||
|
||||
d->requests_sent_count = 1;
|
||||
do_make_request(""sv, std::move(options));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (session->useAnnounceIP() || ipv6_is_any)
|
||||
{
|
||||
if (session->useAnnounceIP())
|
||||
{
|
||||
options.url += format_ip_arg(session->announceIP());
|
||||
}
|
||||
d->requests_sent_count = 1;
|
||||
do_make_request(""sv, std::move(options));
|
||||
}
|
||||
else
|
||||
{
|
||||
d->requests_sent_count = 2;
|
||||
|
||||
// First try to send the announce via IPv4:
|
||||
auto ipv4_options = options;
|
||||
ipv4_options.ip_proto = tr_web::FetchOptions::IPProtocol::V4;
|
||||
do_make_request("IPv4"sv, std::move(ipv4_options));
|
||||
|
||||
// Then try to send via IPv6:
|
||||
options.ip_proto = tr_web::FetchOptions::IPProtocol::V6;
|
||||
do_make_request("IPv6"sv, std::move(options));
|
||||
}
|
||||
}
|
||||
session->fetch(std::move(options));
|
||||
}
|
||||
|
||||
/****
|
||||
*****
|
||||
***** SCRAPE
|
||||
*****
|
||||
****/
|
||||
|
||||
void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::string_view benc, std::string_view log_name)
|
||||
{
|
||||
verboseLog("Scrape response:", TR_DOWN, benc);
|
||||
|
@ -576,100 +682,3 @@ void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::stri
|
|||
tr_error_clear(&error);
|
||||
}
|
||||
}
|
||||
|
||||
class scrape_data
|
||||
{
|
||||
public:
|
||||
scrape_data(tr_scrape_response_func response_func, std::string_view log_name)
|
||||
: response_func_{ std::move(response_func) }
|
||||
, log_name_{ log_name }
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto& response() noexcept
|
||||
{
|
||||
return response_;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto const& log_name() const noexcept
|
||||
{
|
||||
return log_name_;
|
||||
}
|
||||
|
||||
void invoke_callback() const
|
||||
{
|
||||
if (response_func_)
|
||||
{
|
||||
response_func_(response_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
tr_scrape_response response_ = {};
|
||||
tr_scrape_response_func response_func_ = {};
|
||||
std::string log_name_;
|
||||
};
|
||||
|
||||
static void onScrapeDone(tr_web::FetchResponse const& web_response)
|
||||
{
|
||||
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
||||
auto* const data = static_cast<scrape_data*>(vdata);
|
||||
|
||||
auto& response = data->response();
|
||||
response.did_connect = did_connect;
|
||||
response.did_timeout = did_timeout;
|
||||
|
||||
auto const scrape_url_sv = response.scrape_url.sv();
|
||||
tr_logAddTrace(fmt::format("Got scrape response for '{}'", scrape_url_sv), data->log_name());
|
||||
|
||||
if (status != HTTP_OK)
|
||||
{
|
||||
auto const* const response_str = tr_webGetResponseStr(status);
|
||||
response.errmsg = fmt::format(FMT_STRING("Tracker HTTP response {:d} ({:s})"), status, response_str);
|
||||
}
|
||||
else if (!std::empty(body))
|
||||
{
|
||||
tr_announcerParseHttpScrapeResponse(response, body, data->log_name());
|
||||
}
|
||||
|
||||
data->invoke_callback();
|
||||
delete data;
|
||||
}
|
||||
|
||||
static void scrape_url_new(tr_pathbuf& scrape_url, tr_scrape_request const& req)
|
||||
{
|
||||
scrape_url = req.scrape_url.sv();
|
||||
char delimiter = tr_strvContains(scrape_url, '?') ? '&' : '?';
|
||||
|
||||
for (int i = 0; i < req.info_hash_count; ++i)
|
||||
{
|
||||
scrape_url.append(delimiter, "info_hash=");
|
||||
tr_urlPercentEncode(std::back_inserter(scrape_url), req.info_hash[i]);
|
||||
delimiter = '&';
|
||||
}
|
||||
}
|
||||
|
||||
void tr_tracker_http_scrape(tr_session const* session, tr_scrape_request const& request, tr_scrape_response_func on_response)
|
||||
{
|
||||
auto* d = new scrape_data{ std::move(on_response), request.log_name };
|
||||
|
||||
auto& response = d->response();
|
||||
response.scrape_url = request.scrape_url;
|
||||
response.row_count = request.info_hash_count;
|
||||
for (int i = 0; i < response.row_count; ++i)
|
||||
{
|
||||
response.rows[i].info_hash = request.info_hash[i];
|
||||
response.rows[i].seeders = -1;
|
||||
response.rows[i].leechers = -1;
|
||||
response.rows[i].downloads = -1;
|
||||
}
|
||||
|
||||
auto scrape_url = tr_pathbuf{};
|
||||
scrape_url_new(scrape_url, request);
|
||||
tr_logAddTrace(fmt::format("Sending scrape to libcurl: '{}'", scrape_url), request.log_name);
|
||||
auto options = tr_web::FetchOptions{ scrape_url, onScrapeDone, d };
|
||||
options.timeout_secs = TR_SCRAPE_TIMEOUT_SEC;
|
||||
options.sndbuf = 4096;
|
||||
options.rcvbuf = 4096;
|
||||
session->fetch(std::move(options));
|
||||
}
|
||||
|
|
|
@ -43,23 +43,20 @@ using namespace std::literals;
|
|||
#define tr_logAddDebugTier(tier, msg) tr_logAddDebug(msg, (tier)->buildLogName())
|
||||
#define tr_logAddTraceTier(tier, msg) tr_logAddTrace(msg, (tier)->buildLogName())
|
||||
|
||||
/* unless the tracker says otherwise, rescrape this frequently */
|
||||
static auto constexpr DefaultScrapeIntervalSec = int{ 60 * 30 };
|
||||
|
||||
/* the value of the 'numwant' argument passed in tracker requests. */
|
||||
static auto constexpr Numwant = int{ 80 };
|
||||
|
||||
/* how often to announce & scrape */
|
||||
static auto constexpr MaxAnnouncesPerUpkeep = int{ 20 };
|
||||
static auto constexpr MaxScrapesPerUpkeep = int{ 20 };
|
||||
|
||||
/* how many infohashes to remove when we get a scrape-too-long error */
|
||||
static auto constexpr TrMultiscrapeStep = int{ 5 };
|
||||
|
||||
// ---
|
||||
|
||||
namespace
|
||||
{
|
||||
/* unless the tracker says otherwise, rescrape this frequently */
|
||||
auto constexpr DefaultScrapeIntervalSec = int{ 60 * 30 };
|
||||
|
||||
/* the value of the 'numwant' argument passed in tracker requests. */
|
||||
auto constexpr Numwant = int{ 80 };
|
||||
|
||||
/* how often to announce & scrape */
|
||||
auto constexpr MaxAnnouncesPerUpkeep = int{ 20 };
|
||||
auto constexpr MaxScrapesPerUpkeep = int{ 20 };
|
||||
|
||||
/* how many infohashes to remove when we get a scrape-too-long error */
|
||||
auto constexpr TrMultiscrapeStep = int{ 5 };
|
||||
|
||||
struct StopsCompare
|
||||
{
|
||||
|
@ -643,22 +640,6 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
static tr_tier* getTier(tr_announcer_impl* announcer, tr_sha1_digest_t const& info_hash, int tier_id)
|
||||
{
|
||||
if (announcer == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* const tor = announcer->session->torrents().get(info_hash);
|
||||
if (tor == nullptr || tor->torrent_announcer == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return tor->torrent_announcer->getTier(tier_id);
|
||||
}
|
||||
|
||||
// --- PUBLISH
|
||||
|
||||
namespace
|
||||
|
@ -773,7 +754,11 @@ time_t tr_announcerNextManualAnnounce(tr_torrent const* tor)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void tr_logAddTrace_tier_announce_queue(tr_tier const* tier)
|
||||
namespace
|
||||
{
|
||||
namespace announce_helpers
|
||||
{
|
||||
void tr_logAddTrace_tier_announce_queue(tr_tier const* tier)
|
||||
{
|
||||
if (!tr_logLevelIsActive(TR_LOG_TRACE) || std::empty(tier->announce_events))
|
||||
{
|
||||
|
@ -792,7 +777,7 @@ static void tr_logAddTrace_tier_announce_queue(tr_tier const* tier)
|
|||
}
|
||||
|
||||
// higher priorities go to the front of the announce queue
|
||||
static void tier_update_announce_priority(tr_tier* tier)
|
||||
void tier_update_announce_priority(tr_tier* tier)
|
||||
{
|
||||
int priority = -1;
|
||||
|
||||
|
@ -804,7 +789,7 @@ static void tier_update_announce_priority(tr_tier* tier)
|
|||
tier->announce_event_priority = priority;
|
||||
}
|
||||
|
||||
static void tier_announce_remove_trailing(tr_tier* tier, tr_announce_event e)
|
||||
void tier_announce_remove_trailing(tr_tier* tier, tr_announce_event e)
|
||||
{
|
||||
while (!std::empty(tier->announce_events) && tier->announce_events.back() == e)
|
||||
{
|
||||
|
@ -814,7 +799,7 @@ static void tier_announce_remove_trailing(tr_tier* tier, tr_announce_event e)
|
|||
tier_update_announce_priority(tier);
|
||||
}
|
||||
|
||||
static void tier_announce_event_push(tr_tier* tier, tr_announce_event e, time_t announce_at)
|
||||
void tier_announce_event_push(tr_tier* tier, tr_announce_event e, time_t announce_at)
|
||||
{
|
||||
TR_ASSERT(tier != nullptr);
|
||||
|
||||
|
@ -852,7 +837,7 @@ static void tier_announce_event_push(tr_tier* tier, tr_announce_event e, time_t
|
|||
tr_logAddTraceTier(tier, fmt::format("announcing in {} seconds", difftime(announce_at, tr_time())));
|
||||
}
|
||||
|
||||
static auto tier_announce_event_pull(tr_tier* tier)
|
||||
auto tier_announce_event_pull(tr_tier* tier)
|
||||
{
|
||||
auto const e = tier->announce_events.front();
|
||||
tier->announce_events.pop_front();
|
||||
|
@ -860,104 +845,7 @@ static auto tier_announce_event_pull(tr_tier* tier)
|
|||
return e;
|
||||
}
|
||||
|
||||
static void torrentAddAnnounce(tr_torrent* tor, tr_announce_event e, time_t announce_at)
|
||||
{
|
||||
// tell each tier to announce
|
||||
for (auto& tier : tor->torrent_announcer->tiers)
|
||||
{
|
||||
tier_announce_event_push(&tier, e, announce_at);
|
||||
}
|
||||
}
|
||||
|
||||
void tr_announcer_impl::startTorrent(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
|
||||
}
|
||||
|
||||
void tr_announcerManualAnnounce(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_NONE, tr_time());
|
||||
}
|
||||
|
||||
void tr_announcer_impl::stopTorrent(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time());
|
||||
}
|
||||
|
||||
void tr_announcerTorrentCompleted(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time());
|
||||
}
|
||||
|
||||
void tr_announcerChangeMyPort(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
void tr_announcerAddBytes(tr_torrent* tor, int type, uint32_t n_bytes)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(type == TR_ANN_UP || type == TR_ANN_DOWN || type == TR_ANN_CORRUPT);
|
||||
|
||||
for (auto& tier : tor->torrent_announcer->tiers)
|
||||
{
|
||||
tier.byteCounts[type] += n_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
[[nodiscard]] static tr_announce_request create_announce_request(
|
||||
tr_announcer_impl const* const announcer,
|
||||
tr_torrent* const tor,
|
||||
tr_tier const* const tier,
|
||||
tr_announce_event const event)
|
||||
{
|
||||
auto const* const current_tracker = tier->currentTracker();
|
||||
TR_ASSERT(current_tracker != nullptr);
|
||||
|
||||
auto req = tr_announce_request{};
|
||||
req.port = announcer->session->advertisedPeerPort();
|
||||
req.announce_url = current_tracker->announce_url;
|
||||
req.tracker_id = current_tracker->tracker_id;
|
||||
req.info_hash = tor->infoHash();
|
||||
req.peer_id = tr_torrentGetPeerId(tor);
|
||||
req.up = tier->byteCounts[TR_ANN_UP];
|
||||
req.down = tier->byteCounts[TR_ANN_DOWN];
|
||||
req.corrupt = tier->byteCounts[TR_ANN_CORRUPT];
|
||||
req.leftUntilComplete = tor->hasMetainfo() ? tor->totalSize() - tor->hasTotal() : INT64_MAX;
|
||||
req.event = event;
|
||||
req.numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : Numwant;
|
||||
req.key = tor->announce_key();
|
||||
req.partial_seed = tor->isPartialSeed();
|
||||
tier->buildLogName(req.log_name, sizeof(req.log_name));
|
||||
return req;
|
||||
}
|
||||
|
||||
void tr_announcer_impl::removeTorrent(tr_torrent* tor)
|
||||
{
|
||||
// FIXME(ckerr)
|
||||
auto* const ta = tor->torrent_announcer;
|
||||
if (ta == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const& tier : ta->tiers)
|
||||
{
|
||||
if (tier.isRunning && tier.lastAnnounceSucceeded)
|
||||
{
|
||||
stops_.emplace(create_announce_request(this, tor, &tier, TR_ANNOUNCE_EVENT_STOPPED));
|
||||
}
|
||||
}
|
||||
|
||||
tor->torrent_announcer = nullptr;
|
||||
delete ta;
|
||||
}
|
||||
|
||||
static bool isUnregistered(char const* errmsg)
|
||||
bool isUnregistered(char const* errmsg)
|
||||
{
|
||||
auto const lower = tr_strlower(errmsg != nullptr ? errmsg : "");
|
||||
|
||||
|
@ -966,8 +854,10 @@ static bool isUnregistered(char const* errmsg)
|
|||
return std::any_of(std::begin(Keys), std::end(Keys), [&lower](auto const& key) { return tr_strvContains(lower, key); });
|
||||
}
|
||||
|
||||
static void on_announce_error(tr_tier* tier, char const* err, tr_announce_event e)
|
||||
void on_announce_error(tr_tier* tier, char const* err, tr_announce_event e)
|
||||
{
|
||||
using namespace announce_helpers;
|
||||
|
||||
/* increment the error count */
|
||||
auto* current_tracker = tier->currentTracker();
|
||||
if (current_tracker != nullptr)
|
||||
|
@ -1002,12 +892,69 @@ static void on_announce_error(tr_tier* tier, char const* err, tr_announce_event
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_announce_request create_announce_request(
|
||||
tr_announcer_impl const* const announcer,
|
||||
tr_torrent* const tor,
|
||||
tr_tier const* const tier,
|
||||
tr_announce_event const event)
|
||||
{
|
||||
auto const* const current_tracker = tier->currentTracker();
|
||||
TR_ASSERT(current_tracker != nullptr);
|
||||
|
||||
auto req = tr_announce_request{};
|
||||
req.port = announcer->session->advertisedPeerPort();
|
||||
req.announce_url = current_tracker->announce_url;
|
||||
req.tracker_id = current_tracker->tracker_id;
|
||||
req.info_hash = tor->infoHash();
|
||||
req.peer_id = tr_torrentGetPeerId(tor);
|
||||
req.up = tier->byteCounts[TR_ANN_UP];
|
||||
req.down = tier->byteCounts[TR_ANN_DOWN];
|
||||
req.corrupt = tier->byteCounts[TR_ANN_CORRUPT];
|
||||
req.leftUntilComplete = tor->hasMetainfo() ? tor->totalSize() - tor->hasTotal() : INT64_MAX;
|
||||
req.event = event;
|
||||
req.numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : Numwant;
|
||||
req.key = tor->announce_key();
|
||||
req.partial_seed = tor->isPartialSeed();
|
||||
tier->buildLogName(req.log_name, sizeof(req.log_name));
|
||||
return req;
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_tier* getTier(tr_announcer_impl* announcer, tr_sha1_digest_t const& info_hash, int tier_id)
|
||||
{
|
||||
if (announcer == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* const tor = announcer->session->torrents().get(info_hash);
|
||||
if (tor == nullptr || tor->torrent_announcer == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return tor->torrent_announcer->getTier(tier_id);
|
||||
}
|
||||
} // namespace announce_helpers
|
||||
|
||||
void torrentAddAnnounce(tr_torrent* tor, tr_announce_event e, time_t announce_at)
|
||||
{
|
||||
using namespace announce_helpers;
|
||||
|
||||
// tell each tier to announce
|
||||
for (auto& tier : tor->torrent_announcer->tiers)
|
||||
{
|
||||
tier_announce_event_push(&tier, e, announce_at);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void tr_announcer_impl::onAnnounceDone(
|
||||
int tier_id,
|
||||
tr_announce_event event,
|
||||
bool is_running_on_success,
|
||||
tr_announce_response const& response)
|
||||
{
|
||||
using namespace announce_helpers;
|
||||
using namespace publish_helpers;
|
||||
|
||||
auto* const tier = getTier(this, response.info_hash, tier_id);
|
||||
|
@ -1193,54 +1140,92 @@ void tr_announcer_impl::onAnnounceDone(
|
|||
}
|
||||
}
|
||||
|
||||
static void tierAnnounce(tr_announcer_impl* announcer, tr_tier* tier)
|
||||
void tr_announcer_impl::startTorrent(tr_torrent* tor)
|
||||
{
|
||||
TR_ASSERT(!tier->isAnnouncing);
|
||||
TR_ASSERT(!std::empty(tier->announce_events));
|
||||
TR_ASSERT(tier->currentTracker() != nullptr);
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
|
||||
}
|
||||
|
||||
auto const now = tr_time();
|
||||
void tr_announcerManualAnnounce(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_NONE, tr_time());
|
||||
}
|
||||
|
||||
tr_torrent* tor = tier->tor;
|
||||
auto const event = tier_announce_event_pull(tier);
|
||||
auto const req = create_announce_request(announcer, tor, tier, event);
|
||||
void tr_announcer_impl::stopTorrent(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time());
|
||||
}
|
||||
|
||||
tier->isAnnouncing = true;
|
||||
tier->lastAnnounceStartTime = now;
|
||||
void tr_announcerTorrentCompleted(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time());
|
||||
}
|
||||
|
||||
auto tier_id = tier->id;
|
||||
auto is_running_on_success = tor->isRunning;
|
||||
void tr_announcerChangeMyPort(tr_torrent* tor)
|
||||
{
|
||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
|
||||
}
|
||||
|
||||
announcer->announce(
|
||||
req,
|
||||
[session = announcer->session, announcer, tier_id, event, is_running_on_success](tr_announce_response const& response)
|
||||
// ---
|
||||
|
||||
void tr_announcerAddBytes(tr_torrent* tor, int type, uint32_t n_bytes)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(type == TR_ANN_UP || type == TR_ANN_DOWN || type == TR_ANN_CORRUPT);
|
||||
|
||||
for (auto& tier : tor->torrent_announcer->tiers)
|
||||
{
|
||||
tier.byteCounts[type] += n_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
void tr_announcer_impl::removeTorrent(tr_torrent* tor)
|
||||
{
|
||||
using namespace announce_helpers;
|
||||
|
||||
// FIXME(ckerr)
|
||||
auto* const ta = tor->torrent_announcer;
|
||||
if (ta == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const& tier : ta->tiers)
|
||||
{
|
||||
if (tier.isRunning && tier.lastAnnounceSucceeded)
|
||||
{
|
||||
if (session->announcer_)
|
||||
{
|
||||
announcer->onAnnounceDone(tier_id, event, is_running_on_success, response);
|
||||
}
|
||||
});
|
||||
stops_.emplace(create_announce_request(this, tor, &tier, TR_ANNOUNCE_EVENT_STOPPED));
|
||||
}
|
||||
}
|
||||
|
||||
tor->torrent_announcer = nullptr;
|
||||
delete ta;
|
||||
}
|
||||
|
||||
// --- SCRAPE
|
||||
|
||||
static bool multiscrape_too_big(std::string_view errmsg)
|
||||
namespace
|
||||
{
|
||||
namespace on_scrape_done_helpers
|
||||
{
|
||||
[[nodiscard]] TR_CONSTEXPR20 bool multiscrape_too_big(std::string_view errmsg)
|
||||
{
|
||||
/* Found a tracker that returns some bespoke string for this case?
|
||||
Add your patch here and open a PR */
|
||||
auto constexpr TooLongErrors = std::array<std::string_view, 3>{
|
||||
auto too_long_errors = std::array<std::string_view, 3>{
|
||||
"Bad Request",
|
||||
"GET string too long",
|
||||
"Request-URI Too Long",
|
||||
};
|
||||
|
||||
return std::any_of(
|
||||
std::begin(TooLongErrors),
|
||||
std::end(TooLongErrors),
|
||||
std::begin(too_long_errors),
|
||||
std::end(too_long_errors),
|
||||
[&errmsg](auto const& substr) { return tr_strvContains(errmsg, substr); });
|
||||
}
|
||||
|
||||
static void on_scrape_error(tr_session const* /*session*/, tr_tier* tier, char const* errmsg)
|
||||
void on_scrape_error(tr_session const* /*session*/, tr_tier* tier, char const* errmsg)
|
||||
{
|
||||
// increment the error count
|
||||
auto* current_tracker = tier->currentTracker();
|
||||
|
@ -1265,7 +1250,7 @@ static void on_scrape_error(tr_session const* /*session*/, tr_tier* tier, char c
|
|||
tier->scheduleNextScrape(interval);
|
||||
}
|
||||
|
||||
static void checkMultiscrapeMax(tr_announcer_impl* announcer, tr_scrape_response const& response)
|
||||
void checkMultiscrapeMax(tr_announcer_impl* announcer, tr_scrape_response const& response)
|
||||
{
|
||||
if (!multiscrape_too_big(response.errmsg))
|
||||
{
|
||||
|
@ -1303,9 +1288,12 @@ static void checkMultiscrapeMax(tr_announcer_impl* announcer, tr_scrape_response
|
|||
multiscrape_max = n;
|
||||
}
|
||||
}
|
||||
} // namespace on_scrape_done_helpers
|
||||
} // namespace
|
||||
|
||||
void tr_announcer_impl::onScrapeDone(tr_scrape_response const& response)
|
||||
{
|
||||
using namespace on_scrape_done_helpers;
|
||||
using namespace publish_helpers;
|
||||
|
||||
auto const now = tr_time();
|
||||
|
@ -1404,7 +1392,9 @@ void tr_announcer_impl::onScrapeDone(tr_scrape_response const& response)
|
|||
checkMultiscrapeMax(this, response);
|
||||
}
|
||||
|
||||
static void multiscrape(tr_announcer_impl* announcer, std::vector<tr_tier*> const& tiers)
|
||||
namespace
|
||||
{
|
||||
void multiscrape(tr_announcer_impl* announcer, std::vector<tr_tier*> const& tiers)
|
||||
{
|
||||
auto const now = tr_time();
|
||||
auto requests = std::array<tr_scrape_request, MaxScrapesPerUpkeep>{};
|
||||
|
@ -1471,7 +1461,9 @@ static void multiscrape(tr_announcer_impl* announcer, std::vector<tr_tier*> cons
|
|||
}
|
||||
}
|
||||
|
||||
static int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
|
||||
namespace upkeep_helpers
|
||||
{
|
||||
int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
|
||||
{
|
||||
/* prefer higher-priority events */
|
||||
if (auto const priority_a = a->announce_event_priority, priority_b = b->announce_event_priority; priority_a != priority_b)
|
||||
|
@ -1510,9 +1502,40 @@ static int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
|
|||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
||||
static void scrapeAndAnnounceMore(tr_announcer_impl* announcer)
|
||||
void tierAnnounce(tr_announcer_impl* announcer, tr_tier* tier)
|
||||
{
|
||||
time_t const now = tr_time();
|
||||
using namespace announce_helpers;
|
||||
|
||||
TR_ASSERT(!tier->isAnnouncing);
|
||||
TR_ASSERT(!std::empty(tier->announce_events));
|
||||
TR_ASSERT(tier->currentTracker() != nullptr);
|
||||
|
||||
auto const now = tr_time();
|
||||
|
||||
tr_torrent* tor = tier->tor;
|
||||
auto const event = tier_announce_event_pull(tier);
|
||||
auto const req = create_announce_request(announcer, tor, tier, event);
|
||||
|
||||
tier->isAnnouncing = true;
|
||||
tier->lastAnnounceStartTime = now;
|
||||
|
||||
auto tier_id = tier->id;
|
||||
auto is_running_on_success = tor->isRunning;
|
||||
|
||||
announcer->announce(
|
||||
req,
|
||||
[session = announcer->session, announcer, tier_id, event, is_running_on_success](tr_announce_response const& response)
|
||||
{
|
||||
if (session->announcer_)
|
||||
{
|
||||
announcer->onAnnounceDone(tier_id, event, is_running_on_success, response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void scrapeAndAnnounceMore(tr_announcer_impl* announcer)
|
||||
{
|
||||
auto const now = tr_time();
|
||||
|
||||
/* build a list of tiers that need to be announced */
|
||||
auto announce_me = std::vector<tr_tier*>{};
|
||||
|
@ -1557,9 +1580,13 @@ static void scrapeAndAnnounceMore(tr_announcer_impl* announcer)
|
|||
tierAnnounce(announcer, tier);
|
||||
}
|
||||
}
|
||||
} // namespace upkeep_helpers
|
||||
} // namespace
|
||||
|
||||
void tr_announcer_impl::upkeep()
|
||||
{
|
||||
using namespace upkeep_helpers;
|
||||
|
||||
auto const lock = session->unique_lock();
|
||||
|
||||
// maybe send out some "stopped" messages for closed torrents
|
||||
|
@ -1576,7 +1603,11 @@ void tr_announcer_impl::upkeep()
|
|||
|
||||
// ---
|
||||
|
||||
static tr_tracker_view trackerView(tr_torrent const& tor, size_t tier_index, tr_tier const& tier, tr_tracker const& tracker)
|
||||
namespace
|
||||
{
|
||||
namespace tracker_view_helpers
|
||||
{
|
||||
[[nodiscard]] auto trackerView(tr_torrent const& tor, size_t tier_index, tr_tier const& tier, tr_tracker const& tracker)
|
||||
{
|
||||
auto const now = tr_time();
|
||||
auto view = tr_tracker_view{};
|
||||
|
@ -1666,6 +1697,8 @@ static tr_tracker_view trackerView(tr_torrent const& tor, size_t tier_index, tr_
|
|||
|
||||
return view;
|
||||
}
|
||||
} // namespace tracker_view_helpers
|
||||
} // namespace
|
||||
|
||||
size_t tr_announcerTrackerCount(tr_torrent const* tor)
|
||||
{
|
||||
|
@ -1682,6 +1715,8 @@ size_t tr_announcerTrackerCount(tr_torrent const* tor)
|
|||
|
||||
tr_tracker_view tr_announcerTracker(tr_torrent const* tor, size_t nth)
|
||||
{
|
||||
using namespace tracker_view_helpers;
|
||||
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(tor->torrent_announcer != nullptr);
|
||||
|
||||
|
@ -1709,6 +1744,8 @@ tr_tracker_view tr_announcerTracker(tr_torrent const* tor, size_t nth)
|
|||
// so announcer needs to update the tr_tier / tr_trackers to match
|
||||
void tr_announcer_impl::resetTorrent(tr_torrent* tor)
|
||||
{
|
||||
using namespace announce_helpers;
|
||||
|
||||
// make a new tr_announcer_tier
|
||||
auto* const older = tor->torrent_announcer;
|
||||
tor->torrent_announcer = new tr_torrent_announcer{ this, tor };
|
||||
|
|
|
@ -27,11 +27,9 @@
|
|||
#include "tr-assert.h"
|
||||
#include "utils.h"
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static void log_openssl_error(char const* file, int line)
|
||||
namespace
|
||||
{
|
||||
void log_openssl_error(char const* file, int line)
|
||||
{
|
||||
unsigned long const error_code = ERR_get_error();
|
||||
|
||||
|
@ -64,7 +62,7 @@ static void log_openssl_error(char const* file, int line)
|
|||
|
||||
#define log_error() log_openssl_error(__FILE__, __LINE__)
|
||||
|
||||
static bool check_openssl_result(int result, int expected_result, bool expected_equal, char const* file, int line)
|
||||
bool check_openssl_result(int result, int expected_result, bool expected_equal, char const* file, int line)
|
||||
{
|
||||
bool const ret = (result == expected_result) == expected_equal;
|
||||
|
||||
|
@ -77,13 +75,8 @@ static bool check_openssl_result(int result, int expected_result, bool expected_
|
|||
}
|
||||
|
||||
#define check_result(result) check_openssl_result((result), 1, true, __FILE__, __LINE__)
|
||||
#define check_result_neq(result, x_result) check_openssl_result((result), (x_result), false, __FILE__, __LINE__)
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
namespace
|
||||
namespace sha_helpers
|
||||
{
|
||||
|
||||
class ShaHelper
|
||||
|
@ -196,55 +189,26 @@ private:
|
|||
ShaHelper helper_{ EVP_sha256 };
|
||||
};
|
||||
|
||||
} // namespace sha_helpers
|
||||
} // namespace
|
||||
|
||||
// --- sha
|
||||
|
||||
std::unique_ptr<tr_sha1> tr_sha1::create()
|
||||
{
|
||||
using namespace sha_helpers;
|
||||
|
||||
return std::make_unique<Sha1Impl>();
|
||||
}
|
||||
|
||||
std::unique_ptr<tr_sha256> tr_sha256::create()
|
||||
{
|
||||
using namespace sha_helpers;
|
||||
|
||||
return std::make_unique<Sha256Impl>();
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x0090802fL
|
||||
|
||||
static EVP_CIPHER_CTX* openssl_evp_cipher_context_new()
|
||||
{
|
||||
auto* const handle = new EVP_CIPHER_CTX{};
|
||||
|
||||
if (handle != nullptr)
|
||||
{
|
||||
EVP_CIPHER_CTX_init(handle);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void openssl_evp_cipher_context_free(EVP_CIPHER_CTX* handle)
|
||||
{
|
||||
if (handle == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_cleanup(handle);
|
||||
delete handle;
|
||||
}
|
||||
|
||||
#define EVP_CIPHER_CTX_new() openssl_evp_cipher_context_new()
|
||||
#define EVP_CIPHER_CTX_free(x) openssl_evp_cipher_context_free((x))
|
||||
|
||||
#endif
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// --- x509
|
||||
|
||||
tr_x509_store_t tr_ssl_get_x509_store(tr_ssl_ctx_t handle)
|
||||
{
|
||||
|
@ -288,9 +252,7 @@ void tr_x509_cert_free(tr_x509_cert_t handle)
|
|||
X509_free(static_cast<X509*>(handle));
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// --- rand
|
||||
|
||||
bool tr_rand_buffer_crypto(void* buffer, size_t length)
|
||||
{
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
#include "tr-macros.h"
|
||||
#include "utils.h"
|
||||
|
||||
static char* tr_strvDup(std::string_view in)
|
||||
namespace
|
||||
{
|
||||
[[nodiscard]] char* tr_strvdup(std::string_view in)
|
||||
{
|
||||
auto const n = std::size(in);
|
||||
auto* const ret = new char[n + 1];
|
||||
|
@ -23,6 +25,7 @@ static char* tr_strvDup(std::string_view in)
|
|||
ret[n] = '\0';
|
||||
return ret;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void tr_error_free(tr_error* error)
|
||||
{
|
||||
|
@ -43,7 +46,7 @@ void tr_error_set(tr_error** error, int code, std::string_view message)
|
|||
}
|
||||
|
||||
TR_ASSERT(*error == nullptr);
|
||||
*error = new tr_error{ code, tr_strvDup(message) };
|
||||
*error = new tr_error{ code, tr_strvdup(message) };
|
||||
}
|
||||
|
||||
void tr_error_propagate(tr_error** new_error, tr_error** old_error)
|
||||
|
@ -86,7 +89,7 @@ void tr_error_prefix(tr_error** error, char const* prefix)
|
|||
}
|
||||
|
||||
auto* err = *error;
|
||||
auto* const new_message = tr_strvDup(fmt::format(FMT_STRING("{:s}{:s}"), prefix, err->message));
|
||||
auto* const new_message = tr_strvdup(fmt::format(FMT_STRING("{:s}{:s}"), prefix, err->message));
|
||||
delete[] err->message;
|
||||
err->message = new_message;
|
||||
}
|
||||
|
|
1
libtransmission/makelog
Normal file
1
libtransmission/makelog
Normal file
|
@ -0,0 +1 @@
|
|||
ninja: error: loading 'build.ninja': No such file or directory
|
|
@ -467,6 +467,16 @@ public:
|
|||
pool_is_all_seeds_.reset();
|
||||
}
|
||||
|
||||
[[nodiscard]] peer_atom* get_existing_atom(tr_address const& addr)
|
||||
{
|
||||
auto const test = [&addr](auto const& atom)
|
||||
{
|
||||
return atom.addr == addr;
|
||||
};
|
||||
auto const it = std::find_if(std::begin(pool), std::end(pool), test);
|
||||
return it != std::end(pool) ? &*it : nullptr;
|
||||
}
|
||||
|
||||
Handshakes outgoing_handshakes;
|
||||
|
||||
uint16_t interested_count = 0;
|
||||
|
@ -559,6 +569,12 @@ struct tr_peerMgr
|
|||
void refillUpkeep() const;
|
||||
void makeNewPeerConnections(size_t max);
|
||||
|
||||
[[nodiscard]] tr_swarm* get_existing_swarm(tr_sha1_digest_t const& hash) const
|
||||
{
|
||||
auto* const tor = session->torrents().get(hash);
|
||||
return tor == nullptr ? nullptr : tor->swarm;
|
||||
}
|
||||
|
||||
tr_session* const session;
|
||||
Handshakes incoming_handshakes;
|
||||
|
||||
|
@ -623,24 +639,6 @@ tr_peer::~tr_peer()
|
|||
***
|
||||
**/
|
||||
|
||||
static tr_swarm* getExistingSwarm(tr_peerMgr* manager, tr_sha1_digest_t const& hash)
|
||||
{
|
||||
auto* const tor = manager->session->torrents().get(hash);
|
||||
|
||||
return tor == nullptr ? nullptr : tor->swarm;
|
||||
}
|
||||
|
||||
static struct peer_atom* getExistingAtom(tr_swarm const* cswarm, tr_address const& addr)
|
||||
{
|
||||
auto* swarm = const_cast<tr_swarm*>(cswarm);
|
||||
auto const test = [&addr](auto const& atom)
|
||||
{
|
||||
return atom.addr == addr;
|
||||
};
|
||||
auto const it = std::find_if(std::begin(swarm->pool), std::end(swarm->pool), test);
|
||||
return it != std::end(swarm->pool) ? &*it : nullptr;
|
||||
}
|
||||
|
||||
static bool peerIsInUse(tr_swarm const* swarm, struct peer_atom const* atom)
|
||||
{
|
||||
return atom->is_connected || swarm->outgoing_handshakes.count(atom->addr) != 0U ||
|
||||
|
@ -701,7 +699,7 @@ static void atomSetSeed(tr_swarm* swarm, peer_atom& atom)
|
|||
|
||||
static bool tr_peerMgrPeerIsSeed(tr_torrent const* tor, tr_address const& addr)
|
||||
{
|
||||
if (auto const* atom = getExistingAtom(tor->swarm, addr); atom != nullptr)
|
||||
if (auto const* atom = tor->swarm->get_existing_atom(addr); atom != nullptr)
|
||||
{
|
||||
return atom->isSeed();
|
||||
}
|
||||
|
@ -711,7 +709,7 @@ static bool tr_peerMgrPeerIsSeed(tr_torrent const* tor, tr_address const& addr)
|
|||
|
||||
void tr_peerMgrSetUtpSupported(tr_torrent* tor, tr_address const& addr)
|
||||
{
|
||||
if (auto* const atom = getExistingAtom(tor->swarm, addr); atom != nullptr)
|
||||
if (auto* const atom = tor->swarm->get_existing_atom(addr); atom != nullptr)
|
||||
{
|
||||
atom->flags |= ADDED_F_UTP_FLAGS;
|
||||
}
|
||||
|
@ -719,7 +717,7 @@ void tr_peerMgrSetUtpSupported(tr_torrent* tor, tr_address const& addr)
|
|||
|
||||
void tr_peerMgrSetUtpFailed(tr_torrent* tor, tr_address const& addr, bool failed)
|
||||
{
|
||||
if (auto* const atom = getExistingAtom(tor->swarm, addr); atom != nullptr)
|
||||
if (auto* const atom = tor->swarm->get_existing_atom(addr); atom != nullptr)
|
||||
{
|
||||
atom->utp_failed = failed;
|
||||
}
|
||||
|
@ -1046,7 +1044,7 @@ static struct peer_atom* ensureAtomExists(
|
|||
TR_ASSERT(addr.is_valid());
|
||||
TR_ASSERT(from < TR_PEER_FROM__MAX);
|
||||
|
||||
struct peer_atom* a = getExistingAtom(s, addr);
|
||||
struct peer_atom* a = s->get_existing_atom(addr);
|
||||
|
||||
if (a == nullptr)
|
||||
{
|
||||
|
@ -1097,7 +1095,7 @@ static bool on_handshake_done(tr_peerMgr* manager, tr_handshake::Result const& r
|
|||
bool const ok = result.is_connected;
|
||||
bool success = false;
|
||||
|
||||
tr_swarm* const s = getExistingSwarm(manager, result.io->torrent_hash());
|
||||
auto* const s = manager->get_existing_swarm(result.io->torrent_hash());
|
||||
|
||||
auto const [addr, port] = result.io->socket_address();
|
||||
|
||||
|
@ -1116,7 +1114,7 @@ static bool on_handshake_done(tr_peerMgr* manager, tr_handshake::Result const& r
|
|||
{
|
||||
if (s != nullptr)
|
||||
{
|
||||
struct peer_atom* atom = getExistingAtom(s, addr);
|
||||
struct peer_atom* atom = s->get_existing_atom(addr);
|
||||
|
||||
if (atom != nullptr)
|
||||
{
|
||||
|
|
|
@ -67,13 +67,11 @@
|
|||
#include "utils.h"
|
||||
#include "platform-quota.h"
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
namespace
|
||||
{
|
||||
#ifndef _WIN32
|
||||
|
||||
static char const* getdev(std::string_view path)
|
||||
[[nodiscard]] char const* getdev(std::string_view path)
|
||||
{
|
||||
#ifdef HAVE_GETMNTENT
|
||||
|
||||
|
@ -141,7 +139,7 @@ static char const* getdev(std::string_view path)
|
|||
#endif
|
||||
}
|
||||
|
||||
static char const* getfstype(std::string_view device)
|
||||
[[nodiscard]] char const* getfstype(std::string_view device)
|
||||
{
|
||||
#ifdef HAVE_GETMNTENT
|
||||
|
||||
|
@ -209,7 +207,7 @@ static char const* getfstype(std::string_view device)
|
|||
#endif
|
||||
}
|
||||
|
||||
static std::string getblkdev(std::string_view path)
|
||||
std::string getblkdev(std::string_view path)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
|
@ -235,7 +233,7 @@ extern "C"
|
|||
#include <quota.h>
|
||||
}
|
||||
|
||||
struct tr_disk_space getquota(char const* device)
|
||||
[[nodiscard]] tr_disk_space getquota(char const* device)
|
||||
{
|
||||
struct quotahandle* qh;
|
||||
struct quotakey qk;
|
||||
|
@ -285,7 +283,7 @@ struct tr_disk_space getquota(char const* device)
|
|||
|
||||
#else
|
||||
|
||||
static struct tr_disk_space getquota(char const* device)
|
||||
[[nodiscard]] tr_disk_space getquota(char const* device)
|
||||
{
|
||||
#if defined(__DragonFly__)
|
||||
struct ufs_dqblk dq = {};
|
||||
|
@ -371,7 +369,7 @@ static struct tr_disk_space getquota(char const* device)
|
|||
|
||||
#ifdef HAVE_XQM
|
||||
|
||||
static struct tr_disk_space getxfsquota(char const* device)
|
||||
[[nodiscard]] tr_disk_space getxfsquota(char const* device)
|
||||
{
|
||||
struct tr_disk_space disk_space = { -1, -1 };
|
||||
struct fs_disk_quota dq;
|
||||
|
@ -410,7 +408,7 @@ static struct tr_disk_space getxfsquota(char const* device)
|
|||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static tr_disk_space getQuotaSpace([[maybe_unused]] tr_device_info const& info)
|
||||
[[nodiscard]] tr_disk_space getQuotaSpace([[maybe_unused]] tr_device_info const& info)
|
||||
{
|
||||
struct tr_disk_space ret = { -1, -1 };
|
||||
|
||||
|
@ -432,7 +430,7 @@ static tr_disk_space getQuotaSpace([[maybe_unused]] tr_device_info const& info)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct tr_disk_space getDiskSpace(char const* path)
|
||||
[[nodiscard]] tr_disk_space getDiskSpace(char const* path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
|
@ -468,6 +466,8 @@ static struct tr_disk_space getDiskSpace(char const* path)
|
|||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
tr_device_info tr_device_info_create(std::string_view path)
|
||||
{
|
||||
auto out = tr_device_info{};
|
||||
|
|
|
@ -45,13 +45,10 @@
|
|||
|
||||
using namespace std::literals;
|
||||
|
||||
/***
|
||||
**** PATHS
|
||||
***/
|
||||
|
||||
namespace
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
static std::string win32_get_known_folder_ex(REFKNOWNFOLDERID folder_id, DWORD flags)
|
||||
std::string win32_get_known_folder_ex(REFKNOWNFOLDERID folder_id, DWORD flags)
|
||||
{
|
||||
if (PWSTR path; SHGetKnownFolderPath(folder_id, flags | KF_FLAG_DONT_UNEXPAND, nullptr, &path) == S_OK)
|
||||
{
|
||||
|
@ -63,14 +60,13 @@ static std::string win32_get_known_folder_ex(REFKNOWNFOLDERID folder_id, DWORD f
|
|||
return {};
|
||||
}
|
||||
|
||||
static auto win32_get_known_folder(REFKNOWNFOLDERID folder_id)
|
||||
auto win32_get_known_folder(REFKNOWNFOLDERID folder_id)
|
||||
{
|
||||
return win32_get_known_folder_ex(folder_id, KF_FLAG_DONT_VERIFY);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static std::string getHomeDir()
|
||||
std::string getHomeDir()
|
||||
{
|
||||
if (auto dir = tr_env_get_string("HOME"sv); !std::empty(dir))
|
||||
{
|
||||
|
@ -100,7 +96,7 @@ static std::string getHomeDir()
|
|||
return {};
|
||||
}
|
||||
|
||||
static std::string xdgConfigHome()
|
||||
std::string xdgConfigHome()
|
||||
{
|
||||
if (auto dir = tr_env_get_string("XDG_CONFIG_HOME"sv); !std::empty(dir))
|
||||
{
|
||||
|
@ -110,6 +106,51 @@ static std::string xdgConfigHome()
|
|||
return fmt::format("{:s}/.config"sv, getHomeDir());
|
||||
}
|
||||
|
||||
std::string getXdgEntryFromUserDirs(std::string_view key)
|
||||
{
|
||||
auto content = std::vector<char>{};
|
||||
if (auto const filename = fmt::format("{:s}/{:s}"sv, xdgConfigHome(), "user-dirs.dirs"sv);
|
||||
!tr_sys_path_exists(filename) || !tr_loadFile(filename, content) || std::empty(content))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// search for key="val" and extract val
|
||||
auto const search = fmt::format(FMT_STRING("{:s}=\""), key);
|
||||
auto begin = std::search(std::begin(content), std::end(content), std::begin(search), std::end(search));
|
||||
if (begin == std::end(content))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
std::advance(begin, std::size(search));
|
||||
auto const end = std::find(begin, std::end(content), '"');
|
||||
if (end == std::end(content))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
auto val = std::string{ begin, end };
|
||||
|
||||
// if val contains "$HOME", replace that with getHomeDir()
|
||||
auto constexpr Home = "$HOME"sv;
|
||||
if (auto const it = std::search(std::begin(val), std::end(val), std::begin(Home), std::end(Home)); it != std::end(val))
|
||||
{
|
||||
val.replace(it, it + std::size(Home), getHomeDir());
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isWebClientDir(std::string_view path)
|
||||
{
|
||||
auto const filename = tr_pathbuf{ path, '/', "index.html"sv };
|
||||
bool const found = tr_sys_path_exists(filename);
|
||||
tr_logAddTrace(fmt::format(FMT_STRING("Searching for web interface file '{:s}'"), filename));
|
||||
return found;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// ---
|
||||
|
||||
std::string tr_getDefaultConfigDir(std::string_view appname)
|
||||
{
|
||||
if (std::empty(appname))
|
||||
|
@ -149,40 +190,6 @@ size_t tr_getDefaultConfigDirToBuf(char const* appname, char* buf, size_t buflen
|
|||
return tr_strvToBuf(tr_getDefaultConfigDir(appname != nullptr ? appname : ""), buf, buflen);
|
||||
}
|
||||
|
||||
static std::string getXdgEntryFromUserDirs(std::string_view key)
|
||||
{
|
||||
auto content = std::vector<char>{};
|
||||
if (auto const filename = fmt::format("{:s}/{:s}"sv, xdgConfigHome(), "user-dirs.dirs"sv);
|
||||
!tr_sys_path_exists(filename) || !tr_loadFile(filename, content) || std::empty(content))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// search for key="val" and extract val
|
||||
auto const search = fmt::format(FMT_STRING("{:s}=\""), key);
|
||||
auto begin = std::search(std::begin(content), std::end(content), std::begin(search), std::end(search));
|
||||
if (begin == std::end(content))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
std::advance(begin, std::size(search));
|
||||
auto const end = std::find(begin, std::end(content), '"');
|
||||
if (end == std::end(content))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
auto val = std::string{ begin, end };
|
||||
|
||||
// if val contains "$HOME", replace that with getHomeDir()
|
||||
auto constexpr Home = "$HOME"sv;
|
||||
if (auto const it = std::search(std::begin(val), std::end(val), std::begin(Home), std::end(Home)); it != std::end(val))
|
||||
{
|
||||
val.replace(it, it + std::size(Home), getHomeDir());
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string tr_getDefaultDownloadDir()
|
||||
{
|
||||
if (auto dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(dir))
|
||||
|
@ -209,17 +216,7 @@ size_t tr_getDefaultDownloadDirToBuf(char* buf, size_t buflen)
|
|||
return tr_strvToBuf(tr_getDefaultDownloadDir(), buf, buflen);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static bool isWebClientDir(std::string_view path)
|
||||
{
|
||||
auto const filename = tr_pathbuf{ path, '/', "index.html"sv };
|
||||
bool const found = tr_sys_path_exists(filename);
|
||||
tr_logAddTrace(fmt::format(FMT_STRING("Searching for web interface file '{:s}'"), filename));
|
||||
return found;
|
||||
}
|
||||
// ---
|
||||
|
||||
std::string tr_getWebClientDir([[maybe_unused]] tr_session const* session)
|
||||
{
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
|
||||
enum class UpnpState
|
||||
{
|
||||
Idle,
|
||||
|
@ -45,29 +44,6 @@ enum class UpnpState
|
|||
WillMap, // next action is UPNP_AddPortMapping()
|
||||
WillUnmap // next action is UPNP_DeletePortMapping()
|
||||
};
|
||||
|
||||
constexpr auto portFwdState(UpnpState upnp_state, bool is_mapped)
|
||||
{
|
||||
switch (upnp_state)
|
||||
{
|
||||
case UpnpState::WillDiscover:
|
||||
case UpnpState::Discovering:
|
||||
return TR_PORT_UNMAPPED;
|
||||
|
||||
case UpnpState::WillMap:
|
||||
return TR_PORT_MAPPING;
|
||||
|
||||
case UpnpState::WillUnmap:
|
||||
return TR_PORT_UNMAPPING;
|
||||
|
||||
case UpnpState::Idle:
|
||||
return is_mapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED;
|
||||
|
||||
default: // UpnpState::FAILED:
|
||||
return TR_PORT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct tr_upnp
|
||||
|
@ -102,25 +78,31 @@ struct tr_upnp
|
|||
std::optional<std::future<UPNPDev*>> discover_future;
|
||||
};
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
tr_upnp* tr_upnpInit()
|
||||
namespace
|
||||
{
|
||||
return new tr_upnp();
|
||||
constexpr auto port_fwd_state(UpnpState upnp_state, bool is_mapped)
|
||||
{
|
||||
switch (upnp_state)
|
||||
{
|
||||
case UpnpState::WillDiscover:
|
||||
case UpnpState::Discovering:
|
||||
return TR_PORT_UNMAPPED;
|
||||
|
||||
case UpnpState::WillMap:
|
||||
return TR_PORT_MAPPING;
|
||||
|
||||
case UpnpState::WillUnmap:
|
||||
return TR_PORT_UNMAPPING;
|
||||
|
||||
case UpnpState::Idle:
|
||||
return is_mapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED;
|
||||
|
||||
default: // UpnpState::FAILED:
|
||||
return TR_PORT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
void tr_upnpClose(tr_upnp* handle)
|
||||
{
|
||||
delete handle;
|
||||
}
|
||||
|
||||
/**
|
||||
*** Wrappers for miniupnpc functions
|
||||
**/
|
||||
|
||||
static struct UPNPDev* tr_upnpDiscover(int msec, char const* bindaddr)
|
||||
[[nodiscard]] UPNPDev* upnp_discover(int msec, char const* bindaddr)
|
||||
{
|
||||
UPNPDev* ret = nullptr;
|
||||
auto have_err = bool{};
|
||||
|
@ -148,7 +130,7 @@ static struct UPNPDev* tr_upnpDiscover(int msec, char const* bindaddr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int tr_upnpGetSpecificPortMappingEntry(tr_upnp const* handle, char const* proto)
|
||||
[[nodiscard]] int get_specific_port_mapping_entry(tr_upnp const* handle, char const* proto)
|
||||
{
|
||||
auto int_client = std::array<char, 16>{};
|
||||
auto int_port = std::array<char, 16>{};
|
||||
|
@ -191,7 +173,7 @@ static int tr_upnpGetSpecificPortMappingEntry(tr_upnp const* handle, char const*
|
|||
return err;
|
||||
}
|
||||
|
||||
static int tr_upnpAddPortMapping(tr_upnp const* handle, char const* proto, tr_port port, char const* desc)
|
||||
[[nodiscard]] int upnp_add_port_mapping(tr_upnp const* handle, char const* proto, tr_port port, char const* desc)
|
||||
{
|
||||
int const old_errno = errno;
|
||||
errno = 0;
|
||||
|
@ -230,17 +212,13 @@ static int tr_upnpAddPortMapping(tr_upnp const* handle, char const* proto, tr_po
|
|||
return err;
|
||||
}
|
||||
|
||||
static void tr_upnpDeletePortMapping(tr_upnp const* handle, char const* proto, tr_port port)
|
||||
void tr_upnpDeletePortMapping(tr_upnp const* handle, char const* proto, tr_port port)
|
||||
{
|
||||
auto const port_str = fmt::format(FMT_STRING("{:d}"), port.host());
|
||||
|
||||
UPNP_DeletePortMapping(handle->urls.controlURL, handle->data.first.servicetype, port_str.c_str(), proto, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
enum
|
||||
{
|
||||
UPNP_IGD_NONE = 0,
|
||||
|
@ -249,19 +227,32 @@ enum
|
|||
UPNP_IGD_INVALID = 3
|
||||
};
|
||||
|
||||
static auto* discoverThreadfunc(std::string bindaddr) // NOLINT performance-unnecessary-value-param
|
||||
auto* discover_thread_func(std::string bindaddr) // NOLINT performance-unnecessary-value-param
|
||||
{
|
||||
// If multicastif is not NULL, it will be used instead of the default
|
||||
// multicast interface for sending SSDP discover packets.
|
||||
char const* multicastif = std::empty(bindaddr) ? nullptr : bindaddr.c_str();
|
||||
return tr_upnpDiscover(2000, multicastif);
|
||||
return upnp_discover(2000, multicastif);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool isFutureReady(std::future<T> const& future)
|
||||
bool is_future_ready(std::future<T> const& future)
|
||||
{
|
||||
return future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// ---
|
||||
|
||||
tr_upnp* tr_upnpInit()
|
||||
{
|
||||
return new tr_upnp();
|
||||
}
|
||||
|
||||
void tr_upnpClose(tr_upnp* handle)
|
||||
{
|
||||
delete handle;
|
||||
}
|
||||
|
||||
tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_enabled, bool do_port_check, std::string bindaddr)
|
||||
{
|
||||
|
@ -269,7 +260,7 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
|||
{
|
||||
TR_ASSERT(!handle->discover_future);
|
||||
|
||||
auto task = std::packaged_task<UPNPDev*(std::string)>{ discoverThreadfunc };
|
||||
auto task = std::packaged_task<UPNPDev*(std::string)>{ discover_thread_func };
|
||||
handle->discover_future = task.get_future();
|
||||
handle->state = UpnpState::Discovering;
|
||||
|
||||
|
@ -277,7 +268,7 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
|||
}
|
||||
|
||||
if (is_enabled && handle->state == UpnpState::Discovering && handle->discover_future &&
|
||||
isFutureReady(*handle->discover_future))
|
||||
is_future_ready(*handle->discover_future))
|
||||
{
|
||||
auto* const devlist = handle->discover_future->get();
|
||||
handle->discover_future.reset();
|
||||
|
@ -309,8 +300,8 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
|||
}
|
||||
|
||||
if (is_enabled && handle->isMapped && do_port_check &&
|
||||
((tr_upnpGetSpecificPortMappingEntry(handle, "TCP") != UPNPCOMMAND_SUCCESS) ||
|
||||
(tr_upnpGetSpecificPortMappingEntry(handle, "UDP") != UPNPCOMMAND_SUCCESS)))
|
||||
((get_specific_port_mapping_entry(handle, "TCP") != UPNPCOMMAND_SUCCESS) ||
|
||||
(get_specific_port_mapping_entry(handle, "UDP") != UPNPCOMMAND_SUCCESS)))
|
||||
{
|
||||
tr_logAddInfo(fmt::format(_("Port {port} is not forwarded"), fmt::arg("port", handle->port.host())));
|
||||
handle->isMapped = false;
|
||||
|
@ -347,8 +338,8 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
|||
else
|
||||
{
|
||||
auto const desc = fmt::format(FMT_STRING("Transmission at {:d}"), port.host());
|
||||
int const err_tcp = tr_upnpAddPortMapping(handle, "TCP", port, desc.c_str());
|
||||
int const err_udp = tr_upnpAddPortMapping(handle, "UDP", port, desc.c_str());
|
||||
int const err_tcp = upnp_add_port_mapping(handle, "TCP", port, desc.c_str());
|
||||
int const err_udp = upnp_add_port_mapping(handle, "UDP", port, desc.c_str());
|
||||
|
||||
handle->isMapped = err_tcp == 0 || err_udp == 0;
|
||||
}
|
||||
|
@ -374,5 +365,5 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
|||
}
|
||||
}
|
||||
|
||||
return portFwdState(handle->state, handle->isMapped);
|
||||
return port_fwd_state(handle->state, handle->isMapped);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
|
||||
using namespace std::literals;
|
||||
|
||||
static void handle_sigchld(int /*i*/)
|
||||
namespace
|
||||
{
|
||||
void handle_sigchld(int /*i*/)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
|
@ -40,7 +42,7 @@ static void handle_sigchld(int /*i*/)
|
|||
/* FIXME: Call old handler, if any */
|
||||
}
|
||||
|
||||
static void set_system_error(tr_error** error, int code, std::string_view what)
|
||||
void set_system_error(tr_error** error, int code, std::string_view what)
|
||||
{
|
||||
if (error == nullptr)
|
||||
{
|
||||
|
@ -50,7 +52,7 @@ static void set_system_error(tr_error** error, int code, std::string_view what)
|
|||
tr_error_set(error, code, fmt::format(FMT_STRING("{:s} failed: {:s} ({:d})"), what, tr_strerror(code), code));
|
||||
}
|
||||
|
||||
static bool tr_spawn_async_in_child(
|
||||
[[nodiscard]] bool tr_spawn_async_in_child(
|
||||
char const* const* cmd,
|
||||
std::map<std::string_view, std::string_view> const& env,
|
||||
std::string_view work_dir)
|
||||
|
@ -82,7 +84,7 @@ static bool tr_spawn_async_in_child(
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool tr_spawn_async_in_parent(int pipe_fd, tr_error** error)
|
||||
[[nodiscard]] bool tr_spawn_async_in_parent(int pipe_fd, tr_error** error)
|
||||
{
|
||||
int child_errno = 0;
|
||||
ssize_t count = 0;
|
||||
|
@ -114,6 +116,7 @@ static bool tr_spawn_async_in_parent(int pipe_fd, tr_error** error)
|
|||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool tr_spawn_async(
|
||||
char const* const* cmd,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -73,10 +73,12 @@ void tr_utpClose(tr_session* /*session*/)
|
|||
|
||||
#else
|
||||
|
||||
namespace
|
||||
{
|
||||
/* Greg says 50ms works for them. */
|
||||
static auto constexpr UtpInterval = 50ms;
|
||||
auto constexpr UtpInterval = 50ms;
|
||||
|
||||
static void utp_on_accept(tr_session* const session, UTPSocket* const utp_sock)
|
||||
void utp_on_accept(tr_session* const session, UTPSocket* const utp_sock)
|
||||
{
|
||||
auto from_storage = sockaddr_storage{};
|
||||
auto* const from = (struct sockaddr*)&from_storage;
|
||||
|
@ -102,7 +104,7 @@ static void utp_on_accept(tr_session* const session, UTPSocket* const utp_sock)
|
|||
}
|
||||
}
|
||||
|
||||
static void utp_send_to(
|
||||
void utp_send_to(
|
||||
tr_session const* const ss,
|
||||
uint8_t const* const buf,
|
||||
size_t const buflen,
|
||||
|
@ -112,7 +114,7 @@ static void utp_send_to(
|
|||
ss->udp_core_->sendto(buf, buflen, to, tolen);
|
||||
}
|
||||
|
||||
static uint64 utp_callback(utp_callback_arguments* args)
|
||||
uint64 utp_callback(utp_callback_arguments* args)
|
||||
{
|
||||
auto* const session = static_cast<tr_session*>(utp_context_get_userdata(args->context));
|
||||
|
||||
|
@ -139,7 +141,7 @@ static uint64 utp_callback(utp_callback_arguments* args)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void reset_timer(tr_session* session)
|
||||
void reset_timer(tr_session* session)
|
||||
{
|
||||
auto interval = std::chrono::milliseconds{};
|
||||
auto const random_percent = tr_rand_int(1000U) / 1000.0;
|
||||
|
@ -167,7 +169,7 @@ static void reset_timer(tr_session* session)
|
|||
session->utp_timer->startSingleShot(interval);
|
||||
}
|
||||
|
||||
static void timer_callback(void* vsession)
|
||||
void timer_callback(void* vsession)
|
||||
{
|
||||
auto* session = static_cast<tr_session*>(vsession);
|
||||
|
||||
|
@ -177,6 +179,7 @@ static void timer_callback(void* vsession)
|
|||
utp_check_timeouts(session->utp_context);
|
||||
reset_timer(session);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void tr_utpInit(tr_session* session)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue