diff --git a/Transmission.xcodeproj/project.pbxproj b/Transmission.xcodeproj/project.pbxproj index 97c0dd311..d5b1bd3c3 100644 --- a/Transmission.xcodeproj/project.pbxproj +++ b/Transmission.xcodeproj/project.pbxproj @@ -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 = ""; }; ED20B87D285892C5005FA6BE /* crc32_multipliers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crc32_multipliers.h; path = lib/crc32_multipliers.h; sourceTree = ""; }; ED20B87E285892C5005FA6BE /* crc32_tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crc32_tables.h; path = lib/crc32_tables.h; sourceTree = ""; }; + ED67FB402B70FCE400D8A037 /* settings.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = settings.cc; sourceTree = ""; }; + ED67FB412B70FCE400D8A037 /* settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = settings.h; sourceTree = ""; }; ED86936D2ADAE34D00342B1A /* DefaultAppHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DefaultAppHelper.h; sourceTree = ""; }; ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DefaultAppHelper.mm; sourceTree = ""; }; ED8A163B2735A8AA000D61F9 /* peer-mgr-active-requests.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "peer-mgr-active-requests.h"; sourceTree = ""; }; @@ -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 */, diff --git a/libtransmission/CMakeLists.txt b/libtransmission/CMakeLists.txt index c7192d62d..7d71088cf 100644 --- a/libtransmission/CMakeLists.txt +++ b/libtransmission/CMakeLists.txt @@ -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 diff --git a/libtransmission/rpc-server.cc b/libtransmission/rpc-server.cc index ed2fc9bc3..6a8c91466 100644 --- a/libtransmission/rpc-server.cc +++ b/libtransmission/rpc-server.cc @@ -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() } , 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(); - 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(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(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(default_value)); - RPC_SETTINGS_FIELDS(V) -#undef V - return tr_variant{ std::move(settings) }; -} - tr_rpc_server::~tr_rpc_server() { stop_server(this); diff --git a/libtransmission/rpc-server.h b/libtransmission/rpc-server.h index 62fae3fda..9829ac3a1 100644 --- a/libtransmission/rpc-server.h +++ b/libtransmission/rpc-server.h @@ -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 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 host_whitelist_; std::vector whitelist_; diff --git a/libtransmission/session.cc b/libtransmission/session.cc index 7be1ec363..f9fa242c0 100644 --- a/libtransmission/session.cc +++ b/libtransmission/session.cc @@ -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(this, settings_dict) } + , rpc_server_{ std::make_unique(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(); }) } diff --git a/libtransmission/settings.cc b/libtransmission/settings.cc new file mode 100644 index 000000000..4c8706b70 --- /dev/null +++ b/libtransmission/settings.cc @@ -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 + +#include "libtransmission/settings.h" +#include "libtransmission/variant.h" + +namespace libtransmission +{ +namespace +{ +struct LoadVisitor +{ + explicit constexpr LoadVisitor(tr_variant const& src) + : src_{ src } + { + } + + template + void operator()(T* const tgt) + { + if (auto val = VariantConverter::load(src_)) + { + *tgt = *val; + } + } + +private: + tr_variant const& src_; +}; + +struct SaveVisitor +{ + constexpr SaveVisitor(tr_variant::Map& tgt, tr_quark key) + : tgt_{ tgt } + , key_{ key } + { + } + + template + void operator()(T const* const src) + { + tgt_.try_emplace(key_, VariantConverter::save(*src)); + } + +private: + tr_variant::Map& tgt_; + tr_quark key_; +}; +} // unnamed namespace + +void Settings::load(tr_variant const& src) +{ + auto const* map = src.get_if(); + 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(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 diff --git a/libtransmission/settings.h b/libtransmission/settings.h new file mode 100644 index 000000000..b6b232aac --- /dev/null +++ b/libtransmission/settings.h @@ -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 // for size_t +#include +#include +#include +#include + +#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; + using Fields = std::vector; + + Settings() = default; + + [[nodiscard]] virtual Fields fields() = 0; +}; +} // namespace libtransmission