1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-03-08 21:04:25 +00:00

refactor: add libtransmission::Settings (#6575)

* refactor: add libtransmission::Settings

* refactor: move RPC server's settings into tr_rpc_server::Settings

* build: update project.pbxproj

---------

Co-authored-by: Dzmitry Neviadomski <nevack.d@gmail.com>
This commit is contained in:
Charles Kerr 2024-02-07 10:14:47 -06:00 committed by GitHub
parent 168d56cefc
commit 340d0d4966
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 263 additions and 111 deletions

View file

@ -448,6 +448,8 @@
ED20B87C28589274005FA6BE /* common_defs.h in Headers */ = {isa = PBXBuildFile; fileRef = ED20B87B28589274005FA6BE /* common_defs.h */; };
ED20B87F285892C5005FA6BE /* crc32_multipliers.h in Headers */ = {isa = PBXBuildFile; fileRef = ED20B87D285892C5005FA6BE /* crc32_multipliers.h */; };
ED20B880285892C5005FA6BE /* crc32_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = ED20B87E285892C5005FA6BE /* crc32_tables.h */; };
ED67FB422B70FCE400D8A037 /* settings.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED67FB402B70FCE400D8A037 /* settings.cc */; };
ED67FB432B70FCE400D8A037 /* settings.h in Headers */ = {isa = PBXBuildFile; fileRef = ED67FB412B70FCE400D8A037 /* settings.h */; };
ED86936F2ADAE34D00342B1A /* DefaultAppHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */; };
ED8A163F2735A8AA000D61F9 /* peer-mgr-active-requests.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8A163B2735A8AA000D61F9 /* peer-mgr-active-requests.h */; };
ED8A16402735A8AA000D61F9 /* peer-mgr-active-requests.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED8A163C2735A8AA000D61F9 /* peer-mgr-active-requests.cc */; };
@ -1363,6 +1365,8 @@
ED20B87B28589274005FA6BE /* common_defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common_defs.h; sourceTree = "<group>"; };
ED20B87D285892C5005FA6BE /* crc32_multipliers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crc32_multipliers.h; path = lib/crc32_multipliers.h; sourceTree = "<group>"; };
ED20B87E285892C5005FA6BE /* crc32_tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crc32_tables.h; path = lib/crc32_tables.h; sourceTree = "<group>"; };
ED67FB402B70FCE400D8A037 /* settings.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = settings.cc; sourceTree = "<group>"; };
ED67FB412B70FCE400D8A037 /* settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = settings.h; sourceTree = "<group>"; };
ED86936D2ADAE34D00342B1A /* DefaultAppHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DefaultAppHelper.h; sourceTree = "<group>"; };
ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DefaultAppHelper.mm; sourceTree = "<group>"; };
ED8A163B2735A8AA000D61F9 /* peer-mgr-active-requests.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "peer-mgr-active-requests.h"; sourceTree = "<group>"; };
@ -1884,6 +1888,8 @@
CCEBA596277340F6DF9F4483 /* session-alt-speeds.h */,
D5C306568A7346FFFB8EFAD1 /* session-settings.cc */,
D5C306568A7346FFFB8EFAD3 /* session-settings.h */,
ED67FB402B70FCE400D8A037 /* settings.cc */,
ED67FB412B70FCE400D8A037 /* settings.h */,
D9057D68C13B75636539B681 /* variant-converters.cc */,
A25D2CBB0CF4C7190096A262 /* stats.cc */,
A25D2CBA0CF4C7190096A262 /* stats.h */,
@ -2398,6 +2404,7 @@
A29DF8BE0DB2545F00D04E5A /* verify.h in Headers */,
C1FEE57B1C3223CC00D62832 /* watchdir.h in Headers */,
A2AAB6650DE0D08B00E04DDA /* blocklist.h in Headers */,
ED67FB432B70FCE400D8A037 /* settings.h in Headers */,
A2A4E9210DE0F7E9000CE197 /* web.h in Headers */,
A25E03E20E4015380086C225 /* tr-getopt.h in Headers */,
A21FBBAB0EDA78C300BC3C51 /* bandwidth.h in Headers */,
@ -3173,6 +3180,7 @@
C1033E081A3279B800EF44D8 /* crypto-utils-ccrypto.cc in Sources */,
A22CFCA80FC24ED80009BD3E /* tr-dht.cc in Sources */,
0A6169A70FE5C9A200C66CE6 /* bitfield.cc in Sources */,
ED67FB422B70FCE400D8A037 /* settings.cc in Sources */,
1BB44E07B1B52E28291B4E32 /* file-piece-map.cc in Sources */,
A25964A6106D73A800453B31 /* announcer.cc in Sources */,
66F977825E65AD498C028BB0 /* announce-list.cc in Sources */,

View file

@ -118,6 +118,8 @@ target_sources(${TR_NAME}
session-thread.h
session.cc
session.h
settings.cc
settings.h
stats.cc
stats.h
subprocess-posix.cc

View file

@ -400,7 +400,7 @@ bool isHostnameAllowed(tr_rpc_server const* server, evhttp_request const* req)
}
/* If whitelist is disabled, no restrictions. */
if (!server->is_host_whitelist_enabled_)
if (!server->settings_.is_host_whitelist_enabled)
{
return true;
}
@ -460,7 +460,7 @@ bool is_authorized(tr_rpc_server const* server, char const* auth_header)
auto decoded = std::string_view{ decoded_str };
auto const username = tr_strv_sep(&decoded, ':');
auto const password = decoded;
return server->username() == username && tr_ssha1_matches(server->salted_password_, password);
return server->username() == username && tr_ssha1_matches(server->settings().salted_password, password);
}
void handle_request(struct evhttp_request* req, void* arg)
@ -474,7 +474,7 @@ void handle_request(struct evhttp_request* req, void* arg)
{
evhttp_add_header(req->output_headers, "Server", MY_REALM);
if (server->is_anti_brute_force_enabled() && server->login_attempts_ >= server->anti_brute_force_limit_)
if (server->is_anti_brute_force_enabled() && server->login_attempts_ >= server->settings().anti_brute_force_limit)
{
send_simple_response(req, HttpErrorForbidden);
return;
@ -659,7 +659,7 @@ void start_server(tr_rpc_server* server)
auto const port = server->port();
bool const success = server->bind_address_->is_unix_addr() ?
bindUnixSocket(base, httpd, address.c_str(), server->socket_mode_) :
bindUnixSocket(base, httpd, address.c_str(), server->settings().socket_mode) :
(evhttp_bind_socket(httpd, address.c_str(), port.host()) != -1);
auto const addr_port_str = server->bind_address_->to_string(port);
@ -750,12 +750,12 @@ auto parse_whitelist(std::string_view whitelist)
void tr_rpc_server::set_enabled(bool is_enabled)
{
is_enabled_ = is_enabled;
settings_.is_enabled = is_enabled;
session->run_in_session_thread(
[this]()
{
if (!is_enabled_)
if (!settings_.is_enabled)
{
stop_server(this);
}
@ -768,12 +768,12 @@ void tr_rpc_server::set_enabled(bool is_enabled)
void tr_rpc_server::set_port(tr_port port) noexcept
{
if (port_ == port)
if (settings_.port == port)
{
return;
}
port_ = port;
settings_.port = port;
if (is_enabled())
{
@ -783,35 +783,34 @@ void tr_rpc_server::set_port(tr_port port) noexcept
void tr_rpc_server::set_url(std::string_view url)
{
url_ = url;
tr_logAddDebug(fmt::format("setting our URL to '{:s}'", url_));
settings_.url = url;
tr_logAddDebug(fmt::format("setting our URL to '{:s}'", url));
}
void tr_rpc_server::set_whitelist(std::string_view whitelist)
{
this->whitelist_str_ = whitelist;
this->whitelist_ = parse_whitelist(whitelist);
settings_.whitelist_str = whitelist;
whitelist_ = parse_whitelist(whitelist);
}
// --- PASSWORD
void tr_rpc_server::set_username(std::string_view username)
{
username_ = username;
tr_logAddDebug(fmt::format("setting our username to '{:s}'", username_));
settings_.username = username;
tr_logAddDebug(fmt::format("setting our username to '{:s}'", username));
}
void tr_rpc_server::set_password(std::string_view password) noexcept
{
auto const is_salted = tr_ssha1_test(password);
salted_password_ = is_salted ? password : tr_ssha1(password);
tr_logAddDebug(fmt::format("setting our salted password to '{:s}'", salted_password_));
settings_.salted_password = is_salted ? password : tr_ssha1(password);
tr_logAddDebug(fmt::format("setting our salted password to '{:s}'", settings_.salted_password));
}
void tr_rpc_server::set_password_enabled(bool enabled)
{
authentication_required_ = enabled;
settings_.authentication_required = enabled;
tr_logAddDebug(fmt::format("setting password-enabled to '{}'", enabled));
}
@ -822,7 +821,7 @@ std::string tr_rpc_server::get_bind_address() const
void tr_rpc_server::set_anti_brute_force_enabled(bool enabled) noexcept
{
is_anti_brute_force_enabled_ = enabled;
settings_.is_anti_brute_force_enabled = enabled;
if (!enabled)
{
@ -832,60 +831,47 @@ void tr_rpc_server::set_anti_brute_force_enabled(bool enabled) noexcept
// --- LIFECYCLE
tr_rpc_server::tr_rpc_server(tr_session* session_in, tr_variant const& settings)
tr_rpc_server::tr_rpc_server(tr_session* session_in, Settings settings)
: compressor{ libdeflate_alloc_compressor(DeflateLevel), libdeflate_free_compressor }
, web_client_dir_{ tr_getWebClientDir(session_in) }
, bind_address_{ std::make_unique<class tr_rpc_address>() }
, session{ session_in }
{
load(settings);
load(std::move(settings));
}
void tr_rpc_server::load(tr_variant const& src)
void tr_rpc_server::load(Settings settings)
{
auto const* const src_map = src.get_if<tr_variant::Map>();
if (src_map != nullptr)
settings_ = std::move(settings);
if (!tr_strv_ends_with(settings_.url, '/'))
{
#define V(key, field, type, default_value, comment) \
if (auto const iter = src_map->find(key); iter != std::end(*src_map)) \
{ \
if (auto val = libtransmission::VariantConverter::load<decltype(field)>(iter->second); val) \
{ \
this->field = *val; \
} \
}
RPC_SETTINGS_FIELDS(V)
#undef V
settings_.url = fmt::format("{:s}/", settings_.url);
}
if (!tr_strv_ends_with(url_, '/'))
{
url_ = fmt::format("{:s}/", url_);
}
host_whitelist_ = parse_whitelist(settings_.host_whitelist_str);
set_password_enabled(settings_.authentication_required);
set_whitelist(settings_.whitelist_str);
set_username(settings_.username);
set_password(settings_.salted_password);
this->host_whitelist_ = parse_whitelist(host_whitelist_str_);
this->set_password_enabled(authentication_required_);
this->set_whitelist(whitelist_str_);
this->set_username(username_);
this->set_password(salted_password_);
if (!bind_address_->from_string(bind_address_str_))
if (!bind_address_->from_string(settings_.bind_address_str))
{
// NOTE: bind_address_ is default initialized to INADDR_ANY
tr_logAddWarn(fmt::format(
_("The '{key}' setting is '{value}' but must be an IPv4 or IPv6 address or a Unix socket path. Using default value '0.0.0.0'"),
fmt::arg("key", tr_quark_get_string_view(TR_KEY_rpc_bind_address)),
fmt::arg("value", bind_address_str_)));
fmt::arg("value", settings_.bind_address_str)));
}
if (bind_address_->is_unix_addr())
{
this->set_whitelist_enabled(false);
this->is_host_whitelist_enabled_ = false;
set_whitelist_enabled(false);
settings_.is_host_whitelist_enabled = false;
}
if (this->is_enabled())
{
auto const rpc_uri = bind_address_->to_string(this->port()) + this->url_;
auto const rpc_uri = bind_address_->to_string(port()) + settings_.url;
tr_logAddInfo(fmt::format(_("Serving RPC and Web requests on {address}"), fmt::arg("address", rpc_uri)));
session->run_in_session_thread(start_server, this);
@ -906,26 +892,6 @@ void tr_rpc_server::load(tr_variant const& src)
}
}
tr_variant tr_rpc_server::settings() const
{
auto settings = tr_variant::Map{};
#define V(key, field, type, default_value, comment) \
settings.try_emplace(key, libtransmission::VariantConverter::save<decltype(field)>(field));
RPC_SETTINGS_FIELDS(V)
#undef V
return tr_variant{ std::move(settings) };
}
tr_variant tr_rpc_server::default_settings()
{
auto settings = tr_variant::Map{};
#define V(key, field, type, default_value, comment) \
settings.try_emplace(key, libtransmission::VariantConverter::save<decltype(field)>(default_value));
RPC_SETTINGS_FIELDS(V)
#undef V
return tr_variant{ std::move(settings) };
}
tr_rpc_server::~tr_rpc_server()
{
stop_server(this);

View file

@ -19,6 +19,7 @@
#include "libtransmission/net.h"
#include "libtransmission/quark.h"
#include "libtransmission/settings.h"
#include "libtransmission/utils-ev.h"
class tr_rpc_address;
@ -31,26 +32,59 @@ namespace libtransmission
class Timer;
}
#define RPC_SETTINGS_FIELDS(V) \
V(TR_KEY_anti_brute_force_enabled, is_anti_brute_force_enabled_, bool, false, "") \
V(TR_KEY_anti_brute_force_threshold, anti_brute_force_limit_, size_t, 100U, "") \
V(TR_KEY_rpc_authentication_required, authentication_required_, bool, false, "") \
V(TR_KEY_rpc_bind_address, bind_address_str_, std::string, "0.0.0.0", "") \
V(TR_KEY_rpc_enabled, is_enabled_, bool, false, "") \
V(TR_KEY_rpc_host_whitelist, host_whitelist_str_, std::string, "", "") \
V(TR_KEY_rpc_host_whitelist_enabled, is_host_whitelist_enabled_, bool, true, "") \
V(TR_KEY_rpc_port, port_, tr_port, tr_port::from_host(TR_DEFAULT_RPC_PORT), "") \
V(TR_KEY_rpc_password, salted_password_, std::string, "", "") \
V(TR_KEY_rpc_socket_mode, socket_mode_, tr_mode_t, 0750, "") \
V(TR_KEY_rpc_url, url_, std::string, TR_DEFAULT_RPC_URL_STR, "") \
V(TR_KEY_rpc_username, username_, std::string, "", "") \
V(TR_KEY_rpc_whitelist, whitelist_str_, std::string, TR_DEFAULT_RPC_WHITELIST, "") \
V(TR_KEY_rpc_whitelist_enabled, is_whitelist_enabled_, bool, true, "")
class tr_rpc_server
{
public:
tr_rpc_server(tr_session* session, tr_variant const& settings);
class Settings final : public libtransmission::Settings
{
public:
Settings() = default;
explicit Settings(tr_variant const& src)
{
load(src);
}
// NB: When adding a field here, you must also add it to
// fields() if you want it to be in session-settings.json
size_t anti_brute_force_limit = 100U;
bool authentication_required = false;
std::string bind_address_str = "0.0.0.0";
std::string host_whitelist_str = "";
bool is_anti_brute_force_enabled = false;
bool is_enabled = false;
bool is_host_whitelist_enabled = true;
bool is_whitelist_enabled = true;
tr_port port = tr_port::from_host(TR_DEFAULT_RPC_PORT);
std::string salted_password = "";
tr_mode_t socket_mode = 0750;
std::string url = TR_DEFAULT_RPC_URL_STR;
std::string username = "";
std::string whitelist_str = TR_DEFAULT_RPC_WHITELIST;
private:
[[nodiscard]] Fields fields() override
{
return {
{ TR_KEY_anti_brute_force_enabled, &is_anti_brute_force_enabled },
{ TR_KEY_anti_brute_force_threshold, &anti_brute_force_limit },
{ TR_KEY_rpc_authentication_required, &authentication_required },
{ TR_KEY_rpc_bind_address, &bind_address_str },
{ TR_KEY_rpc_enabled, &is_enabled },
{ TR_KEY_rpc_host_whitelist, &host_whitelist_str },
{ TR_KEY_rpc_host_whitelist_enabled, &is_host_whitelist_enabled },
{ TR_KEY_rpc_port, &port },
{ TR_KEY_rpc_password, &salted_password },
{ TR_KEY_rpc_socket_mode, &socket_mode },
{ TR_KEY_rpc_url, &url },
{ TR_KEY_rpc_username, &username },
{ TR_KEY_rpc_whitelist, &whitelist_str },
{ TR_KEY_rpc_whitelist_enabled, &is_whitelist_enabled },
};
}
};
tr_rpc_server(tr_session* session, Settings settings);
~tr_rpc_server();
tr_rpc_server(tr_rpc_server&) = delete;
@ -58,84 +92,87 @@ public:
tr_rpc_server& operator=(tr_rpc_server&) = delete;
tr_rpc_server& operator=(tr_rpc_server&&) = delete;
void load(tr_variant const& src);
[[nodiscard]] tr_variant settings() const;
[[nodiscard]] static tr_variant default_settings();
void load(Settings settings);
[[nodiscard]] constexpr Settings const& settings() const
{
return settings_;
}
[[nodiscard]] constexpr tr_port port() const noexcept
{
return port_;
return settings_.port;
}
void set_port(tr_port port) noexcept;
[[nodiscard]] constexpr auto is_enabled() const noexcept
{
return is_enabled_;
return settings_.is_enabled;
}
void set_enabled(bool is_enabled);
[[nodiscard]] constexpr auto is_whitelist_enabled() const noexcept
{
return is_whitelist_enabled_;
return settings_.is_whitelist_enabled;
}
constexpr void set_whitelist_enabled(bool is_whitelist_enabled) noexcept
{
is_whitelist_enabled_ = is_whitelist_enabled;
settings_.is_whitelist_enabled = is_whitelist_enabled;
}
[[nodiscard]] constexpr auto const& whitelist() const noexcept
{
return whitelist_str_;
return settings_.whitelist_str;
}
void set_whitelist(std::string_view whitelist);
[[nodiscard]] constexpr auto const& username() const noexcept
{
return username_;
return settings_.username;
}
void set_username(std::string_view username);
[[nodiscard]] constexpr auto is_password_enabled() const noexcept
{
return authentication_required_;
return settings_.authentication_required;
}
void set_password_enabled(bool enabled);
[[nodiscard]] constexpr auto const& get_salted_password() const noexcept
{
return salted_password_;
return settings_.salted_password;
}
void set_password(std::string_view password) noexcept;
[[nodiscard]] constexpr auto is_anti_brute_force_enabled() const noexcept
{
return is_anti_brute_force_enabled_;
return settings_.is_anti_brute_force_enabled;
}
void set_anti_brute_force_enabled(bool enabled) noexcept;
[[nodiscard]] constexpr auto get_anti_brute_force_limit() const noexcept
{
return anti_brute_force_limit_;
return settings_.anti_brute_force_limit;
}
constexpr void set_anti_brute_force_limit(int limit) noexcept
{
anti_brute_force_limit_ = limit;
settings_.anti_brute_force_limit = limit;
}
std::unique_ptr<libdeflate_compressor, void (*)(libdeflate_compressor*)> compressor;
[[nodiscard]] constexpr auto const& url() const noexcept
{
return url_;
return settings_.url;
}
void set_url(std::string_view url);
@ -144,12 +181,10 @@ public:
[[nodiscard]] constexpr auto socket_mode() const noexcept
{
return socket_mode_;
return settings_.socket_mode;
}
#define V(key, name, type, default_value, comment) type name = type{ default_value };
RPC_SETTINGS_FIELDS(V)
#undef V
Settings settings_;
std::vector<std::string> host_whitelist_;
std::vector<std::string> whitelist_;

View file

@ -460,18 +460,18 @@ tr_address tr_session::bind_address(tr_address_type type) const noexcept
tr_variant tr_sessionGetDefaultSettings()
{
auto ret = tr_variant::make_map();
ret.merge(tr_session_settings::default_settings());
ret.merge(tr_rpc_server::default_settings());
ret.merge(tr_rpc_server::Settings{}.save());
ret.merge(tr_session_alt_speeds::default_settings());
ret.merge(tr_session_settings::default_settings());
return ret;
}
tr_variant tr_sessionGetSettings(tr_session const* session)
{
auto settings = tr_variant::make_map();
settings.merge(session->settings_.settings());
settings.merge(session->alt_speeds_.settings());
settings.merge(session->rpc_server_->settings());
settings.merge(session->rpc_server_->settings().save());
settings.merge(session->settings_.settings());
tr_variantDictAddInt(&settings, TR_KEY_message_level, tr_logGetLevel());
return settings;
}
@ -752,7 +752,7 @@ void tr_session::setSettings(tr_variant const& settings, bool force)
// delegate loading out the other settings
alt_speeds_.load(settings);
rpc_server_->load(settings);
rpc_server_->load(tr_rpc_server::Settings{ settings });
}
void tr_session::setSettings(tr_session_settings&& settings_in, bool force)
@ -2117,7 +2117,7 @@ tr_session::tr_session(std::string_view config_dir, tr_variant const& settings_d
, settings_{ settings_dict }
, session_id_{ tr_time }
, peer_mgr_{ tr_peerMgrNew(this), &tr_peerMgrFree }
, rpc_server_{ std::make_unique<tr_rpc_server>(this, settings_dict) }
, rpc_server_{ std::make_unique<tr_rpc_server>(this, tr_rpc_server::Settings{ settings_dict }) }
, now_timer_{ timer_maker_->create([this]() { on_now_timer(); }) }
, queue_timer_{ timer_maker_->create([this]() { on_queue_timer(); }) }
, save_timer_{ timer_maker_->create([this]() { on_save_timer(); }) }

View file

@ -0,0 +1,86 @@
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <variant>
#include "libtransmission/settings.h"
#include "libtransmission/variant.h"
namespace libtransmission
{
namespace
{
struct LoadVisitor
{
explicit constexpr LoadVisitor(tr_variant const& src)
: src_{ src }
{
}
template<typename T>
void operator()(T* const tgt)
{
if (auto val = VariantConverter::load<T>(src_))
{
*tgt = *val;
}
}
private:
tr_variant const& src_;
};
struct SaveVisitor
{
constexpr SaveVisitor(tr_variant::Map& tgt, tr_quark key)
: tgt_{ tgt }
, key_{ key }
{
}
template<typename T>
void operator()(T const* const src)
{
tgt_.try_emplace(key_, VariantConverter::save<T>(*src));
}
private:
tr_variant::Map& tgt_;
tr_quark key_;
};
} // unnamed namespace
void Settings::load(tr_variant const& src)
{
auto const* map = src.get_if<tr_variant::Map>();
if (map == nullptr)
{
return;
}
for (auto& [key, prop_vptr] : fields())
{
if (auto const iter = map->find(key); iter != std::end(*map))
{
std::visit(LoadVisitor{ iter->second }, prop_vptr);
}
}
}
tr_variant Settings::save() const
{
auto const fields = const_cast<Settings*>(this)->fields();
auto map = tr_variant::Map{};
map.reserve(std::size(fields));
for (auto const& [key, prop_vptr] : fields)
{
std::visit(SaveVisitor{ map, key }, prop_vptr);
}
return map;
}
} // namespace libtransmission

View file

@ -0,0 +1,55 @@
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#pragma once
#include <cstddef> // for size_t
#include <string>
#include <utility>
#include <variant>
#include <vector>
#include "libtransmission/transmission.h"
#include "libtransmission/log.h" // for tr_log_level
#include "libtransmission/net.h" // for tr_port, tr_tos_t
#include "libtransmission/open-files.h" // for tr_open_files::Preallocation
#include "libtransmission/peer-io.h" // tr_preferred_transport
#include "libtransmission/quark.h"
#include "libtransmission/variant.h"
namespace libtransmission
{
class Settings
{
public:
void load(tr_variant const& src);
[[nodiscard]] tr_variant save() const;
protected:
using field_key_type = tr_quark;
using field_mapped_type = std::variant<
bool*,
double*,
size_t*,
std::string*,
tr_encryption_mode*,
tr_log_level*,
tr_mode_t*,
tr_open_files::Preallocation*,
tr_port*,
tr_preferred_transport*,
tr_tos_t*,
tr_verify_added_mode*>;
using field_value_type = std::pair<const field_key_type, field_mapped_type>;
using Fields = std::vector<field_value_type>;
Settings() = default;
[[nodiscard]] virtual Fields fields() = 0;
};
} // namespace libtransmission