refactor: no macros in transmission.h (#8099)

* refactor: replace TR_RPC_SESSION_ID_HEADER macro with TrRpcSessionIdHeader constant

refactor: replace TR_RPC_RPC_VERSION_HEADER macro with TrRpcVersionHeader constant

* refactor: remove macro TR_DEFAULT_RPC_PORT_STR

* refactor: remove macro TR_DEFAULT_PEER_PORT_STR

* refactor: remove macro TR_DEFAULT_PEER_LIMIT_GLOBAL_STR

* refactor: remove macro TR_DEFAULT_PEER_LIMIT_TORRENT_STR

* refactor: remove macro TR_DEFAULT_PEER_SOCKET_TOS_STR

* refactor: replace DEFAULT_BLOCKLIST_FILENAME macro with TrDefaultBlocklistFilename constant

* refactor: rename TrHttpServerDefaultBasePath to TrDefaultHttpServerBasePath for consistency with other defaults

* refactor: group constants together near the top of transmission.h

* refactor: hardcode string lengths to avoid FTBFS on older C++17 compilers

* refactor: move macros to the tr_getopt clients

* refactor: explicitly specify the parameter index to avoid passing in TrRpcSessionIdHeader twice

* refactor: add an error message to new static_asserts
This commit is contained in:
Charles Kerr
2026-01-14 07:52:57 -06:00
committed by GitHub
parent 70bc3eec48
commit 140958a8a1
11 changed files with 62 additions and 69 deletions

View File

@@ -49,6 +49,8 @@ sig_atomic_t manualUpdate = false;
char const* torrentPath = nullptr;
using Arg = tr_option::Arg;
static_assert(TrDefaultPeerPort == 51413, "update 'port' desc");
static_assert(TrDefaultPeerSocketTos == "le", "update 'tos' desc");
auto constexpr Options = std::array<tr_option, 20>{ {
{ 'b', "blocklist", "Enable peer blocklists", "b", Arg::None, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", Arg::None, nullptr },
@@ -61,11 +63,10 @@ auto constexpr Options = std::array<tr_option, 20>{ {
{ 'g', "config-dir", "Where to find configuration files", "g", Arg::Required, "<path>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", Arg::None, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", Arg::None, nullptr },
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", Arg::Required, "<port>" },
{ 'p', "port", "Port for incoming peers (Default: 51413)", "p", Arg::Required, "<port>" },
{ 't',
"tos",
"Peer socket DSCP / ToS setting (number, or a DSCP string, e.g. 'af11' or 'cs0', default=" TR_DEFAULT_PEER_SOCKET_TOS_STR
")",
"Peer socket DSCP / ToS. Number or DSCP string, e.g. 'af11' or 'cs0' (Default: 'le')",
"t",
Arg::Required,
"<dscp-or-tos>" },

View File

@@ -80,8 +80,13 @@ char constexpr Usage[] = "Transmission " LONG_VERSION_STRING
"Usage: transmission-daemon [options]";
using Arg = tr_option::Arg;
static_assert(TrDefaultRpcWhitelist == "127.0.0.1,::1", "update 'allowed' desc");
static_assert(TrDefaultPeerPort == 51413, "update 'peerport' desc");
static_assert(TrDefaultPeerLimitTorrent == 50, "update 'peerlimit-torrent' desc");
static_assert(TrDefaultPeerLimitGlobal == 200, "update 'peerlimit-global' desc");
static_assert(TrDefaultRpcPort == 9091 && R"(update "port" desc)");
auto constexpr Options = std::array<tr_option, 48>{ {
{ 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", Arg::Required, "<list>" },
{ 'a', "allowed", "Allowed IP addresses. (Default: '127.0.0.1,::1')", "a", Arg::Required, "<list>" },
{ 'b', "blocklist", "Enable peer blocklists", "b", Arg::None, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", Arg::None, nullptr },
{ 'c', "watch-dir", "Where to watch for new torrent files", "c", Arg::Required, "<directory>" },
@@ -93,7 +98,7 @@ auto constexpr Options = std::array<tr_option, 48>{ {
{ 'e', "logfile", "Dump the log messages to this filename", "e", Arg::Required, "<filename>" },
{ 'f', "foreground", "Run in the foreground instead of daemonizing", "f", Arg::None, nullptr },
{ 'g', "config-dir", "Where to look for configuration files", "g", Arg::Required, "<path>" },
{ 'p', "port", "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR ")", "p", Arg::Required, "<port>" },
{ 'p', "port", "RPC port (Default: 9091)", "p", Arg::Required, "<port>" },
{ 't', "auth", "Require authentication", "t", Arg::None, nullptr },
{ 'T', "no-auth", "Don't require authentication", "T", Arg::None, nullptr },
{ 'u', "username", "Set username for authentication", "u", Arg::Required, "<username>" },
@@ -122,21 +127,11 @@ auto constexpr Options = std::array<tr_option, 48>{ {
"<protocol(s)>" },
{ 831, "utp", "*DEPRECATED* Enable µTP for peer connections", nullptr, Arg::None, nullptr },
{ 832, "no-utp", "*DEPRECATED* Disable µTP for peer connections", nullptr, Arg::None, nullptr },
{ 'P', "peerport", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "P", Arg::Required, "<port>" },
{ 'P', "peerport", "Port for incoming peers (Default: 51413)", "P", Arg::Required, "<port>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", Arg::None, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", Arg::None, nullptr },
{ 'L',
"peerlimit-global",
"Maximum overall number of peers (Default: " TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ")",
"L",
Arg::Required,
"<limit>" },
{ 'l',
"peerlimit-torrent",
"Maximum number of peers per torrent (Default: " TR_DEFAULT_PEER_LIMIT_TORRENT_STR ")",
"l",
Arg::Required,
"<limit>" },
{ 'L', "peerlimit-global", "Maximum overall number of peers (Default: 200)", "L", Arg::Required, "<limit>" },
{ 'l', "peerlimit-torrent", "Maximum number of peers per torrent (Default: 50)", "l", Arg::Required, "<limit>" },
{ 910, "encryption-required", "Encrypt all peer connections", "er", Arg::None, nullptr },
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", Arg::None, nullptr },
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", Arg::None, nullptr },

View File

@@ -565,7 +565,7 @@ size_t Blocklists::update_primary_blocklist(std::string_view external_file, bool
{
// These rules will replace the default blocklist.
// Build the path of the default blocklist .bin file where we'll save these rules.
auto const bin_file = tr_pathbuf{ folder_, '/', DEFAULT_BLOCKLIST_FILENAME };
auto const bin_file = tr_pathbuf{ folder_, '/', TrDefaultBlocklistFilename };
// Try to save it
auto added = Blocklist::saveNew(external_file, bin_file, is_enabled);

View File

@@ -477,7 +477,7 @@ bool isHostnameAllowed(tr_rpc_server const* server, evhttp_request* const req)
bool test_session_id(tr_rpc_server const* server, evhttp_request* const req)
{
auto const* const input_headers = evhttp_request_get_input_headers(req);
char const* const session_id = evhttp_find_header(input_headers, TR_RPC_SESSION_ID_HEADER);
char const* const session_id = evhttp_find_header(input_headers, std::data(TrRpcSessionIdHeader));
return session_id != nullptr && server->session->sessionId() == session_id;
}
@@ -624,26 +624,26 @@ void handle_request(struct evhttp_request* req, void* arg)
else if (!test_session_id(server, req))
{
auto const session_id = std::string{ server->session->sessionId() };
evhttp_add_header(output_headers, std::data(TrRpcSessionIdHeader), session_id.c_str());
evhttp_add_header(output_headers, std::data(TrRpcVersionHeader), std::data(TrRpcVersionSemver));
auto const expose_val = fmt::format("{:s}, {:s}", TrRpcSessionIdHeader, TrRpcVersionHeader);
evhttp_add_header(output_headers, "Access-Control-Expose-Headers", expose_val.c_str());
auto const body = fmt::format(
"<p>Your request had an invalid session_id header.</p>"
"<p>To fix this, follow these steps:"
"<ol><li> When reading a response, get its " TR_RPC_SESSION_ID_HEADER
" header and remember it"
"<ol><li> When reading a response, get its {0:s} header and remember it"
"<li> Add the updated header to your outgoing requests"
"<li> When you get this 409 error message, resend your request with the updated header"
"</ol></p>"
"<p>This requirement has been added to help prevent "
"<a href=\"https://en.wikipedia.org/wiki/Cross-site_request_forgery\">CSRF</a> "
"attacks.</p>"
"<p><code>{:s}: {:s}</code></p>",
TR_RPC_SESSION_ID_HEADER,
"<p><code>{0:s}: {1:s}</code></p>",
TrRpcSessionIdHeader,
session_id);
evhttp_add_header(output_headers, TR_RPC_SESSION_ID_HEADER, session_id.c_str());
evhttp_add_header(output_headers, TR_RPC_RPC_VERSION_HEADER, std::data(TrRpcVersionSemver));
evhttp_add_header(
output_headers,
"Access-Control-Expose-Headers",
TR_RPC_SESSION_ID_HEADER ", " TR_RPC_RPC_VERSION_HEADER);
send_simple_response(req, 409, body.c_str());
}
#endif

View File

@@ -67,9 +67,9 @@ public:
std::string bind_address_str = "0.0.0.0";
std::string host_whitelist_str;
std::string salted_password;
std::string url = std::string{ TrHttpServerDefaultBasePath };
std::string url = std::string{ TrDefaultHttpServerBasePath };
std::string username;
std::string whitelist_str = TR_DEFAULT_RPC_WHITELIST;
std::string whitelist_str = std::string{ TrDefaultRpcWhitelist };
tr_mode_t socket_mode = 0750;
tr_port port = tr_port::from_host(TrDefaultRpcPort);

View File

@@ -31,6 +31,20 @@ using tr_tracker_id_t = uint32_t;
using tr_torrent_id_t = int;
using tr_mode_t = uint16_t;
inline auto constexpr TrDefaultBlocklistFilename = std::string_view{ "blocklist.bin" };
inline auto constexpr TrDefaultHttpServerBasePath = std::string_view{ "/transmission/" };
inline auto constexpr TrDefaultPeerLimitGlobal = 200U;
inline auto constexpr TrDefaultPeerLimitTorrent = 50U;
inline auto constexpr TrDefaultPeerPort = 51413U;
inline auto constexpr TrDefaultPeerSocketTos = std::string_view{ "le" };
inline auto constexpr TrDefaultRpcPort = 9091U;
inline auto constexpr TrDefaultRpcWhitelist = std::string_view{ "127.0.0.1,::1" };
inline auto constexpr TrHttpServerRpcRelativePath = std::string_view{ "rpc" };
inline auto constexpr TrHttpServerWebRelativePath = std::string_view{ "web/" };
inline auto constexpr TrRpcSessionIdHeader = std::string_view{ "X-Transmission-Session-Id" };
inline auto constexpr TrRpcVersionHeader = std::string_view{ "X-Transmission-Rpc-Version" };
struct tr_block_span_t
{
tr_block_index_t begin;
@@ -50,9 +64,6 @@ struct tr_torrent;
struct tr_torrent_metainfo;
struct tr_variant;
#define TR_RPC_SESSION_ID_HEADER "X-Transmission-Session-Id"
#define TR_RPC_RPC_VERSION_HEADER "X-Transmission-Rpc-Version"
enum tr_verify_added_mode : uint8_t
{
// See discussion @ https://github.com/transmission/transmission/pull/2626
@@ -124,21 +135,6 @@ size_t tr_getDefaultConfigDirToBuf(char const* appname, char* buf, size_t buflen
/** @brief buffer variant of `tr_getDefaultDownloadDir()`. See `tr_strv_to_buf()`. */
size_t tr_getDefaultDownloadDirToBuf(char* buf, size_t buflen);
#define TR_DEFAULT_RPC_WHITELIST "127.0.0.1,::1"
#define TR_DEFAULT_RPC_PORT_STR "9091"
inline auto constexpr TrDefaultRpcPort = 9091U;
#define TR_DEFAULT_PEER_PORT_STR "51413"
inline auto constexpr TrDefaultPeerPort = 51413U;
#define TR_DEFAULT_PEER_SOCKET_TOS_STR "le"
#define TR_DEFAULT_PEER_LIMIT_GLOBAL_STR "200"
inline auto constexpr TrDefaultPeerLimitGlobal = 200U;
#define TR_DEFAULT_PEER_LIMIT_TORRENT_STR "50"
inline auto constexpr TrDefaultPeerLimitTorrent = 50U;
inline auto constexpr TrHttpServerDefaultBasePath = std::string_view{ "/transmission/" };
inline auto constexpr TrHttpServerRpcRelativePath = std::string_view{ "rpc" };
inline auto constexpr TrHttpServerWebRelativePath = std::string_view{ "web/" };
/**
* Add libtransmission's default settings to the benc dictionary.
*
@@ -717,10 +713,6 @@ char const* tr_blocklistGetURL(tr_session const* session);
invokes the "blocklist_update" method */
void tr_blocklistSetURL(tr_session* session, char const* url);
/** @brief the file in the $config/blocklists/ directory that's
used by `tr_blocklistSetContent()` and "blocklist_update" */
#define DEFAULT_BLOCKLIST_FILENAME "blocklist.bin"
/** @} */
/**

View File

@@ -138,9 +138,11 @@ static NSString* const kWebUIURLFormat = @"http://localhost:%ld/";
NSURL* blocklistDir = [[NSFileManager.defaultManager URLsForDirectory:NSApplicationDirectory inDomains:NSUserDomainMask][0]
URLByAppendingPathComponent:@"Transmission/blocklists/"];
[NSFileManager.defaultManager moveItemAtURL:[blocklistDir URLByAppendingPathComponent:@"level1.bin"]
toURL:[blocklistDir URLByAppendingPathComponent:@DEFAULT_BLOCKLIST_FILENAME]
error:nil];
[NSFileManager.defaultManager
moveItemAtURL:[blocklistDir URLByAppendingPathComponent:@"level1.bin"]
toURL:[blocklistDir
URLByAppendingPathComponent:[NSString stringWithUTF8String:TrDefaultBlocklistFilename.data()]]
error:nil];
}
//save a new random port

View File

@@ -238,7 +238,7 @@ tr_variant::Map Prefs::defaults()
map.try_emplace(TR_KEY_remote_session_password, tr_variant::unmanaged_string(""sv));
map.try_emplace(TR_KEY_remote_session_port, TrDefaultRpcPort);
map.try_emplace(TR_KEY_remote_session_requires_authentication, false);
map.try_emplace(TR_KEY_remote_session_url_base_path, tr_variant::unmanaged_string(TrHttpServerDefaultBasePath));
map.try_emplace(TR_KEY_remote_session_url_base_path, tr_variant::unmanaged_string(TrDefaultHttpServerBasePath));
map.try_emplace(TR_KEY_remote_session_username, tr_variant::unmanaged_string(""sv));
map.try_emplace(TR_KEY_show_backup_trackers, false);
map.try_emplace(TR_KEY_show_filterbar, true);

View File

@@ -121,7 +121,7 @@ void RpcClient::sendNetworkRequest(QByteArray const& body, QFutureInterface<RpcR
req.setRawHeader("User-Agent", "Transmisson/" SHORT_VERSION_STRING);
if (!session_id_.isEmpty())
{
req.setRawHeader(TR_RPC_SESSION_ID_HEADER, session_id_);
req.setRawHeader(SessionIdHeaderName, session_id_);
}
if (verbose_)
@@ -206,19 +206,18 @@ void RpcClient::networkRequestFinished(QNetworkReply* reply)
}
}
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409 &&
reply->hasRawHeader(TR_RPC_SESSION_ID_HEADER))
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409 && reply->hasRawHeader(SessionIdHeaderName))
{
// we got a 409 telling us our session id has expired.
// update it and resubmit the request.
auto version_str = QString::fromUtf8("unknown");
if (reply->hasRawHeader(TR_RPC_RPC_VERSION_HEADER))
if (reply->hasRawHeader(VersionHeaderName))
{
network_style_ = api_compat::Style::Tr5;
version_str = QString::fromUtf8(reply->rawHeader(TR_RPC_RPC_VERSION_HEADER));
version_str = QString::fromUtf8(reply->rawHeader(VersionHeaderName));
if (QVersionNumber::fromString(version_str).majorVersion() > TrRpcVersionSemverMajor)
{
fmt::print(
@@ -243,7 +242,7 @@ void RpcClient::networkRequestFinished(QNetworkReply* reply)
static_cast<int>(network_style_));
}
session_id_ = reply->rawHeader(TR_RPC_SESSION_ID_HEADER);
session_id_ = reply->rawHeader(SessionIdHeaderName);
sendNetworkRequest(reply->property(RequestBodyKey).toByteArray(), promise);
return;
}

View File

@@ -86,6 +86,9 @@ private slots:
void localRequestFinished(TrVariantPtr response);
private:
QByteArray const SessionIdHeaderName = std::data(TrRpcSessionIdHeader);
QByteArray const VersionHeaderName = std::data(TrRpcVersionHeader);
QNetworkAccessManager* networkAccessManager();
void sendNetworkRequest(QByteArray const& body, QFutureInterface<RpcResponse> const& promise);

View File

@@ -213,6 +213,7 @@ enum
// --- Command-Line Arguments
using Arg = tr_option::Arg;
static_assert(TrDefaultPeerPort == 51413, "update 'port' desc");
auto constexpr Options = std::array<tr_option, 106>{ {
{ 'a', "add", "Add torrent files by filename or URL", "a", Arg::None, nullptr },
{ 970, "alt-speed", "Use the alternate Limits", "as", Arg::None, nullptr },
@@ -271,7 +272,7 @@ auto constexpr Options = std::array<tr_option, 106>{ {
{ 820, "ssl", "Use SSL when talking to daemon", nullptr, Arg::None, nullptr },
{ 'o', "dht", "Enable distributed hash tables (DHT)", "o", Arg::None, nullptr },
{ 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", Arg::None, nullptr },
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", Arg::Required, "<port>" },
{ 'p', "port", "Port for incoming peers (Default: 51413)", "p", Arg::Required, "<port>" },
{ 962, "port-test", "Port testing", "pt", Arg::None, nullptr },
{ 'P', "random-port", "Random port for incoming peers", "P", Arg::None, nullptr },
{ 900, "priority-high", "Try to download these file(s) first", "ph", Arg::Required, "<files>" },
@@ -875,8 +876,8 @@ void warn_if_unsupported_rpc_version(std::string_view const semver)
[[nodiscard]] size_t parse_response_header(void* ptr, size_t size, size_t nmemb, void* vconfig)
{
using namespace header_utils;
static auto const session_id_header = tr_strlower(TR_RPC_SESSION_ID_HEADER);
static auto const rpc_version_header = tr_strlower(TR_RPC_RPC_VERSION_HEADER);
static auto const session_id_header = tr_strlower(TrRpcSessionIdHeader);
static auto const rpc_version_header = tr_strlower(TrRpcVersionHeader);
auto& config = *static_cast<RemoteConfig*>(vconfig);
@@ -2403,7 +2404,7 @@ CURL* tr_curl_easy_init(std::string* writebuf, RemoteConfig& config)
if (auto const& str = config.session_id; !std::empty(str))
{
auto const h = fmt::format("{:s}: {:s}", TR_RPC_SESSION_ID_HEADER, str);
auto const h = fmt::format("{:s}: {:s}", TrRpcSessionIdHeader, str);
auto* const custom_headers = curl_slist_append(nullptr, h.c_str());
(void)curl_easy_setopt(curl, CURLOPT_HTTPHEADER, custom_headers);
@@ -3578,7 +3579,7 @@ int tr_main(int argc, char* argv[])
if (std::empty(rpcurl))
{
rpcurl = fmt::format("{:s}:{:d}{:s}{:s}", host, port, TrHttpServerDefaultBasePath, TrHttpServerRpcRelativePath);
rpcurl = fmt::format("{:s}:{:d}{:s}{:s}", host, port, TrDefaultHttpServerBasePath, TrHttpServerRpcRelativePath);
}
return process_args(rpcurl.c_str(), argc, (char const* const*)argv, config);