refactor: decouple session settings from the session class (#4053)

This commit is contained in:
Charles Kerr
2022-11-01 19:32:26 -05:00
committed by GitHub
parent 41422c357e
commit 611d36ac84
28 changed files with 2098 additions and 1350 deletions
+20
View File
@@ -427,6 +427,11 @@
C8B27BA428153F6600A22B5D /* edit.cc in Sources */ = {isa = PBXBuildFile; fileRef = C887BEC22807FCE900867D3C /* edit.cc */; };
C8B27BA528153F6900A22B5D /* show.cc in Sources */ = {isa = PBXBuildFile; fileRef = C887BEC32807FCE900867D3C /* show.cc */; };
CAB35C64252F6F5E00552A55 /* mime-types.h in Headers */ = {isa = PBXBuildFile; fileRef = CAB35C62252F6F5E00552A55 /* mime-types.h */; };
CCEBA596277340F6DF9F4480 /* session-alt-speeds.cc in Sources */ = {isa = PBXBuildFile; fileRef = CCEBA596277340F6DF9F4481 /* session-alt-speeds.cc */; };
CCEBA596277340F6DF9F4482 /* session-alt-speeds.h in Headers */ = {isa = PBXBuildFile; fileRef = CCEBA596277340F6DF9F4483 /* session-alt-speeds.h */; };
D5C306568A7346FFFB8EFAD0 /* session-settings.cc in Sources */ = {isa = PBXBuildFile; fileRef = D5C306568A7346FFFB8EFAD1 /* session-settings.cc */; };
D5C306568A7346FFFB8EFAD2 /* session-settings.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C306568A7346FFFB8EFAD3 /* session-settings.h */; };
D9057D68C13B75636539B680 /* variant-converters.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9057D68C13B75636539B681 /* variant-converters.cc */; };
E138A9780C04D88F00C5426C /* ProgressGradients.mm in Sources */ = {isa = PBXBuildFile; fileRef = E138A9760C04D88F00C5426C /* ProgressGradients.mm */; };
E23B55A5FC3B557F7746D510 /* interned-string.h in Headers */ = {isa = PBXBuildFile; fileRef = E23B55A5FC3B557F7746D511 /* interned-string.h */; settings = {ATTRIBUTES = (Project, ); }; };
E71A5565279C2DD600EBFA1E /* tr-assert.mm in Sources */ = {isa = PBXBuildFile; fileRef = E71A5564279C2DD600EBFA1E /* tr-assert.mm */; };
@@ -1183,6 +1188,11 @@
C8B27B9028153F3100A22B5D /* transmission-edit */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "transmission-edit"; sourceTree = BUILT_PRODUCTS_DIR; };
C8B27BA128153F3400A22B5D /* transmission-show */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "transmission-show"; sourceTree = BUILT_PRODUCTS_DIR; };
CAB35C62252F6F5E00552A55 /* mime-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mime-types.h"; sourceTree = "<group>"; };
CCEBA596277340F6DF9F4481 /* session-alt-speeds.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = session-alt-speeds.cc; sourceTree = "<group>"; };
CCEBA596277340F6DF9F4483 /* session-alt-speeds.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = session-alt-speeds.h; sourceTree = "<group>"; };
D5C306568A7346FFFB8EFAD1 /* session-settings.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = session-settings.cc; sourceTree = "<group>"; };
D5C306568A7346FFFB8EFAD3 /* session-settings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = session-settings.h; sourceTree = "<group>"; };
D9057D68C13B75636539B681 /* variant-converters.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = variant-converters.cc; sourceTree = "<group>"; };
E138A9750C04D88F00C5426C /* ProgressGradients.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProgressGradients.h; sourceTree = "<group>"; };
E138A9760C04D88F00C5426C /* ProgressGradients.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ProgressGradients.mm; sourceTree = "<group>"; };
E23B55A5FC3B557F7746D511 /* interned-string.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "interned-string.h"; sourceTree = "<group>"; };
@@ -1685,6 +1695,11 @@
C10C644C1D9AF328003C1B4C /* session-id.h */,
BEFC1DF60C07861A00B0BB3C /* session.cc */,
BEFC1E140C07861A00B0BB3C /* session.h */,
CCEBA596277340F6DF9F4481 /* session-alt-speeds.cc */,
CCEBA596277340F6DF9F4483 /* session-alt-speeds.h */,
D5C306568A7346FFFB8EFAD1 /* session-settings.cc */,
D5C306568A7346FFFB8EFAD3 /* session-settings.h */,
D9057D68C13B75636539B681 /* variant-converters.cc */,
A25D2CBB0CF4C7190096A262 /* stats.cc */,
A25D2CBA0CF4C7190096A262 /* stats.h */,
C11DEA141FCD31C0009E22B9 /* subprocess-posix.cc */,
@@ -2159,6 +2174,8 @@
C1425B381EE9C805001DB85F /* peer-socket.h in Headers */,
BEFC1E450C07861A00B0BB3C /* net.h in Headers */,
BEFC1E4D0C07861A00B0BB3C /* session.h in Headers */,
CCEBA596277340F6DF9F4482 /* session-alt-speeds.h in Headers */,
D5C306568A7346FFFB8EFAD2 /* session-settings.h in Headers */,
BEFC1E4E0C07861A00B0BB3C /* inout.h in Headers */,
BEFC1E520C07861A00B0BB3C /* open-files.h in Headers */,
ED8A163F2735A8AA000D61F9 /* peer-mgr-active-requests.h in Headers */,
@@ -2877,6 +2894,9 @@
A2AAB65C0DE0CF6200E04DDA /* rpc-server.cc in Sources */,
ED8A16402735A8AA000D61F9 /* peer-mgr-active-requests.cc in Sources */,
BEFC1E2F0C07861A00B0BB3C /* session.cc in Sources */,
CCEBA596277340F6DF9F4480 /* session-alt-speeds.cc in Sources */,
D5C306568A7346FFFB8EFAD0 /* session-settings.cc in Sources */,
D9057D68C13B75636539B680 /* variant-converters.cc in Sources */,
BEFC1E320C07861A00B0BB3C /* torrent.cc in Sources */,
2B9BA6C508B488FE586A0AB0 /* torrents.cc in Sources */,
A47A7C87B8B57BE50DF0D410 /* torrent-files.cc in Sources */,
+4
View File
@@ -51,6 +51,8 @@ set(PROJECT_FILES
rpc-server.cc
rpcimpl.cc
session-id.cc
session-alt-speeds.cc
session-settings.cc
session.cc
stats.cc
subprocess-posix.cc
@@ -74,6 +76,7 @@ set(PROJECT_FILES
variant-benc.cc
variant-json.cc
variant.cc
variant-converters.cc
verify.cc
watchdir-generic.cc
watchdir-inotify.cc
@@ -195,6 +198,7 @@ set(${PROJECT_NAME}_PRIVATE_HEADERS
port-forwarding.h
resume.h
rpc-server.h
session-alt-speeds.h
session.h
stats.h
subprocess.h
+1 -1
View File
@@ -877,7 +877,7 @@ static tr_announce_request* announce_request_new(
TR_ASSERT(current_tracker != nullptr);
auto* const req = new tr_announce_request();
req->port = announcer->session->peerPort();
req->port = announcer->session->advertisedPeerPort();
req->announce_url = current_tracker->announce_url;
req->tracker_id = current_tracker->tracker_id;
req->info_hash = tor->infoHash();
+25 -48
View File
@@ -37,6 +37,7 @@
#include "tr-macros.h"
#include "tr-utp.h"
#include "utils.h"
#include "variant.h"
#ifndef IN_MULTICAST
#define IN_MULTICAST(a) (((a)&0xf0000000) == 0xe0000000)
@@ -84,61 +85,37 @@ int tr_address_compare(tr_address const* a, tr_address const* b) noexcept
* TCP sockets
**********************************************************************/
// RFCs 2474, 3246, 4594 & 8622
// Service class names are defined in RFC 4594, RFC 5865, and RFC 8622.
// Not all platforms have these IPTOS_ definitions, so hardcode them here
static auto constexpr IpTosNames = std::array<std::pair<int, std::string_view>, 28>{ {
{ 0x00, "cs0" }, // IPTOS_CLASS_CS0
{ 0x04, "le" },
{ 0x20, "cs1" }, // IPTOS_CLASS_CS1
{ 0x28, "af11" }, // IPTOS_DSCP_AF11
{ 0x30, "af12" }, // IPTOS_DSCP_AF12
{ 0x38, "af13" }, // IPTOS_DSCP_AF13
{ 0x40, "cs2" }, // IPTOS_CLASS_CS2
{ 0x48, "af21" }, // IPTOS_DSCP_AF21
{ 0x50, "af22" }, // IPTOS_DSCP_AF22
{ 0x58, "af23" }, // IPTOS_DSCP_AF23
{ 0x60, "cs3" }, // IPTOS_CLASS_CS3
{ 0x68, "af31" }, // IPTOS_DSCP_AF31
{ 0x70, "af32" }, // IPTOS_DSCP_AF32
{ 0x78, "af33" }, // IPTOS_DSCP_AF33
{ 0x80, "cs4" }, // IPTOS_CLASS_CS4
{ 0x88, "af41" }, // IPTOS_DSCP_AF41
{ 0x90, "af42" }, // IPTOS_DSCP_AF42
{ 0x98, "af43" }, // IPTOS_DSCP_AF43
{ 0xa0, "cs5" }, // IPTOS_CLASS_CS5
{ 0xb8, "ef" }, // IPTOS_DSCP_EF
{ 0xc0, "cs6" }, // IPTOS_CLASS_CS6
{ 0xe0, "cs7" }, // IPTOS_CLASS_CS7
// <netinet/ip.h> lists these TOS names as deprecated,
// but keep them defined here for backward compatibility
{ 0x00, "routine" }, // IPTOS_PREC_ROUTINE
{ 0x02, "lowcost" }, // IPTOS_LOWCOST
{ 0x02, "mincost" }, // IPTOS_MINCOST
{ 0x04, "reliable" }, // IPTOS_RELIABILITY
{ 0x08, "throughput" }, // IPTOS_THROUGHPUT
{ 0x10, "lowdelay" }, // IPTOS_LOWDELAY
} };
std::string tr_netTosToName(int tos)
[[nodiscard]] std::optional<tr_tos_t> tr_tos_t::fromString(std::string_view name)
{
auto const test = [tos](auto const& pair)
auto const needle = tr_strlower(tr_strvStrip(name));
for (auto const& [value, key] : Names)
{
return pair.first == tos;
if (needle == key)
{
return tr_tos_t(value);
}
};
auto const it = std::find_if(std::begin(IpTosNames), std::end(IpTosNames), test);
return it == std::end(IpTosNames) ? std::to_string(tos) : std::string{ it->second };
if (auto value = tr_parseNum<int>(needle); value)
{
return tr_tos_t(*value);
}
return {};
}
std::optional<int> tr_netTosFromName(std::string_view name)
std::string tr_tos_t::toString() const
{
auto const test = [&name](auto const& pair)
for (auto const& [value, key] : Names)
{
return pair.second == name;
};
auto const it = std::find_if(std::begin(IpTosNames), std::end(IpTosNames), test);
return it != std::end(IpTosNames) ? it->first : tr_parseNum<int>(name);
if (value_ == value)
{
return std::string{ key };
}
}
return std::to_string(value_);
}
void tr_netSetTOS([[maybe_unused]] tr_socket_t s, [[maybe_unused]] int tos, tr_address_type type)
+62 -4
View File
@@ -9,6 +9,7 @@
#endif
#include <algorithm> // for std::copy_n
#include <array>
#include <cstddef> // size_t
#include <optional>
#include <string>
@@ -291,11 +292,68 @@ bool tr_net_hasIPv6(tr_port);
/// TOS / DSCP
// get a string of one of <netinet/ip.h>'s IPTOS_ values, e.g. "cs0"
[[nodiscard]] std::string tr_netTosToName(int tos);
/**
* A toString() / fromString() convenience wrapper around the TOS int value
*/
class tr_tos_t
{
public:
constexpr tr_tos_t() = default;
// get the number that corresponds to the specified IPTOS_ name, e.g. "cs0" returns 0x00
[[nodiscard]] std::optional<int> tr_netTosFromName(std::string_view name);
constexpr explicit tr_tos_t(int value)
: value_{ value }
{
}
[[nodiscard]] constexpr operator int() const noexcept
{
return value_;
}
[[nodiscard]] static std::optional<tr_tos_t> fromString(std::string_view);
[[nodiscard]] std::string toString() const;
private:
int value_ = 0x04;
// RFCs 2474, 3246, 4594 & 8622
// Service class names are defined in RFC 4594, RFC 5865, and RFC 8622.
// Not all platforms have these IPTOS_ definitions, so hardcode them here
static auto constexpr Names = std::array<std::pair<int, std::string_view>, 28>{ {
{ 0x00, "cs0" }, // IPTOS_CLASS_CS0
{ 0x04, "le" },
{ 0x20, "cs1" }, // IPTOS_CLASS_CS1
{ 0x28, "af11" }, // IPTOS_DSCP_AF11
{ 0x30, "af12" }, // IPTOS_DSCP_AF12
{ 0x38, "af13" }, // IPTOS_DSCP_AF13
{ 0x40, "cs2" }, // IPTOS_CLASS_CS2
{ 0x48, "af21" }, // IPTOS_DSCP_AF21
{ 0x50, "af22" }, // IPTOS_DSCP_AF22
{ 0x58, "af23" }, // IPTOS_DSCP_AF23
{ 0x60, "cs3" }, // IPTOS_CLASS_CS3
{ 0x68, "af31" }, // IPTOS_DSCP_AF31
{ 0x70, "af32" }, // IPTOS_DSCP_AF32
{ 0x78, "af33" }, // IPTOS_DSCP_AF33
{ 0x80, "cs4" }, // IPTOS_CLASS_CS4
{ 0x88, "af41" }, // IPTOS_DSCP_AF41
{ 0x90, "af42" }, // IPTOS_DSCP_AF42
{ 0x98, "af43" }, // IPTOS_DSCP_AF43
{ 0xa0, "cs5" }, // IPTOS_CLASS_CS5
{ 0xb8, "ef" }, // IPTOS_DSCP_EF
{ 0xc0, "cs6" }, // IPTOS_CLASS_CS6
{ 0xe0, "cs7" }, // IPTOS_CLASS_CS7
// <netinet/ip.h> lists these TOS names as deprecated,
// but keep them defined here for backward compatibility
{ 0x00, "routine" }, // IPTOS_PREC_ROUTINE
{ 0x02, "lowcost" }, // IPTOS_LOWCOST
{ 0x02, "mincost" }, // IPTOS_MINCOST
{ 0x04, "reliable" }, // IPTOS_RELIABILITY
{ 0x08, "throughput" }, // IPTOS_THROUGHPUT
{ 0x10, "lowdelay" }, // IPTOS_LOWDELAY
} };
};
// set the IPTOS_ value for the specified socket
void tr_netSetTOS(tr_socket_t sock, int tos, tr_address_type type);
+1 -1
View File
@@ -971,7 +971,7 @@ static void sendLtepHandshake(tr_peerMsgsImpl* msgs)
// port number of the other side. Note that there is no need for the
// receiving side of the connection to send this extension message,
// since its port number is already known.
tr_variantDictAddInt(&val, TR_KEY_p, msgs->session->peerPort().host());
tr_variantDictAddInt(&val, TR_KEY_p, msgs->session->advertisedPeerPort().host());
// http://bittorrent.org/beps/bep_0010.html
// An integer, the number of outstanding request messages this
+17 -12
View File
@@ -57,7 +57,7 @@ void tr_natpmp::setCommandTime()
command_time_ = tr_time() + CommandWaitSecs;
}
tr_natpmp::PulseResult tr_natpmp::pulse(tr_port private_port, bool is_enabled)
tr_natpmp::PulseResult tr_natpmp::pulse(tr_port local_port, bool is_enabled)
{
if (is_enabled && state_ == State::Discover)
{
@@ -89,14 +89,19 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port private_port, bool is_enabled)
}
}
if ((state_ == State::Idle || state_ == State::Err) && is_mapped_ && (!is_enabled || private_port_ != private_port))
if ((state_ == State::Idle || state_ == State::Err) && is_mapped_ && (!is_enabled || local_port_ != local_port))
{
state_ = State::SendUnmap;
}
if (state_ == State::SendUnmap && canSendCommand())
{
auto const val = sendnewportmappingrequest(&natpmp_, NATPMP_PROTOCOL_TCP, private_port_.host(), public_port_.host(), 0);
auto const val = sendnewportmappingrequest(
&natpmp_,
NATPMP_PROTOCOL_TCP,
local_port_.host(),
advertised_port_.host(),
0);
logVal("sendnewportmappingrequest", val);
state_ = val < 0 ? State::Err : State::RecvUnmap;
setCommandTime();
@@ -114,10 +119,10 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port private_port, bool is_enabled)
tr_logAddInfo(fmt::format(_("Port {port} is no longer forwarded"), fmt::arg("port", unmapped_port.host())));
if (private_port_ == unmapped_port)
if (local_port_ == unmapped_port)
{
private_port_.clear();
public_port_.clear();
local_port_.clear();
advertised_port_.clear();
state_ = State::Idle;
is_mapped_ = false;
}
@@ -145,8 +150,8 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port private_port, bool is_enabled)
auto const val = sendnewportmappingrequest(
&natpmp_,
NATPMP_PROTOCOL_TCP,
private_port.host(),
private_port.host(),
local_port.host(),
local_port.host(),
LifetimeSecs);
logVal("sendnewportmappingrequest", val);
state_ = val < 0 ? State::Err : State::RecvMap;
@@ -164,9 +169,9 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port private_port, bool is_enabled)
state_ = State::Idle;
is_mapped_ = true;
renew_time_ = tr_time() + (resp.pnu.newportmapping.lifetime / 2);
private_port_ = tr_port::fromHost(resp.pnu.newportmapping.privateport);
public_port_ = tr_port::fromHost(resp.pnu.newportmapping.mappedpublicport);
tr_logAddInfo(fmt::format(_("Port {port} forwarded successfully"), fmt::arg("port", private_port_.host())));
local_port_ = tr_port::fromHost(resp.pnu.newportmapping.privateport);
advertised_port_ = tr_port::fromHost(resp.pnu.newportmapping.mappedpublicport);
tr_logAddInfo(fmt::format(_("Port {port} forwarded successfully"), fmt::arg("port", local_port_.host())));
}
else if (val != NATPMP_TRYAGAIN)
{
@@ -177,7 +182,7 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port private_port, bool is_enabled)
switch (state_)
{
case State::Idle:
return { is_mapped_ ? TR_PORT_MAPPED : TR_PORT_UNMAPPED, public_port_, private_port_ };
return { is_mapped_ ? TR_PORT_MAPPED : TR_PORT_UNMAPPED, local_port_, advertised_port_ };
case State::Discover:
return { TR_PORT_UNMAPPED, {}, {} };
+5 -5
View File
@@ -39,11 +39,11 @@ public:
{
tr_port_forwarding_state state = TR_PORT_ERROR;
tr_port public_port = {};
tr_port private_port = {};
tr_port local_port = {};
tr_port advertised_port = {};
};
PulseResult pulse(tr_port port, bool is_enabled);
PulseResult pulse(tr_port local_port, bool is_enabled);
private:
enum class State
@@ -67,8 +67,8 @@ private:
natpmp_t natpmp_ = {};
tr_port public_port_ = {};
tr_port private_port_ = {};
tr_port local_port_ = {};
tr_port advertised_port_ = {};
time_t renew_time_ = 0;
time_t command_time_ = 0;
+6 -6
View File
@@ -192,20 +192,20 @@ private:
auto const old_state = state();
auto const result = natpmp_->pulse(mediator_.privatePeerPort(), is_enabled);
auto const result = natpmp_->pulse(mediator_.localPeerPort(), is_enabled);
natpmp_state_ = result.state;
if (!std::empty(result.public_port) && !std::empty(result.private_port))
if (!std::empty(result.local_port) && !std::empty(result.advertised_port))
{
mediator_.onPortForwarded(result.public_port, result.private_port);
mediator_.onPortForwarded(result.advertised_port);
tr_logAddInfo(fmt::format(
_("Mapped private port {private_port} to public port {public_port}"),
fmt::arg("public_port", result.public_port.host()),
fmt::arg("private_port", result.private_port.host())));
fmt::arg("private_port", result.local_port.host()),
fmt::arg("public_port", result.advertised_port.host())));
}
upnp_state_ = tr_upnpPulse(
upnp_,
mediator_.privatePeerPort(),
mediator_.localPeerPort(),
is_enabled,
do_check,
mediator_.incomingPeerAddress().readable());
+2 -2
View File
@@ -28,10 +28,10 @@ public:
public:
virtual ~Mediator() = default;
[[nodiscard]] virtual tr_port privatePeerPort() const = 0;
[[nodiscard]] virtual tr_port localPeerPort() const = 0;
[[nodiscard]] virtual tr_address incomingPeerAddress() const = 0;
[[nodiscard]] virtual libtransmission::TimerMaker& timerMaker() = 0;
virtual void onPortForwarded(tr_port public_port, tr_port private_port) = 0;
virtual void onPortForwarded(tr_port advertised_port) = 0;
};
[[nodiscard]] static std::unique_ptr<tr_port_forwarding> create(Mediator&);
+59 -190
View File
@@ -354,7 +354,7 @@ static bool isHostnameAllowed(tr_rpc_server const* server, evhttp_request const*
}
/* If whitelist is disabled, no restrictions. */
if (!server->isHostWhitelistEnabled)
if (!server->is_host_whitelist_enabled_)
{
return true;
}
@@ -382,7 +382,7 @@ static bool isHostnameAllowed(tr_rpc_server const* server, evhttp_request const*
return true;
}
auto const& src = server->hostWhitelist;
auto const& src = server->host_whitelist_;
return std::any_of(
std::begin(src),
std::end(src),
@@ -567,10 +567,10 @@ static char const* tr_rpc_address_to_string(tr_rpc_address const& addr, char* bu
static std::string tr_rpc_address_with_port(tr_rpc_server const* server)
{
auto addr_buf = std::array<char, TrUnixAddrStrLen>{};
tr_rpc_address_to_string(*server->bindAddress, std::data(addr_buf), std::size(addr_buf));
tr_rpc_address_to_string(*server->bind_address_, std::data(addr_buf), std::size(addr_buf));
std::string addr_port_str = std::data(addr_buf);
if (server->bindAddress->type != TR_RPC_AF_UNIX)
if (server->bind_address_->type != TR_RPC_AF_UNIX)
{
addr_port_str.append(":" + std::to_string(server->port().host()));
}
@@ -675,7 +675,7 @@ static void rpc_server_start_retry_cancel(tr_rpc_server* server)
static void startServer(tr_rpc_server* server)
{
if (server->httpd != nullptr)
if (server->httpd)
{
return;
}
@@ -688,7 +688,7 @@ static void startServer(tr_rpc_server* server)
auto const address = server->getBindAddress();
auto const port = server->port();
bool const success = server->bindAddress->type == TR_RPC_AF_UNIX ?
bool const success = server->bind_address_->type == TR_RPC_AF_UNIX ?
bindUnixSocket(base, httpd, address.c_str(), server->socket_mode_) :
(evhttp_bind_socket(httpd, address.c_str(), port.host()) != -1);
@@ -717,7 +717,7 @@ static void startServer(tr_rpc_server* server)
else
{
evhttp_set_gencb(httpd, handle_request, server);
server->httpd = httpd;
server->httpd = std::unique_ptr<evhttp, void (*)(evhttp*)>{ httpd, evhttp_free };
tr_logAddInfo(fmt::format(_("Listening for RPC and Web requests on '{address}'"), fmt::arg("address", addr_port_str)));
}
@@ -731,19 +731,17 @@ static void stopServer(tr_rpc_server* server)
rpc_server_start_retry_cancel(server);
struct evhttp* httpd = server->httpd;
if (httpd == nullptr)
auto& httpd = server->httpd;
if (httpd)
{
return;
}
auto const address = server->getBindAddress();
server->httpd = nullptr;
evhttp_free(httpd);
httpd.reset();
if (server->bindAddress->type == TR_RPC_AF_UNIX)
if (server->bind_address_->type == TR_RPC_AF_UNIX)
{
unlink(address.c_str() + std::size(TrUnixSocketPrefix));
}
@@ -865,7 +863,7 @@ void tr_rpc_server::setPasswordEnabled(bool enabled)
std::string tr_rpc_server::getBindAddress() const
{
auto buf = std::array<char, TrUnixAddrStrLen>{};
return tr_rpc_address_to_string(*this->bindAddress, std::data(buf), std::size(buf));
return tr_rpc_address_to_string(*this->bind_address_, std::data(buf), std::size(buf));
}
void tr_rpc_server::setAntiBruteForceEnabled(bool enabled) noexcept
@@ -882,201 +880,53 @@ void tr_rpc_server::setAntiBruteForceEnabled(bool enabled) noexcept
***** LIFE CYCLE
****/
static void missing_settings_key(tr_quark const q)
{
tr_logAddDebug(fmt::format("Couldn't find settings key '{}'", tr_quark_get_string_view(q)));
}
tr_rpc_server::tr_rpc_server(tr_session* session_in, tr_variant* settings)
: compressor{ libdeflate_alloc_compressor(DeflateLevel), libdeflate_free_compressor }
, web_client_dir_{ tr_getWebClientDir(session_in) }
, bindAddress(std::make_unique<struct tr_rpc_address>())
, bind_address_(std::make_unique<struct tr_rpc_address>())
, session{ session_in }
{
auto i = int64_t{};
auto sv = std::string_view{};
load(settings);
}
auto key = TR_KEY_rpc_enabled;
if (auto val = bool{}; !tr_variantDictFindBool(settings, key, &val))
{
missing_settings_key(key);
void tr_rpc_server::load(tr_variant* src)
{
#define V(key, field, type, default_value, comment) \
if (auto* const child = tr_variantDictFind(src, key); child != nullptr) \
{ \
if (auto val = libtransmission::VariantConverter::load<decltype(field)>(child); val) \
{ \
this->field = *val; \
} \
}
else
RPC_SETTINGS_FIELDS(V);
#undef V
if (!tr_strvEndsWith(url_, '/'))
{
this->is_enabled_ = val;
url_ = fmt::format(FMT_STRING("{:s}/"), url_);
}
key = TR_KEY_rpc_port;
this->host_whitelist_ = parseWhitelist(host_whitelist_str_);
this->setPasswordEnabled(authentication_required_);
this->setWhitelist(whitelist_str_);
this->setUsername(username_);
this->setPassword(salted_password_);
if (!tr_variantDictFindInt(settings, key, &i))
{
missing_settings_key(key);
}
else
{
this->port_.setHost(i);
}
key = TR_KEY_rpc_url;
if (!tr_variantDictFindStrView(settings, key, &sv))
{
missing_settings_key(key);
}
else if (std::empty(sv) || sv.back() != '/')
{
this->url_ = fmt::format(FMT_STRING("{:s}/"), sv);
}
else
{
this->url_ = sv;
}
key = TR_KEY_rpc_whitelist_enabled;
if (auto val = bool{}; !tr_variantDictFindBool(settings, key, &val))
{
missing_settings_key(key);
}
else
{
this->setWhitelistEnabled(val);
}
key = TR_KEY_rpc_host_whitelist_enabled;
if (auto val = bool{}; !tr_variantDictFindBool(settings, key, &val))
{
missing_settings_key(key);
}
else
{
this->isHostWhitelistEnabled = val;
}
key = TR_KEY_rpc_host_whitelist;
if (!tr_variantDictFindStrView(settings, key, &sv))
{
missing_settings_key(key);
}
else if (!std::empty(sv))
{
this->hostWhitelist = parseWhitelist(sv);
}
key = TR_KEY_rpc_authentication_required;
if (auto val = bool{}; !tr_variantDictFindBool(settings, key, &val))
{
missing_settings_key(key);
}
else
{
this->setPasswordEnabled(val);
}
key = TR_KEY_rpc_whitelist;
if (!tr_variantDictFindStrView(settings, key, &sv))
{
missing_settings_key(key);
}
else if (!std::empty(sv))
{
this->setWhitelist(sv);
}
key = TR_KEY_rpc_username;
if (!tr_variantDictFindStrView(settings, key, &sv))
{
missing_settings_key(key);
}
else
{
this->setUsername(sv);
}
key = TR_KEY_rpc_password;
if (!tr_variantDictFindStrView(settings, key, &sv))
{
missing_settings_key(key);
}
else
{
this->setPassword(sv);
}
key = TR_KEY_anti_brute_force_enabled;
if (auto val = bool{}; !tr_variantDictFindBool(settings, key, &val))
{
missing_settings_key(key);
}
else
{
this->setAntiBruteForceEnabled(val);
}
key = TR_KEY_anti_brute_force_threshold;
if (!tr_variantDictFindInt(settings, key, &i))
{
missing_settings_key(key);
}
else
{
this->setAntiBruteForceLimit(static_cast<int>(i));
}
key = TR_KEY_rpc_socket_mode;
bool is_missing_rpc_socket_mode_key = true;
if (tr_variantDictFindStrView(settings, key, &sv))
{
/* Read the socket permission as a string representing an octal number. */
is_missing_rpc_socket_mode_key = false;
i = tr_parseNum<int>(sv, nullptr, 8).value_or(tr_rpc_server::DefaultRpcSocketMode);
}
else if (tr_variantDictFindInt(settings, key, &i))
{
/* Or as a base 10 integer to remain compatible with the old settings format. */
is_missing_rpc_socket_mode_key = false;
}
if (is_missing_rpc_socket_mode_key)
{
missing_settings_key(key);
}
else
{
this->socket_mode_ = static_cast<tr_mode_t>(i);
}
key = TR_KEY_rpc_bind_address;
if (!tr_variantDictFindStrView(settings, key, &sv))
{
missing_settings_key(key);
bindAddress->set_inaddr_any();
}
else if (!tr_rpc_address_from_string(*bindAddress, sv))
if (!tr_rpc_address_from_string(*bind_address_, bind_address_str_))
{
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::format("key", tr_quark_get_string_view(key)),
fmt::format("value", sv)));
bindAddress->set_inaddr_any();
fmt::format("key", tr_quark_get_string_view(TR_KEY_rpc_bind_address)),
fmt::format("value", bind_address_str_)));
bind_address_->set_inaddr_any();
}
if (bindAddress->type == TR_RPC_AF_UNIX)
if (bind_address_->type == TR_RPC_AF_UNIX)
{
this->setWhitelistEnabled(false);
this->isHostWhitelistEnabled = false;
this->is_host_whitelist_enabled_ = false;
}
if (this->isEnabled())
{
auto const rpc_uri = tr_rpc_address_with_port(this) + this->url_;
@@ -1100,6 +950,25 @@ tr_rpc_server::tr_rpc_server(tr_session* session_in, tr_variant* settings)
}
}
void tr_rpc_server::save(tr_variant* tgt) const
{
#define V(key, field, type, default_value, comment) \
tr_variantDictRemove(tgt, key); \
libtransmission::VariantConverter::save<decltype(field)>(tr_variantDictAdd(tgt, key), field);
RPC_SETTINGS_FIELDS(V)
#undef V
}
void tr_rpc_server::defaultSettings(tr_variant* tgt){
#define V(key, field, type, default_value, comment) \
{ \
tr_variantDictRemove(tgt, key); \
libtransmission::VariantConverter::save<decltype(field)>(tr_variantDictAdd(tgt, key), default_value); \
}
RPC_SETTINGS_FIELDS(V)
#undef V
}
tr_rpc_server::~tr_rpc_server()
{
stopServer(this);
+29 -17
View File
@@ -28,6 +28,22 @@ 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::fromHost(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:
@@ -39,6 +55,10 @@ public:
tr_rpc_server& operator=(tr_rpc_server&) = delete;
tr_rpc_server& operator=(tr_rpc_server&&) = delete;
void load(tr_variant* src);
void save(tr_variant* tgt) const;
static void defaultSettings(tr_variant* tgt);
[[nodiscard]] constexpr tr_port port() const noexcept
{
return port_;
@@ -124,31 +144,23 @@ public:
return socket_mode_;
}
std::vector<std::string> hostWhitelist;
#define V(key, name, type, default_value, comment) type name = type{ default_value };
RPC_SETTINGS_FIELDS(V)
#undef V
std::vector<std::string> host_whitelist_;
std::vector<std::string> whitelist_;
std::string const web_client_dir_;
std::string salted_password_;
std::string username_;
std::string whitelist_str_;
std::string url_;
std::unique_ptr<struct tr_rpc_address> bindAddress;
std::unique_ptr<struct tr_rpc_address> bind_address_;
std::unique_ptr<libtransmission::Timer> start_retry_timer;
struct evhttp* httpd = nullptr;
std::unique_ptr<struct evhttp, void (*)(struct evhttp*)> httpd{ nullptr, [](evhttp*) {
} };
tr_session* const session;
int anti_brute_force_limit_ = 0;
int login_attempts_ = 0;
size_t login_attempts_ = 0U;
int start_retry_counter = 0;
static tr_mode_t constexpr DefaultRpcSocketMode = 0750;
tr_mode_t socket_mode_ = DefaultRpcSocketMode;
tr_port port_;
bool is_anti_brute_force_enabled_ = false;
bool is_enabled_ = false;
bool isHostWhitelistEnabled = false;
bool is_password_enabled_ = false;
bool is_whitelist_enabled_ = false;
};
+4 -4
View File
@@ -1449,7 +1449,7 @@ static char const* portTest(
tr_variant* /*args_out*/,
struct tr_rpc_idle_data* idle_data)
{
auto const port = session->peerPort();
auto const port = session->advertisedPeerPort();
auto const url = fmt::format(FMT_STRING("https://portcheck.transmissionbt.com/{:d}"), port.host());
session->fetch({ url, onPortTested, idle_data });
return nullptr;
@@ -1904,12 +1904,12 @@ static char const* sessionSet(
if (tr_variantDictFindInt(args_in, TR_KEY_alt_speed_time_begin, &i))
{
tr_sessionSetAltSpeedBegin(session, static_cast<int>(i));
tr_sessionSetAltSpeedBegin(session, static_cast<size_t>(i));
}
if (tr_variantDictFindInt(args_in, TR_KEY_alt_speed_time_end, &i))
{
tr_sessionSetAltSpeedEnd(session, static_cast<int>(i));
tr_sessionSetAltSpeedEnd(session, static_cast<size_t>(i));
}
if (tr_variantDictFindInt(args_in, TR_KEY_alt_speed_time_day, &i))
@@ -2288,7 +2288,7 @@ static void addSessionField(tr_session const* s, tr_variant* d, tr_quark key)
break;
case TR_KEY_peer_port:
tr_variantDictAddInt(d, key, s->peerPort().host());
tr_variantDictAddInt(d, key, s->advertisedPeerPort().host());
break;
case TR_KEY_peer_port_random_on_start:
+118
View File
@@ -0,0 +1,118 @@
// This file Copyright © 2022 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 <fmt/chrono.h>
#include "transmission.h"
#include "log.h"
#include "session-alt-speeds.h"
#include "variant.h"
#include "utils.h" // for _()
using namespace std::literals;
void tr_session_alt_speeds::load(tr_variant* src)
{
#define V(key, field, type, default_value, comment) \
if (auto* const child = tr_variantDictFind(src, key); child != nullptr) \
{ \
if (auto val = libtransmission::VariantConverter::load<decltype(field)>(child); val) \
{ \
this->field = *val; \
} \
}
ALT_SPEEDS_FIELDS(V);
#undef V
updateScheduler();
}
void tr_session_alt_speeds::save(tr_variant* tgt) const
{
#define V(key, field, type, default_value, comment) \
tr_variantDictRemove(tgt, key); \
libtransmission::VariantConverter::save<decltype(field)>(tr_variantDictAdd(tgt, key), field);
ALT_SPEEDS_FIELDS(V)
#undef V
}
void tr_session_alt_speeds::defaultSettings(tr_variant* tgt)
{
#define V(key, field, type, default_value, comment) \
{ \
type const val = default_value; \
tr_variantDictRemove(tgt, key); \
libtransmission::VariantConverter::save<decltype(field)>(tr_variantDictAdd(tgt, key), val); \
}
ALT_SPEEDS_FIELDS(V)
#undef V
}
/// minutes
void tr_session_alt_speeds::updateMinutes()
{
minutes_.reset();
for (int day = 0; day < 7; ++day)
{
if ((static_cast<tr_sched_day>(use_on_these_weekdays_) & (1 << day)) != 0)
{
auto const begin = minute_begin_;
auto const end = minute_end_ > minute_begin_ ? minute_end_ : minute_end_ + MinutesPerDay;
for (auto i = begin; i < end; ++i)
{
minutes_.set((i + day * MinutesPerDay) % MinutesPerWeek);
}
}
}
}
void tr_session_alt_speeds::updateScheduler()
{
updateMinutes();
scheduler_set_is_active_to_.reset();
checkScheduler();
}
void tr_session_alt_speeds::checkScheduler()
{
if (!isSchedulerEnabled())
{
return;
}
if (auto const active = isActiveMinute(mediator_.time());
!scheduler_set_is_active_to_ || scheduler_set_is_active_to_ != active)
{
tr_logAddInfo(active ? _("Time to turn on turtle mode") : _("Time to turn off turtle mode"));
scheduler_set_is_active_to_ = active;
setActive(active, ChangeReason::Scheduler);
}
}
void tr_session_alt_speeds::setActive(bool active, ChangeReason reason)
{
if (is_active_ != active)
{
is_active_ = active;
mediator_.isActiveChanged(is_active_, reason);
}
}
[[nodiscard]] bool tr_session_alt_speeds::isActiveMinute(time_t time) const noexcept
{
auto const tm = fmt::localtime(time);
size_t minute_of_the_week = tm.tm_wday * MinutesPerDay + tm.tm_hour * MinutesPerHour + tm.tm_min;
if (minute_of_the_week >= MinutesPerWeek) /* leap minutes? */
{
minute_of_the_week = MinutesPerWeek - 1;
}
return minutes_.test(minute_of_the_week);
}
+158
View File
@@ -0,0 +1,158 @@
// This file Copyright © 2022 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.
#ifndef __TRANSMISSION__
#error only libtransmission should #include this header.
#endif
#pragma once
#include <bitset>
#include <ctime> // for time_t
#include <optional>
#include "transmission.h" // for TR_SCHED_ALL
#include "quark.h"
struct tr_variant;
#define ALT_SPEEDS_FIELDS(V) \
V(TR_KEY_alt_speed_up, speed_up_kilobytes_per_second_, size_t, 50U, "") \
V(TR_KEY_alt_speed_down, speed_down_kilobytes_per_second_, size_t, 50U, "") \
V(TR_KEY_alt_speed_time_enabled, scheduler_enabled_, bool, false, "whether alt speeds toggle on and off on schedule") \
V(TR_KEY_alt_speed_time_day, use_on_these_weekdays_, size_t, TR_SCHED_ALL, "days of the week") \
V(TR_KEY_alt_speed_time_begin, minute_begin_, size_t, 540U, "minutes past midnight; 9AM") \
V(TR_KEY_alt_speed_time_end, minute_end_, size_t, 1020U, "minutes past midnight; 5PM")
/** Manages alternate speed limits and a scheduler to auto-toggle them. */
class tr_session_alt_speeds
{
public:
enum class ChangeReason
{
User,
Scheduler
};
class Mediator
{
public:
virtual ~Mediator() noexcept = default;
using ChangeReason = tr_session_alt_speeds::ChangeReason;
virtual void isActiveChanged(bool is_active, ChangeReason reason) = 0;
[[nodiscard]] virtual time_t time() = 0;
};
constexpr explicit tr_session_alt_speeds(Mediator& mediator) noexcept
: mediator_{ mediator }
{
}
void load(tr_variant* src);
void save(tr_variant* tgt) const;
static void defaultSettings(tr_variant* tgt);
[[nodiscard]] constexpr bool isActive() const noexcept
{
return is_active_;
}
void checkScheduler();
void setSchedulerEnabled(bool enabled)
{
scheduler_enabled_ = enabled;
updateScheduler();
}
// return true iff the scheduler will turn alt speeds on/off
[[nodiscard]] constexpr auto isSchedulerEnabled() const noexcept
{
return scheduler_enabled_;
}
void setStartMinute(size_t minute)
{
minute_begin_ = minute;
updateScheduler();
}
[[nodiscard]] constexpr auto startMinute() const noexcept
{
return minute_begin_;
}
void setEndMinute(size_t minute)
{
minute_end_ = minute;
updateScheduler();
}
[[nodiscard]] constexpr auto endMinute() const noexcept
{
return minute_end_;
}
void setWeekdays(tr_sched_day days)
{
use_on_these_weekdays_ = days;
updateScheduler();
}
[[nodiscard]] constexpr tr_sched_day weekdays() const noexcept
{
return static_cast<tr_sched_day>(use_on_these_weekdays_);
}
[[nodiscard]] constexpr auto limitKBps(tr_direction dir) const noexcept
{
return dir == TR_DOWN ? speed_down_kilobytes_per_second_ : speed_up_kilobytes_per_second_;
}
constexpr void setLimitKBps(tr_direction dir, size_t limit) noexcept
{
if (dir == TR_DOWN)
{
speed_down_kilobytes_per_second_ = limit;
}
else
{
speed_up_kilobytes_per_second_ = limit;
}
}
void setActive(bool active, ChangeReason reason);
private:
Mediator& mediator_;
void updateScheduler();
void updateMinutes();
// whether `time` hits in one of the `minutes_` that is true
[[nodiscard]] bool isActiveMinute(time_t time) const noexcept;
static auto constexpr MinutesPerHour = int{ 60 };
static auto constexpr MinutesPerDay = int{ MinutesPerHour * 24 };
static auto constexpr MinutesPerWeek = int{ MinutesPerDay * 7 };
// are alt speeds active right now?
bool is_active_ = false;
// bitfield of all the minutes in a week.
// Each bit's value indicates whether the scheduler wants
// alt speeds on or off at that given minute.
std::bitset<10080> minutes_{};
// recent change that was made by the scheduler
std::optional<bool> scheduler_set_is_active_to_;
#define V(key, name, type, default_value, comment) type name = type{ default_value };
ALT_SPEEDS_FIELDS(V)
#undef V
};
+34
View File
@@ -0,0 +1,34 @@
// This file Copyright © 2022 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 <fmt/format.h>
#include "transmission.h"
#include "session-settings.h"
#include "variant.h"
void tr_session_settings::load(tr_variant* src)
{
#define V(key, field, type, default_value, comment) \
if (auto* const child = tr_variantDictFind(src, key); child != nullptr) \
{ \
if (auto val = libtransmission::VariantConverter::load<decltype(field)>(child); val) \
{ \
this->field = *val; \
} \
}
SESSION_SETTINGS_FIELDS(V);
#undef V
}
void tr_session_settings::save(tr_variant* tgt) const
{
#define V(key, field, type, default_value, comment) \
tr_variantDictRemove(tgt, key); \
libtransmission::VariantConverter::save<decltype(field)>(tr_variantDictAdd(tgt, key), field);
SESSION_SETTINGS_FIELDS(V)
#undef V
}
+92
View File
@@ -0,0 +1,92 @@
// This file Copyright © 2022 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 "transmission.h"
#include "log.h" // for tr_log_level
#include "net.h" // for tr_port, tr_tos_t
#include "quark.h"
struct tr_variant;
#define SESSION_SETTINGS_FIELDS(V) \
V(TR_KEY_announce_ip, announce_ip, std::string, "", "") \
V(TR_KEY_announce_ip_enabled, announce_ip_enabled, bool, false, "") \
V(TR_KEY_bind_address_ipv4, bind_address_ipv4, std::string, "0.0.0.0", "") \
V(TR_KEY_bind_address_ipv6, bind_address_ipv6, std::string, "::", "") \
V(TR_KEY_blocklist_enabled, blocklist_enabled, bool, false, "") \
V(TR_KEY_blocklist_url, blocklist_url, std::string, "http://www.example.com/blocklist", "") \
V(TR_KEY_cache_size_mb, cache_size_mb, size_t, 4U, "") \
V(TR_KEY_default_trackers, default_trackers_str, std::string, "", "") \
V(TR_KEY_dht_enabled, dht_enabled, bool, true, "") \
V(TR_KEY_download_dir, download_dir, std::string, tr_getDefaultDownloadDir(), "") \
V(TR_KEY_download_queue_enabled, download_queue_enabled, bool, true, "") \
V(TR_KEY_download_queue_size, download_queue_size, size_t, 5U, "") \
V(TR_KEY_encryption, encryption_mode, tr_encryption_mode, TR_ENCRYPTION_PREFERRED, "") \
V(TR_KEY_idle_seeding_limit, idle_seeding_limit_minutes, size_t, 30U, "") \
V(TR_KEY_idle_seeding_limit_enabled, idle_seeding_limit_enabled, bool, false, "") \
V(TR_KEY_incomplete_dir, incomplete_dir, std::string, tr_getDefaultDownloadDir(), "") \
V(TR_KEY_incomplete_dir_enabled, incomplete_dir_enabled, bool, false, "") \
V(TR_KEY_lpd_enabled, lpd_enabled, bool, true, "") \
V(TR_KEY_message_level, log_level, tr_log_level, TR_LOG_INFO, "") \
V(TR_KEY_peer_congestion_algorithm, peer_congestion_algorithm, std::string, "", "") \
V(TR_KEY_peer_id_ttl_hours, peer_id_ttl_hours, size_t, 6U, "") \
V(TR_KEY_peer_limit_global, peer_limit_global, size_t, TR_DEFAULT_PEER_LIMIT_GLOBAL, "") \
V(TR_KEY_peer_limit_per_torrent, peer_limit_per_torrent, size_t, TR_DEFAULT_PEER_LIMIT_TORRENT, "") \
V(TR_KEY_peer_port, peer_port, tr_port, tr_port::fromHost(TR_DEFAULT_PEER_PORT), "The local machine's incoming peer port") \
V(TR_KEY_peer_port_random_high, peer_port_random_high, tr_port, tr_port::fromHost(65535), "") \
V(TR_KEY_peer_port_random_low, peer_port_random_low, tr_port, tr_port::fromHost(49152), "") \
V(TR_KEY_peer_port_random_on_start, peer_port_random_on_start, bool, false, "") \
V(TR_KEY_peer_socket_tos, peer_socket_tos, tr_tos_t, 0x04, "") \
V(TR_KEY_pex_enabled, pex_enabled, bool, true, "") \
V(TR_KEY_port_forwarding_enabled, port_forwarding_enabled, bool, true, "") \
V(TR_KEY_preallocation, preallocation_mode, tr_preallocation_mode, TR_PREALLOCATE_SPARSE, "") \
V(TR_KEY_prefetch_enabled, is_prefetch_enabled, bool, true, "") \
V(TR_KEY_queue_stalled_enabled, queue_stalled_enabled, bool, true, "") \
V(TR_KEY_queue_stalled_minutes, queue_stalled_minutes, size_t, 30U, "") \
V(TR_KEY_ratio_limit, ratio_limit, double, 2.0, "") \
V(TR_KEY_ratio_limit_enabled, ratio_limit_enabled, bool, false, "") \
V(TR_KEY_rename_partial_files, is_incomplete_file_naming_enabled, bool, false, "") \
V(TR_KEY_scrape_paused_torrents_enabled, should_scrape_paused_torrents, bool, true, "") \
V(TR_KEY_script_torrent_added_enabled, script_torrent_added_enabled, bool, false, "") \
V(TR_KEY_script_torrent_added_filename, script_torrent_added_filename, std::string, "", "") \
V(TR_KEY_script_torrent_done_enabled, script_torrent_done_enabled, bool, false, "") \
V(TR_KEY_script_torrent_done_filename, script_torrent_done_filename, std::string, "", "") \
V(TR_KEY_script_torrent_done_seeding_enabled, script_torrent_done_seeding_enabled, bool, false, "") \
V(TR_KEY_script_torrent_done_seeding_filename, script_torrent_done_seeding_filename, std::string, "", "") \
V(TR_KEY_seed_queue_enabled, seed_queue_enabled, bool, false, "") \
V(TR_KEY_seed_queue_size, seed_queue_size, size_t, 10U, "") \
V(TR_KEY_speed_limit_down, speed_limit_down, size_t, 100U, "") \
V(TR_KEY_speed_limit_down_enabled, speed_limit_down_enabled, bool, false, "") \
V(TR_KEY_speed_limit_up, speed_limit_up, size_t, 100U, "") \
V(TR_KEY_speed_limit_up_enabled, speed_limit_up_enabled, bool, false, "") \
V(TR_KEY_start_added_torrents, should_start_added_torrents, bool, true, "") \
V(TR_KEY_tcp_enabled, tcp_enabled, bool, true, "") \
V(TR_KEY_trash_original_torrent_files, should_delete_source_torrents, bool, false, "") \
V(TR_KEY_umask, umask, tr_mode_t, 022, "") \
V(TR_KEY_upload_slots_per_torrent, upload_slots_per_torrent, size_t, 8U, "") \
V(TR_KEY_utp_enabled, utp_enabled, bool, true, "")
struct tr_session_settings
{
tr_session_settings() = default;
explicit tr_session_settings(tr_variant* src)
{
load(src);
}
void load(tr_variant* src);
void save(tr_variant* tgt) const;
#define V(key, name, type, default_value, comment) type name = type{ default_value };
SESSION_SETTINGS_FIELDS(V)
#undef V
};
+226 -805
View File
File diff suppressed because it is too large Load Diff
+215 -234
View File
@@ -36,20 +36,15 @@
#include "open-files.h"
#include "port-forwarding.h"
#include "quark.h"
#include "session-alt-speeds.h"
#include "session-id.h"
#include "session-settings.h"
#include "stats.h"
#include "torrents.h"
#include "tr-lpd.h"
#include "verify.h"
#include "web.h"
enum tr_auto_switch_state_t
{
TR_AUTO_SWITCH_UNUSED,
TR_AUTO_SWITCH_ON,
TR_AUTO_SWITCH_OFF,
};
tr_peer_id_t tr_peerIdInit();
struct event_base;
@@ -61,6 +56,7 @@ class tr_web;
struct BlocklistFile;
struct struct_utp_context;
struct tr_announcer;
struct tr_variant;
namespace libtransmission
{
@@ -76,47 +72,6 @@ class SessionTest;
} // namespace libtransmission::test
struct tr_turtle_info
{
/* TR_UP and TR_DOWN speed limits */
std::array<tr_bytes_per_second_t, 2> speedLimit_Bps = {};
/* is turtle mode on right now? */
bool isEnabled = false;
/* does turtle mode turn itself on and off at given times? */
bool isClockEnabled = false;
/* when clock mode is on, minutes after midnight to turn on turtle mode.
* Valid range: 0..<1440 */
int beginMinute = 0;
/* when clock mode is on, minutes after midnight to turn off turtle mode.
* Valid range: 0..<1440 */
int endMinute = 0;
/* only use clock mode on these days of the week */
tr_sched_day days = {};
/* called when isEnabled changes */
tr_altSpeedFunc callback = nullptr;
/* the callback's user_data argument */
void* callbackUserData = nullptr;
/* the callback's changedByUser argument.
* indicates whether the change came from the user or from the clock. */
bool changedByUser = false;
/* bitfield of all the minutes in a week.
* Each bit's value indicates whether the scheduler wants turtle
* limits on or off at that given minute in the week. */
tr_bitfield minutes{ 10080 };
/* recent action that was done by turtle's automatic switch */
tr_auto_switch_state_t autoTurtleState = TR_AUTO_SWITCH_UNUSED;
};
/** @brief handle to an active libtransmission session */
struct tr_session
{
@@ -142,6 +97,24 @@ private:
tr_socket_t socket_ = TR_BAD_SOCKET;
};
class AltSpeedMediator final : public tr_session_alt_speeds::Mediator
{
public:
explicit AltSpeedMediator(tr_session& session)
: session_{ session }
{
}
void isActiveChanged(bool is_active, tr_session_alt_speeds::ChangeReason reason) override;
[[nodiscard]] time_t time() override;
~AltSpeedMediator() noexcept override = default;
private:
tr_session& session_;
};
class AnnouncerUdpMediator final : public tr_announcer_udp::Mediator
{
public:
@@ -189,9 +162,9 @@ private:
return session_.bind_ipv4_.addr_;
}
[[nodiscard]] tr_port privatePeerPort() const override
[[nodiscard]] tr_port localPeerPort() const override
{
return session_.private_peer_port_;
return session_.localPeerPort();
}
[[nodiscard]] libtransmission::TimerMaker& timerMaker() override
@@ -199,10 +172,9 @@ private:
return session_.timerMaker();
}
void onPortForwarded(tr_port public_port, tr_port private_port) override
void onPortForwarded(tr_port public_port) override
{
session_.public_peer_port_ = public_port;
session_.private_peer_port_ = private_port;
session_.advertised_peer_port_ = public_port;
}
private:
@@ -240,7 +212,7 @@ private:
[[nodiscard]] tr_port port() const override
{
return session_.peerPort();
return session_.advertisedPeerPort();
}
[[nodiscard]] bool allowsLPD() const override
@@ -299,7 +271,7 @@ private:
};
public:
explicit tr_session(std::string_view config_dir);
explicit tr_session(std::string_view config_dir, tr_variant* settings_dict = nullptr);
[[nodiscard]] std::string_view sessionId() const noexcept
{
@@ -350,12 +322,12 @@ public:
[[nodiscard]] constexpr auto const& downloadDir() const noexcept
{
return download_dir_;
return settings_.download_dir;
}
void setDownloadDir(std::string_view dir)
{
download_dir_ = dir;
settings_.download_dir = dir;
}
// default trackers
@@ -363,7 +335,7 @@ public:
[[nodiscard]] constexpr auto const& defaultTrackersStr() const noexcept
{
return default_trackers_str_;
return settings_.default_trackers_str;
}
[[nodiscard]] constexpr auto const& defaultTrackers() const noexcept
@@ -377,63 +349,63 @@ public:
[[nodiscard]] constexpr auto const& incompleteDir() const noexcept
{
return incomplete_dir_;
return settings_.incomplete_dir;
}
void setIncompleteDir(std::string_view dir)
{
incomplete_dir_ = dir;
settings_.incomplete_dir = dir;
}
[[nodiscard]] constexpr auto useIncompleteDir() const noexcept
{
return incomplete_dir_enabled_;
return settings_.incomplete_dir_enabled;
}
constexpr void useIncompleteDir(bool enabled) noexcept
{
incomplete_dir_enabled_ = enabled;
settings_.incomplete_dir_enabled = enabled;
}
// scripts
constexpr void useScript(TrScript i, bool enabled)
{
scripts_enabled_[i] = enabled;
scriptEnabledFlag(i) = enabled;
}
[[nodiscard]] constexpr auto useScript(TrScript i) const
[[nodiscard]] constexpr bool useScript(TrScript i) const
{
return scripts_enabled_[i];
return const_cast<tr_session*>(this)->scriptEnabledFlag(i);
}
void setScript(TrScript i, std::string_view path)
{
scripts_[i] = path;
scriptFilename(i) = path;
}
[[nodiscard]] constexpr auto const& script(TrScript i) const
{
return scripts_[i];
return const_cast<tr_session*>(this)->scriptFilename(i);
}
// blocklist
[[nodiscard]] constexpr auto useBlocklist() const noexcept
{
return blocklist_enabled_;
return settings_.blocklist_enabled;
}
void useBlocklist(bool enabled);
[[nodiscard]] constexpr auto const& blocklistUrl() const noexcept
{
return blocklist_url_;
return settings_.blocklist_url;
}
void setBlocklistUrl(std::string_view url)
{
blocklist_url_ = url;
settings_.blocklist_url = url;
}
// RPC
@@ -458,22 +430,32 @@ public:
[[nodiscard]] constexpr auto const& peerCongestionAlgorithm() const noexcept
{
return peer_congestion_algorithm_;
return settings_.peer_congestion_algorithm;
}
void setPeerCongestionAlgorithm(std::string_view algorithm)
{
peer_congestion_algorithm_ = algorithm;
settings_.peer_congestion_algorithm = algorithm;
}
void setSocketTOS(tr_socket_t sock, tr_address_type type) const
{
tr_netSetTOS(sock, peer_socket_tos_, type);
tr_netSetTOS(sock, settings_.peer_socket_tos, type);
}
[[nodiscard]] constexpr auto peerLimit() const noexcept
{
return settings_.peer_limit_global;
}
[[nodiscard]] constexpr auto peerLimitPerTorrent() const noexcept
{
return settings_.peer_limit_per_torrent;
}
[[nodiscard]] constexpr bool incPeerCount() noexcept
{
if (this->peer_count_ >= this->peer_limit_)
if (this->peer_count_ >= this->peerLimit())
{
return false;
}
@@ -508,22 +490,22 @@ public:
[[nodiscard]] constexpr std::string const& announceIP() const noexcept
{
return announce_ip_;
return settings_.announce_ip;
}
void setAnnounceIP(std::string_view ip)
{
announce_ip_ = ip;
settings_.announce_ip = ip;
}
[[nodiscard]] constexpr bool useAnnounceIP() const noexcept
{
return announce_ip_enabled_;
return settings_.announce_ip_enabled;
}
constexpr void useAnnounceIP(bool enabled) noexcept
{
announce_ip_enabled_ = enabled;
settings_.announce_ip_enabled = enabled;
}
// callbacks
@@ -627,50 +609,53 @@ public:
session_stats_.addFileCreated();
}
[[nodiscard]] constexpr tr_port peerPort() const noexcept
// The incoming peer port that's been opened on the local machine
// that Transmission is running on.
[[nodiscard]] constexpr tr_port localPeerPort() const noexcept
{
return public_peer_port_;
return settings_.peer_port;
}
// The incoming peer port that's been opened on the public-facing
// device. This is usually the same as localPeerPort() but can differ,
// e.g. if the public device is a router that chose to use a different
// port than the one requested by Transmission.
[[nodiscard]] constexpr tr_port advertisedPeerPort() const noexcept
{
return advertised_peer_port_;
}
[[nodiscard]] constexpr tr_port udpPort() const noexcept
{
// uses the same port number that's used for incoming TCP connections
return public_peer_port_;
// Always use the same port number that's used for incoming TCP connections.
// This simplifies port forwarding and reduces the chance of confusion,
// since incoming UDP and TCP connections will use the same port number
return advertisedPeerPort();
}
[[nodiscard]] constexpr auto queueEnabled(tr_direction dir) const noexcept
{
return queue_enabled_[dir];
return dir == TR_DOWN ? settings_.download_queue_enabled : settings_.seed_queue_enabled;
}
[[nodiscard]] constexpr auto queueSize(tr_direction dir) const noexcept
{
return queue_size_[dir];
return dir == TR_DOWN ? settings_.download_queue_size : settings_.seed_queue_size;
}
[[nodiscard]] constexpr auto queueStalledEnabled() const noexcept
{
return queue_stalled_enabled_;
return settings_.queue_stalled_enabled;
}
[[nodiscard]] constexpr auto queueStalledMinutes() const noexcept
{
return queue_stalled_minutes_;
}
[[nodiscard]] constexpr auto peerLimit() const noexcept
{
return peer_limit_;
}
[[nodiscard]] constexpr auto peerLimitPerTorrent() const noexcept
{
return peer_limit_per_torrent_;
return settings_.queue_stalled_minutes;
}
[[nodiscard]] constexpr auto uploadSlotsPerTorrent() const noexcept
{
return upload_slots_per_torrent_;
return settings_.upload_slots_per_torrent;
}
[[nodiscard]] constexpr auto isClosing() const noexcept
@@ -685,64 +670,64 @@ public:
[[nodiscard]] constexpr auto encryptionMode() const noexcept
{
return encryption_mode_;
return settings_.encryption_mode;
}
[[nodiscard]] constexpr auto preallocationMode() const noexcept
{
return preallocation_mode_;
return settings_.preallocation_mode;
}
[[nodiscard]] constexpr auto shouldScrapePausedTorrents() const noexcept
{
return should_scrape_paused_torrents_;
return settings_.should_scrape_paused_torrents;
}
[[nodiscard]] constexpr auto shouldPauseAddedTorrents() const noexcept
{
return should_pause_added_torrents_;
return !settings_.should_start_added_torrents;
}
[[nodiscard]] constexpr auto shouldDeleteSource() const noexcept
{
return should_delete_source_torrents_;
return settings_.should_delete_source_torrents;
}
[[nodiscard]] constexpr auto allowsDHT() const noexcept
{
return is_dht_enabled_;
return settings_.dht_enabled;
}
[[nodiscard]] constexpr bool allowsLPD() const noexcept
{
return is_lpd_enabled_;
return settings_.lpd_enabled;
}
[[nodiscard]] constexpr auto allowsPEX() const noexcept
{
return is_pex_enabled_;
return settings_.pex_enabled;
}
[[nodiscard]] constexpr auto allowsTCP() const noexcept
{
return is_tcp_enabled_;
return settings_.tcp_enabled;
}
[[nodiscard]] bool allowsUTP() const noexcept;
[[nodiscard]] constexpr auto allowsPrefetch() const noexcept
{
return is_prefetch_enabled_;
return settings_.is_prefetch_enabled;
}
[[nodiscard]] constexpr auto isIdleLimited() const noexcept
{
return is_idle_limited_;
return settings_.idle_seeding_limit_enabled;
}
[[nodiscard]] constexpr auto idleLimitMinutes() const noexcept
{
return idle_limit_minutes_;
return settings_.idle_seeding_limit_minutes;
}
[[nodiscard]] std::vector<tr_torrent*> getAllTorrents() const
@@ -776,14 +761,14 @@ public:
[[nodiscard]] PublicAddressResult publicAddress(tr_address_type type) const noexcept;
[[nodiscard]] constexpr auto speedLimitBps(tr_direction dir) const noexcept
[[nodiscard]] constexpr auto speedLimitKBps(tr_direction dir) const noexcept
{
return speed_limit_Bps_[dir];
return dir == TR_DOWN ? settings_.speed_limit_down : settings_.speed_limit_up;
}
[[nodiscard]] constexpr auto isSpeedLimited(tr_direction dir) const noexcept
{
return speed_limit_enabled_[dir];
return dir == TR_DOWN ? settings_.speed_limit_down_enabled : settings_.speed_limit_up_enabled;
}
[[nodiscard]] auto pieceSpeedBps(tr_direction dir) const noexcept
@@ -795,27 +780,27 @@ public:
[[nodiscard]] constexpr auto isIncompleteFileNamingEnabled() const noexcept
{
return is_incomplete_file_naming_enabled_;
return settings_.is_incomplete_file_naming_enabled;
}
[[nodiscard]] constexpr auto isPortRandom() const noexcept
{
return is_port_random_;
return settings_.peer_port_random_on_start;
}
[[nodiscard]] constexpr auto isRatioLimited() const noexcept
{
return is_ratio_limited_;
return settings_.ratio_limit_enabled;
}
[[nodiscard]] constexpr auto desiredRatio() const noexcept
{
return desired_ratio_;
return settings_.ratio_limit;
}
[[nodiscard]] constexpr auto peerIdTTLHours() const noexcept
{
return peer_id_ttl_hours_;
return settings_.peer_id_ttl_hours;
}
void verifyRemove(tr_torrent* tor)
@@ -857,6 +842,36 @@ public:
}
private:
constexpr bool& scriptEnabledFlag(TrScript i)
{
if (i == TR_SCRIPT_ON_TORRENT_ADDED)
{
return settings_.script_torrent_added_enabled;
}
if (i == TR_SCRIPT_ON_TORRENT_DONE)
{
return settings_.script_torrent_done_enabled;
}
return settings_.script_torrent_done_seeding_enabled;
}
constexpr std::string& scriptFilename(TrScript i)
{
if (i == TR_SCRIPT_ON_TORRENT_ADDED)
{
return settings_.script_torrent_added_filename;
}
if (i == TR_SCRIPT_ON_TORRENT_DONE)
{
return settings_.script_torrent_done_filename;
}
return settings_.script_torrent_done_seeding_filename;
}
[[nodiscard]] tr_port randomPort() const;
void setPeerPort(tr_port port);
@@ -869,7 +884,8 @@ private:
struct init_data;
void initImpl(init_data&);
void setImpl(init_data&);
void setSettings(tr_variant* settings_dict, bool force);
void closeImplStart();
void closeImplWaitForIdleUdp();
void closeImplFinish();
@@ -884,13 +900,21 @@ private:
friend bool tr_sessionIsPortForwardingEnabled(tr_session const* session);
friend bool tr_sessionIsRPCEnabled(tr_session const* session);
friend bool tr_sessionIsRPCPasswordEnabled(tr_session const* session);
friend bool tr_sessionUsesAltSpeed(tr_session const* session);
friend bool tr_sessionUsesAltSpeedTime(tr_session const* session);
friend char const* tr_sessionGetRPCPassword(tr_session const* session);
friend char const* tr_sessionGetRPCUsername(tr_session const* session);
friend char const* tr_sessionGetRPCWhitelist(tr_session const* session);
friend int tr_sessionGetAntiBruteForceThreshold(tr_session const* session);
friend size_t tr_blocklistGetRuleCount(tr_session const* session);
friend size_t tr_blocklistSetContent(tr_session* session, char const* content_filename);
friend size_t tr_sessionGetAltSpeedBegin(tr_session const* session);
friend size_t tr_sessionGetAltSpeedEnd(tr_session const* session);
friend size_t tr_sessionGetCacheLimit_MB(tr_session const* session);
friend tr_kilobytes_per_second_t tr_sessionGetAltSpeed_KBps(tr_session const* session, tr_direction dir);
friend tr_kilobytes_per_second_t tr_sessionGetSpeedLimit_KBps(tr_session const* session, tr_direction dir);
friend tr_port_forwarding_state tr_sessionGetPortForwarding(tr_session const* session);
friend tr_sched_day tr_sessionGetAltSpeedDay(tr_session const* session);
friend tr_session* tr_sessionInit(char const* config_dir, bool message_queueing_enabled, tr_variant* client_settings);
friend uint16_t tr_sessionGetPeerPort(tr_session const* session);
friend uint16_t tr_sessionGetRPCPort(tr_session const* session);
@@ -900,8 +924,14 @@ private:
friend void tr_sessionLimitSpeed(tr_session* session, tr_direction dir, bool limited);
friend void tr_sessionReloadBlocklists(tr_session* session);
friend void tr_sessionSet(tr_session* session, tr_variant* settings);
friend void tr_sessionSetAltSpeedBegin(tr_session* session, size_t minutes_since_midnight);
friend void tr_sessionSetAltSpeedDay(tr_session* session, tr_sched_day days);
friend void tr_sessionSetAltSpeedEnd(tr_session* session, size_t minutes_since_midnight);
friend void tr_sessionSetAltSpeedFunc(tr_session* session, tr_altSpeedFunc func, void* user_data);
friend void tr_sessionSetAltSpeed_KBps(tr_session* session, tr_direction dir, tr_bytes_per_second_t limit);
friend void tr_sessionSetAntiBruteForceEnabled(tr_session* session, bool is_enabled);
friend void tr_sessionSetAntiBruteForceThreshold(tr_session* session, int max_bad_requests);
friend void tr_sessionSetCacheLimit_MB(tr_session* session, size_t mb);
friend void tr_sessionSetDHTEnabled(tr_session* session, bool enabled);
friend void tr_sessionSetDeleteSource(tr_session* session, bool delete_source);
friend void tr_sessionSetEncryption(tr_session* session, tr_encryption_mode mode);
@@ -916,8 +946,8 @@ private:
friend void tr_sessionSetPeerPortRandomOnStart(tr_session* session, bool random);
friend void tr_sessionSetPexEnabled(tr_session* session, bool enabled);
friend void tr_sessionSetPortForwardingEnabled(tr_session* session, bool enabled);
friend void tr_sessionSetQueueEnabled(tr_session* session, tr_direction dir, bool do_limit_simultaneous_seed_torrents);
friend void tr_sessionSetQueueSize(tr_session* session, tr_direction dir, size_t max_simultaneous_seed_torrents);
friend void tr_sessionSetQueueEnabled(tr_session* session, tr_direction dir, bool do_limit_simultaneous_torrents);
friend void tr_sessionSetQueueSize(tr_session* session, tr_direction dir, size_t max_simultaneous_torrents);
friend void tr_sessionSetQueueStalledEnabled(tr_session* session, bool is_enabled);
friend void tr_sessionSetQueueStalledMinutes(tr_session* session, int minutes);
friend void tr_sessionSetRPCCallback(tr_session* session, tr_rpc_func func, void* user_data);
@@ -928,12 +958,14 @@ private:
friend void tr_sessionSetRPCUsername(tr_session* session, char const* username);
friend void tr_sessionSetRatioLimit(tr_session* session, double desired_ratio);
friend void tr_sessionSetRatioLimited(tr_session* session, bool is_limited);
friend void tr_sessionSetSpeedLimit_Bps(tr_session* session, tr_direction dir, tr_bytes_per_second_t bytes_per_second);
friend void tr_sessionSetSpeedLimit_KBps(tr_session* session, tr_direction dir, tr_kilobytes_per_second_t limit);
friend void tr_sessionSetUTPEnabled(tr_session* session, bool enabled);
/// constexpr fields
friend void tr_sessionUseAltSpeed(tr_session* session, bool enabled);
friend void tr_sessionUseAltSpeedTime(tr_session* session, bool enabled);
public:
/// constexpr fields
static constexpr std::array<std::tuple<tr_quark, tr_quark, TrScript>, 3> Scripts{
{ { TR_KEY_script_torrent_added_enabled, TR_KEY_script_torrent_added_filename, TR_SCRIPT_ON_TORRENT_ADDED },
{ TR_KEY_script_torrent_done_enabled, TR_KEY_script_torrent_done_filename, TR_SCRIPT_ON_TORRENT_DONE },
@@ -942,23 +974,29 @@ public:
TR_SCRIPT_ON_TORRENT_DONE_SEEDING } }
};
private:
/// const fields
private:
std::string const config_dir_;
std::string const resume_dir_;
std::string const torrent_dir_;
std::string const blocklist_dir_;
std::unique_ptr<event_base, void (*)(event_base*)> const event_base_;
// depends on: event_base_
// depends-on: event_base_
std::unique_ptr<libtransmission::TimerMaker> const timer_maker_;
// depends on: event_base_
// depends-on: event_base_
std::unique_ptr<libtransmission::Dns> const dns_;
/// static fields
static std::recursive_mutex session_mutex_;
/// trivial type fields
tr_session_settings settings_;
std::optional<tr_address> external_ip_;
queue_start_callback_t queue_start_callback_ = nullptr;
@@ -979,91 +1017,25 @@ private:
tr_rpc_func rpc_func_ = nullptr;
void* rpc_func_user_data_ = nullptr;
float desired_ratio_ = 2.0F;
tr_altSpeedFunc alt_speed_active_changed_func_ = nullptr;
void* alt_speed_active_changed_func_user_data_ = nullptr;
std::array<tr_bytes_per_second_t, 2> speed_limit_Bps_ = { 0U, 0U };
std::array<bool, 2> speed_limit_enabled_ = { false, false };
std::array<bool, 2> queue_enabled_ = { false, false };
std::array<size_t, 2> queue_size_ = { 0, 0 };
uint32_t umask_ = 022;
// One of <netinet/ip.h>'s IPTOS_ values.
// See tr_netTos*() in libtransmission/net.h for more info
int peer_socket_tos_ = *tr_netTosFromName(TR_DEFAULT_PEER_SOCKET_TOS_STR);
int queue_stalled_minutes_ = 0;
tr_encryption_mode encryption_mode_ = TR_ENCRYPTION_PREFERRED;
tr_preallocation_mode preallocation_mode_ = TR_PREALLOCATE_SPARSE;
// The open port on the local machine for incoming peer requests
tr_port private_peer_port_;
// The open port on the public device for incoming peer requests.
// This is usually the same as private_peer_port but can differ
// if the public device is a router and it decides to use a different
// The incoming peer port that's been opened on the public-facing
// device. This is usually the same as localPeerPort() but can differ,
// e.g. if the public device is a router that chose to use a different
// port than the one requested by Transmission.
tr_port public_peer_port_;
tr_port random_port_low_;
tr_port random_port_high_;
tr_port advertised_peer_port_;
uint16_t peer_count_ = 0;
uint16_t peer_limit_ = 200;
uint16_t peer_limit_per_torrent_ = 50;
uint16_t idle_limit_minutes_ = 0;
uint16_t upload_slots_per_torrent_ = 8;
uint8_t peer_id_ttl_hours_ = 6;
bool is_closing_ = false;
bool is_closed_ = false;
bool is_utp_enabled_ = false;
bool is_pex_enabled_ = false;
bool is_dht_enabled_ = false;
bool is_lpd_enabled_ = false;
bool is_tcp_enabled_ = true;
bool is_idle_limited_ = false;
bool is_prefetch_enabled_ = false;
bool is_ratio_limited_ = false;
bool queue_stalled_enabled_ = false;
bool is_port_random_ = false;
bool should_pause_added_torrents_ = false;
bool should_delete_source_torrents_ = false;
bool should_scrape_paused_torrents_ = false;
bool is_incomplete_file_naming_enabled_ = false;
std::array<bool, TR_SCRIPT_N_TYPES> scripts_enabled_ = {};
bool blocklist_enabled_ = false;
bool incomplete_dir_enabled_ = false;
bool announce_ip_enabled_ = false;
/// fields that aren't trivial,
/// but are self-contained / have no interdependencies
tr_stats session_stats_{ config_dir_, time(nullptr) };
std::array<std::string, TR_SCRIPT_N_TYPES> scripts_;
std::string download_dir_;
std::string incomplete_dir_;
std::string blocklist_url_;
std::string default_trackers_str_;
std::string peer_congestion_algorithm_;
std::string announce_ip_;
tr_announce_list default_trackers_;
tr_session_id session_id_;
@@ -1071,64 +1043,73 @@ private:
tr_bindinfo bind_ipv4_ = tr_bindinfo{ tr_inaddr_any };
tr_bindinfo bind_ipv6_ = tr_bindinfo{ tr_in6addr_any };
public:
struct tr_turtle_info turtle;
/// other fields
private:
static std::recursive_mutex session_mutex_;
public:
std::vector<std::unique_ptr<BlocklistFile>> blocklists_;
struct tr_event_handle* events = nullptr;
// depends-on: announcer_udp_
// FIXME(ckerr): circular dependency udp_core -> announcer_udp -> announcer_udp_mediator -> udp_core
std::unique_ptr<tr_udp_core> udp_core_;
private:
struct tr_peerMgr* peer_mgr_ = nullptr;
std::unique_ptr<tr_port_forwarding> port_forwarding_;
tr_torrents torrents_;
tr_open_files open_files_;
public:
std::unique_ptr<Cache> cache = std::make_unique<Cache>(torrents_, 1024 * 1024 * 2);
private:
std::unique_ptr<tr_web> web_;
std::unique_ptr<tr_lpd> lpd_;
public:
struct tr_announcer* announcer = nullptr;
// monitors the "global pool" speeds
tr_bandwidth top_bandwidth_;
private:
// depends-on: top_bandwidth_
std::vector<std::pair<tr_interned_string, std::unique_ptr<tr_bandwidth>>> bandwidth_groups_;
// depends-on: settings_, timer_maker_
PortForwardingMediator port_forwarding_mediator_{ *this };
std::unique_ptr<tr_port_forwarding> port_forwarding_ = tr_port_forwarding::create(port_forwarding_mediator_);
// depends-on: events, top_bandwidth_
AltSpeedMediator alt_speed_mediator_{ *this };
tr_session_alt_speeds alt_speeds_{ alt_speed_mediator_ };
tr_open_files open_files_;
// depends-on: open_files_
tr_torrents torrents_;
// depends-on: timer_maker_, top_bandwidth_, torrents_
std::unique_ptr<struct tr_peerMgr, void (*)(struct tr_peerMgr*)> peer_mgr_;
public:
// depends-on: settings_, open_files_, torrents_
std::unique_ptr<Cache> cache = std::make_unique<Cache>(torrents_, 1024 * 1024 * 2);
private:
// depends-on: settings_, events, torrents_
WebMediator web_mediator_{ this };
std::unique_ptr<tr_web> web_ = tr_web::create(this->web_mediator_);
// depends-on: peer_mgr_, torrents_
LpdMediator lpd_mediator_{ *this };
// depends-on: lpd_mediator_
std::unique_ptr<tr_lpd> lpd_;
// depends-on: dns_, udp_core_
AnnouncerUdpMediator announcer_udp_mediator_{ *this };
public:
// depends-on: announcer_udp_mediator_
std::unique_ptr<tr_announcer_udp> announcer_udp_ = tr_announcer_udp::create(announcer_udp_mediator_);
// depends-on: settings_, torrents_, announcer_udp_
struct tr_announcer* announcer = nullptr;
private:
std::vector<std::pair<tr_interned_string, std::unique_ptr<tr_bandwidth>>> bandwidth_groups_;
std::vector<std::unique_ptr<BlocklistFile>> blocklists_;
// depends-on: event_base_, timer_maker_, settings_, torrents_
std::unique_ptr<tr_rpc_server> rpc_server_;
PortForwardingMediator port_forwarding_mediator_{ *this };
WebMediator web_mediator_{ this };
LpdMediator lpd_mediator_{ *this };
// depends-on: alt_speeds_, udp_core_, torrents_
std::unique_ptr<libtransmission::Timer> now_timer_;
// depends-on: torrents_
std::unique_ptr<libtransmission::Timer> save_timer_;
std::unique_ptr<tr_verify_worker> verifier_ = std::make_unique<tr_verify_worker>();
+4 -4
View File
@@ -753,9 +753,9 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
if ((loaded & tr_resume::Speedlimit) == 0)
{
tr_torrentUseSpeedLimit(tor, TR_UP, false);
tor->setSpeedLimitBps(TR_UP, tor->session->speedLimitBps(TR_UP));
tor->setSpeedLimitBps(TR_UP, tr_toSpeedBytes(tor->session->speedLimitKBps(TR_UP)));
tr_torrentUseSpeedLimit(tor, TR_DOWN, false);
tor->setSpeedLimitBps(TR_DOWN, tor->session->speedLimitBps(TR_DOWN));
tor->setSpeedLimitBps(TR_DOWN, tr_toSpeedBytes(tor->session->speedLimitKBps(TR_DOWN)));
tr_torrentUseSessionLimits(tor, true);
}
@@ -1000,9 +1000,9 @@ static time_t torrentGetIdleSecs(tr_torrent const* tor, tr_torrent_activity acti
-1;
}
static inline bool tr_torrentIsStalled(tr_torrent const* tor, time_t idle_secs)
static inline bool tr_torrentIsStalled(tr_torrent const* tor, size_t idle_secs)
{
return tor->session->queueStalledEnabled() && idle_secs > tor->session->queueStalledMinutes() * 60;
return tor->session->queueStalledEnabled() && idle_secs > tor->session->queueStalledMinutes() * 60U;
}
tr_stat const* tr_torrentStat(tr_torrent* tor)
+1 -1
View File
@@ -725,7 +725,7 @@ void tr_dhtUpkeep()
auto lock = impl.session->unique_lock();
auto const now = tr_time();
auto const incoming_peer_port = impl.session->peerPort();
auto const incoming_peer_port = impl.session->advertisedPeerPort();
for (auto* const tor : impl.session->torrents())
{
+10 -9
View File
@@ -131,8 +131,10 @@ size_t tr_getDefaultDownloadDirToBuf(char* buf, size_t buflen);
#define TR_DEFAULT_RPC_PORT 9091
#define TR_DEFAULT_RPC_URL_STR "/transmission/"
#define TR_DEFAULT_PEER_PORT_STR "51413"
#define TR_DEFAULT_PEER_PORT 51413
#define TR_DEFAULT_PEER_SOCKET_TOS_STR "le"
#define TR_DEFAULT_PEER_LIMIT_GLOBAL_STR "200"
#define TR_DEFAULT_PEER_LIMIT_GLOBAL 200
#define TR_DEFAULT_PEER_LIMIT_TORRENT_STR "50"
#define TR_DEFAULT_PEER_LIMIT_TORRENT 50
@@ -504,8 +506,7 @@ enum tr_direction
**** Primary session speed limits
***/
void tr_sessionSetSpeedLimit_Bps(tr_session*, tr_direction, tr_bytes_per_second_t bytes_per_second);
void tr_sessionSetSpeedLimit_KBps(tr_session*, tr_direction, tr_kilobytes_per_second_t kilo_per_second);
void tr_sessionSetSpeedLimit_KBps(tr_session*, tr_direction, tr_kilobytes_per_second_t limit);
tr_kilobytes_per_second_t tr_sessionGetSpeedLimit_KBps(tr_session const*, tr_direction);
void tr_sessionLimitSpeed(tr_session*, tr_direction, bool);
@@ -515,7 +516,7 @@ bool tr_sessionIsSpeedLimited(tr_session const*, tr_direction);
**** Alternative speed limits that are used during scheduled times
***/
void tr_sessionSetAltSpeed_KBps(tr_session*, tr_direction, tr_kilobytes_per_second_t kilo_per_second);
void tr_sessionSetAltSpeed_KBps(tr_session*, tr_direction, tr_kilobytes_per_second_t limit);
tr_kilobytes_per_second_t tr_sessionGetAltSpeed_KBps(tr_session const*, tr_direction);
void tr_sessionUseAltSpeed(tr_session*, bool);
@@ -524,11 +525,11 @@ bool tr_sessionUsesAltSpeed(tr_session const*);
void tr_sessionUseAltSpeedTime(tr_session*, bool);
bool tr_sessionUsesAltSpeedTime(tr_session const*);
void tr_sessionSetAltSpeedBegin(tr_session*, int minutes_since_midnight);
int tr_sessionGetAltSpeedBegin(tr_session const*);
void tr_sessionSetAltSpeedBegin(tr_session*, size_t minutes_since_midnight);
size_t tr_sessionGetAltSpeedBegin(tr_session const*);
void tr_sessionSetAltSpeedEnd(tr_session*, int minutes_since_midnight);
int tr_sessionGetAltSpeedEnd(tr_session const*);
void tr_sessionSetAltSpeedEnd(tr_session*, size_t minutes_since_midnight);
size_t tr_sessionGetAltSpeedEnd(tr_session const*);
enum tr_sched_day
{
@@ -641,13 +642,13 @@ void tr_torrentsQueueMoveBottom(tr_torrent* const* torrents, size_t torrent_coun
**/
/** @brief Set the number of torrents allowed to download (if direction is TR_DOWN) or seed (if direction is TR_UP) at the same time */
void tr_sessionSetQueueSize(tr_session*, tr_direction, size_t max_simultaneous_seed_torrents);
void tr_sessionSetQueueSize(tr_session*, tr_direction, size_t max_simultaneous_torrents);
/** @brief Return the number of torrents allowed to download (if direction is TR_DOWN) or seed (if direction is TR_UP) at the same time */
size_t tr_sessionGetQueueSize(tr_session const*, tr_direction);
/** @brief Set whether or not to limit how many torrents can download (TR_DOWN) or seed (TR_UP) at the same time */
void tr_sessionSetQueueEnabled(tr_session*, tr_direction, bool do_limit_simultaneous_seed_torrents);
void tr_sessionSetQueueEnabled(tr_session*, tr_direction, bool do_limit_simultaneous_torrents);
/** @brief Return true if we're limiting how many torrents can concurrently download (TR_DOWN) or seed (TR_UP) at the same time */
bool tr_sessionGetQueueEnabled(tr_session const*, tr_direction);
+350
View File
@@ -0,0 +1,350 @@
// This file Copyright © 2022 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 <fmt/format.h>
#include "transmission.h"
#include "log.h" // for tr_log_level
#include "net.h" // for tr_port
#include "utils.h" // for tr_strvStrip(), tr_strlower()
#include "variant.h"
using namespace std::literals;
namespace libtransmission
{
template<>
std::optional<bool> VariantConverter::load<bool>(tr_variant* src)
{
if (auto val = bool{}; tr_variantGetBool(src, &val))
{
return val;
}
return {};
}
template<>
void VariantConverter::save<bool>(tr_variant* tgt, bool const& val)
{
tr_variantInitBool(tgt, val);
}
///
template<>
std::optional<double> VariantConverter::load<double>(tr_variant* src)
{
if (auto val = double{}; tr_variantGetReal(src, &val))
{
return val;
}
return {};
}
template<>
void VariantConverter::save<double>(tr_variant* tgt, double const& val)
{
tr_variantInitReal(tgt, val);
}
///
namespace EncryptionHelpers
{
// clang-format off
static auto constexpr Keys = std::array<std::pair<std::string_view, tr_encryption_mode>, 3>{{
{ "required", TR_ENCRYPTION_REQUIRED },
{ "preferred", TR_ENCRYPTION_PREFERRED },
{ "allowed", TR_CLEAR_PREFERRED }
}};
// clang-format on
} // namespace EncryptionHelpers
template<>
std::optional<tr_encryption_mode> VariantConverter::load<tr_encryption_mode>(tr_variant* src)
{
using namespace EncryptionHelpers;
if (auto val = std::string_view{}; tr_variantGetStrView(src, &val))
{
auto const needle = tr_strlower(tr_strvStrip(val));
for (auto const& [key, encryption] : Keys)
{
if (key == needle)
{
return encryption;
}
}
}
if (auto val = int64_t{}; tr_variantGetInt(src, &val))
{
for (auto const& [key, encryption] : Keys)
{
if (encryption == val)
{
return encryption;
}
}
}
return {};
}
template<>
void VariantConverter::save<tr_encryption_mode>(tr_variant* tgt, tr_encryption_mode const& val)
{
using namespace EncryptionHelpers;
for (auto const& [key, value] : Keys)
{
if (value == val)
{
tr_variantInitStrView(tgt, key);
return;
}
}
}
///
namespace LogLevelHelpers
{
// clang-format off
static auto constexpr Keys = std::array<std::pair<std::string_view, tr_log_level>, 7>{ {
{ "critical", TR_LOG_CRITICAL },
{ "debug", TR_LOG_DEBUG },
{ "error", TR_LOG_ERROR },
{ "info", TR_LOG_INFO },
{ "off", TR_LOG_OFF },
{ "trace", TR_LOG_TRACE },
{ "warn", TR_LOG_WARN },
}};
// clang-format on
} // namespace LogLevelHelpers
template<>
std::optional<tr_log_level> VariantConverter::load<tr_log_level>(tr_variant* src)
{
using namespace LogLevelHelpers;
if (auto val = std::string_view{}; tr_variantGetStrView(src, &val))
{
auto const needle = tr_strlower(tr_strvStrip(val));
for (auto const& [name, log_level] : Keys)
{
if (needle == name)
{
return log_level;
}
}
}
if (auto val = int64_t{}; tr_variantGetInt(src, &val))
{
for (auto const& [name, log_level] : Keys)
{
if (log_level == val)
{
return log_level;
}
}
}
return {};
}
template<>
void VariantConverter::save<tr_log_level>(tr_variant* tgt, tr_log_level const& val)
{
using namespace LogLevelHelpers;
for (auto const& [key, value] : Keys)
{
if (value == val)
{
tr_variantInitStrView(tgt, key);
return;
}
}
}
///
template<>
std::optional<tr_mode_t> VariantConverter::load<tr_mode_t>(tr_variant* src)
{
if (auto val = std::string_view{}; tr_variantGetStrView(src, &val))
{
if (auto const mode = tr_parseNum<uint32_t>(val, nullptr, 8); mode)
{
return static_cast<tr_mode_t>(*mode);
}
}
if (auto val = int64_t{}; tr_variantGetInt(src, &val))
{
return static_cast<tr_mode_t>(val);
}
return {};
}
template<>
void VariantConverter::save<tr_mode_t>(tr_variant* tgt, tr_mode_t const& val)
{
tr_variantInitStr(tgt, fmt::format("{:#03o}", val));
}
///
template<>
std::optional<tr_port> VariantConverter::load<tr_port>(tr_variant* src)
{
if (auto val = int64_t{}; tr_variantGetInt(src, &val))
{
return tr_port::fromHost(val);
}
return {};
}
template<>
void VariantConverter::save<tr_port>(tr_variant* tgt, tr_port const& val)
{
tr_variantInitInt(tgt, val.host());
}
///
namespace PreallocationModeHelpers
{
// clang-format off
static auto constexpr Keys = std::array<std::pair<std::string_view, tr_preallocation_mode>, 5>{{
{ "off", TR_PREALLOCATE_NONE },
{ "none", TR_PREALLOCATE_NONE },
{ "fast", TR_PREALLOCATE_SPARSE },
{ "sparse", TR_PREALLOCATE_SPARSE },
{ "full", TR_PREALLOCATE_FULL },
}};
// clang-format on
} // namespace PreallocationModeHelpers
template<>
std::optional<tr_preallocation_mode> VariantConverter::load<tr_preallocation_mode>(tr_variant* src)
{
using namespace PreallocationModeHelpers;
if (auto val = std::string_view{}; tr_variantGetStrView(src, &val))
{
auto const needle = tr_strlower(tr_strvStrip(val));
for (auto const& [name, value] : Keys)
{
if (name == needle)
{
return value;
}
}
}
if (auto val = int64_t{}; tr_variantGetInt(src, &val))
{
for (auto const& [name, value] : Keys)
{
if (value == val)
{
return value;
}
}
}
return {};
}
template<>
void VariantConverter::save<tr_preallocation_mode>(tr_variant* tgt, tr_preallocation_mode const& val)
{
using namespace PreallocationModeHelpers;
for (auto const& [key, value] : Keys)
{
if (value == val)
{
tr_variantInitStrView(tgt, key);
return;
}
}
}
///
template<>
std::optional<size_t> VariantConverter::load<size_t>(tr_variant* src)
{
if (auto val = int64_t{}; tr_variantGetInt(src, &val))
{
return static_cast<size_t>(val);
}
return {};
}
template<>
void VariantConverter::save<size_t>(tr_variant* tgt, size_t const& val)
{
tr_variantInitInt(tgt, val);
}
///
template<>
std::optional<std::string> VariantConverter::load<std::string>(tr_variant* src)
{
if (auto val = std::string_view{}; tr_variantGetStrView(src, &val))
{
return std::string{ val };
}
return {};
}
template<>
void VariantConverter::save<std::string>(tr_variant* tgt, std::string const& val)
{
tr_variantInitStr(tgt, val);
}
///
template<>
std::optional<tr_tos_t> VariantConverter::load<tr_tos_t>(tr_variant* src)
{
if (auto val = std::string_view{}; tr_variantGetStrView(src, &val))
{
return tr_tos_t::fromString(val);
}
if (auto val = int64_t{}; tr_variantGetInt(src, &val))
{
return tr_tos_t{ static_cast<int>(val) };
}
return {};
}
template<>
void VariantConverter::save<tr_tos_t>(tr_variant* tgt, tr_tos_t const& val)
{
tr_variantInitStr(tgt, val.toString());
}
} // namespace libtransmission
+15
View File
@@ -281,4 +281,19 @@ bool tr_variantDictFindRaw(tr_variant* dict, tr_quark const key, std::byte const
/* this is only quasi-supported. don't rely on it too heavily outside of libT */
void tr_variantMergeDicts(tr_variant* dict_target, tr_variant const* dict_source);
namespace libtransmission
{
struct VariantConverter
{
public:
template<typename T>
static std::optional<T> load(tr_variant* src);
template<typename T>
static void save(tr_variant* tgt, T const& val);
};
} // namespace libtransmission
/* @} */
+2
View File
@@ -35,6 +35,8 @@ add_executable(libtransmission-test
rename-test.cc
rpc-test.cc
session-test.cc
session-alt-speeds-test.cc
settings-test.cc
strbuf-test.cc
subprocess-test-script.cmd
subprocess-test.cc
@@ -0,0 +1,156 @@
// This file Copyright (C) 2022 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 <ctime>
#include <vector>
#include <fmt/chrono.h>
#include "transmission.h"
#include "session.h"
#include "session-id.h"
#include "version.h"
#include "test-fixtures.h"
using namespace std::literals;
class SessionAltSpeedsTest : public ::testing::Test
{
protected:
using ChangeReason = tr_session_alt_speeds::ChangeReason;
class MockMediator final : public tr_session_alt_speeds::Mediator
{
public:
~MockMediator() override = default;
void isActiveChanged(bool is_active, ChangeReason reason) override
{
changelog_.emplace_back(is_active, reason, time());
}
[[nodiscard]] time_t time() override
{
return current_time_;
}
time_t current_time_ = 0;
struct Change
{
Change() = default;
Change(bool is_active_in, ChangeReason reason_in, time_t timestamp_in)
: is_active{ is_active_in }
, reason{ reason_in }
, timestamp{ timestamp_in }
{
}
bool is_active = false;
ChangeReason reason = ChangeReason::User;
time_t timestamp = 0;
};
std::vector<Change> changelog_;
};
static auto constexpr ArbitraryTimestamp1 = time_t{ 666 };
static auto midnightSundayMorning()
{
// new year's, Sun Jan 1 2023
auto tmdate = tm{};
tmdate.tm_sec = 0;
tmdate.tm_min = 0;
tmdate.tm_hour = 0;
tmdate.tm_mday = 1;
tmdate.tm_mon = 0;
tmdate.tm_year = 2023 - 1900;
tmdate.tm_wday = 0; // Sunday
tmdate.tm_yday = 0;
tmdate.tm_isdst = 0;
return mktime(&tmdate);
}
};
namespace libtransmission::test
{
TEST_F(SessionAltSpeedsTest, canInstantiate)
{
auto mediator = MockMediator{};
auto alt_speeds = tr_session_alt_speeds{ mediator };
EXPECT_FALSE(alt_speeds.isActive());
}
TEST_F(SessionAltSpeedsTest, canActivate)
{
static auto constexpr Now = ArbitraryTimestamp1;
auto mediator = MockMediator{};
mediator.current_time_ = Now;
auto alt_speeds = tr_session_alt_speeds{ mediator };
auto const changed_value = !alt_speeds.isActive();
EXPECT_EQ(0U, std::size(mediator.changelog_));
static auto constexpr Reason = ChangeReason::User;
alt_speeds.setActive(changed_value, Reason);
EXPECT_EQ(changed_value, alt_speeds.isActive());
ASSERT_EQ(1U, std::size(mediator.changelog_));
EXPECT_EQ(changed_value, mediator.changelog_[0].is_active);
EXPECT_EQ(Reason, mediator.changelog_[0].reason);
EXPECT_EQ(Now, mediator.changelog_[0].timestamp);
}
TEST_F(SessionAltSpeedsTest, canSchedule)
{
auto mediator = MockMediator{};
auto now = midnightSundayMorning(); // midnight
mediator.current_time_ = now;
auto alt_speeds = tr_session_alt_speeds{ mediator };
alt_speeds.setStartMinute(60); // start at 1AM
alt_speeds.setEndMinute(120); // end at 2AM
alt_speeds.setWeekdays(TR_SCHED_ALL); // every day
alt_speeds.setSchedulerEnabled(true);
auto n_changes = std::size(mediator.changelog_);
EXPECT_EQ(0U, n_changes);
// Confirm that walking up to the threshold, but not crossing it, does not enable
now += std::chrono::duration_cast<std::chrono::seconds>(59min).count();
mediator.current_time_ = now;
alt_speeds.checkScheduler();
EXPECT_EQ(n_changes, std::size(mediator.changelog_));
// Confirm that crossin the threshold does enable
now += std::chrono::duration_cast<std::chrono::seconds>(1min).count();
mediator.current_time_ = now;
alt_speeds.checkScheduler();
ASSERT_EQ(n_changes + 1, std::size(mediator.changelog_));
EXPECT_EQ(true, mediator.changelog_[n_changes].is_active);
EXPECT_EQ(ChangeReason::Scheduler, mediator.changelog_[n_changes].reason);
EXPECT_EQ(now, mediator.changelog_[n_changes].timestamp);
++n_changes;
// Confirm that walking up to the threshold, but not crossing it, does not disable
now += std::chrono::duration_cast<std::chrono::seconds>(59min).count();
mediator.current_time_ = now;
alt_speeds.checkScheduler();
EXPECT_EQ(n_changes, std::size(mediator.changelog_));
// Confirm that crossin the threshold does disable
now += std::chrono::duration_cast<std::chrono::seconds>(1min).count();
mediator.current_time_ = now;
alt_speeds.checkScheduler();
ASSERT_EQ(n_changes + 1, std::size(mediator.changelog_));
EXPECT_EQ(false, mediator.changelog_[n_changes].is_active);
EXPECT_EQ(ChangeReason::Scheduler, mediator.changelog_[n_changes].reason);
EXPECT_EQ(now, mediator.changelog_[n_changes].timestamp);
}
} // namespace libtransmission::test
+74 -7
View File
@@ -4,8 +4,10 @@
// License text can be found in the licenses/ folder.
#include "transmission.h"
#include "session.h"
#include "session-alt-speeds.h"
#include "session-id.h"
#include "session.h"
#include "version.h"
#include "test-fixtures.h"
@@ -20,10 +22,7 @@
using namespace std::literals;
namespace libtransmission
{
namespace test
namespace libtransmission::test
{
TEST_F(SessionTest, propertiesApi)
@@ -277,6 +276,74 @@ TEST_F(SessionTest, sessionId)
EXPECT_FALSE(tr_session_id::isLocal(session_id_str_1));
}
} // namespace test
TEST_F(SessionTest, getDefaultSettingsIncludesSubmodules)
{
auto settings = tr_variant{};
tr_variantInitDict(&settings, 0);
tr_sessionGetDefaultSettings(&settings);
} // namespace libtransmission
// Choose a setting from each of [tr_session, tr_session_alt_speeds, tr_rpc_server] to test all of them.
// These are all `false` by default
for (auto const& key : { TR_KEY_peer_port_random_on_start, TR_KEY_alt_speed_time_enabled, TR_KEY_rpc_enabled })
{
auto flag = bool{};
EXPECT_TRUE(tr_variantDictFindBool(&settings, key, &flag));
EXPECT_FALSE(flag);
}
tr_variantClear(&settings);
}
TEST_F(SessionTest, honorsSettings)
{
// Baseline: confirm that these settings are disabled by default
EXPECT_FALSE(session_->isPortRandom());
EXPECT_FALSE(tr_sessionUsesAltSpeedTime(session_));
EXPECT_FALSE(tr_sessionIsRPCEnabled(session_));
// Choose a setting from each of [tr_session, tr_session_alt_speeds, tr_rpc_server] to test all of them.
// These are all `false` by default
auto settings = tr_variant{};
tr_variantInitDict(&settings, 0);
tr_sessionGetDefaultSettings(&settings);
for (auto const& key : { TR_KEY_peer_port_random_on_start, TR_KEY_alt_speed_time_enabled, TR_KEY_rpc_enabled })
{
tr_variantDictRemove(&settings, key);
tr_variantDictAddBool(&settings, key, true);
}
auto* session = tr_sessionInit(sandboxDir().data(), false, &settings);
tr_variantClear(&settings);
// confirm that these settings were enabled
EXPECT_TRUE(session->isPortRandom());
EXPECT_TRUE(tr_sessionUsesAltSpeedTime(session));
EXPECT_TRUE(tr_sessionIsRPCEnabled(session));
tr_sessionClose(session);
}
TEST_F(SessionTest, savesSettings)
{
// Baseline: confirm that these settings are disabled by default
EXPECT_FALSE(session_->isPortRandom());
EXPECT_FALSE(tr_sessionUsesAltSpeedTime(session_));
EXPECT_FALSE(tr_sessionIsRPCEnabled(session_));
tr_sessionSetPeerPortRandomOnStart(session_, true);
tr_sessionUseAltSpeedTime(session_, true);
tr_sessionSetRPCEnabled(session_, true);
// Choose a setting from each of [tr_session, tr_session_alt_speeds, tr_rpc_server] to test all of them.
auto settings = tr_variant{};
tr_variantInitDict(&settings, 0);
tr_sessionGetSettings(session_, &settings);
for (auto const& key : { TR_KEY_peer_port_random_on_start, TR_KEY_alt_speed_time_enabled, TR_KEY_rpc_enabled })
{
auto flag = bool{};
EXPECT_TRUE(tr_variantDictFindBool(&settings, key, &flag));
EXPECT_TRUE(flag);
}
tr_variantClear(&settings);
}
} // namespace libtransmission::test
+408
View File
@@ -0,0 +1,408 @@
// This file Copyright (C) 2022 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 "transmission.h"
#include "session-settings.h"
#include "test-fixtures.h"
using namespace std::literals;
class SettingsTest : public ::testing::Test
{
protected:
using SessionSettings = tr_session_settings;
};
TEST_F(SettingsTest, canInstantiate)
{
auto settings = tr_session_settings{};
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.save(&dict);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadBools)
{
static auto constexpr Key = TR_KEY_seed_queue_enabled;
auto settings = tr_session_settings{};
auto const expected_value = !settings.seed_queue_enabled;
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddBool(&dict, Key, expected_value);
settings.load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(expected_value, settings.seed_queue_enabled);
}
TEST_F(SettingsTest, canSaveBools)
{
static auto constexpr Key = TR_KEY_seed_queue_enabled;
auto settings = tr_session_settings{};
auto const expected_value = !settings.seed_queue_enabled;
settings.seed_queue_enabled = expected_value;
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.save(&dict);
auto val = bool{};
EXPECT_TRUE(tr_variantDictFindBool(&dict, Key, &val));
EXPECT_EQ(expected_value, val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadDoubles)
{
static auto constexpr Key = TR_KEY_ratio_limit;
auto settings = tr_session_settings{};
auto const expected_value = settings.ratio_limit + 1.0;
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddReal(&dict, Key, expected_value);
settings.load(&dict);
EXPECT_NEAR(expected_value, settings.ratio_limit, 0.001);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canSaveDoubles)
{
static auto constexpr Key = TR_KEY_seed_queue_enabled;
auto settings = tr_session_settings{};
auto const default_value = settings.seed_queue_enabled;
auto const expected_value = !default_value;
settings.seed_queue_enabled = expected_value;
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.save(&dict);
auto val = bool{};
EXPECT_TRUE(tr_variantDictFindBool(&dict, Key, &val));
EXPECT_EQ(expected_value, val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadEncryptionMode)
{
static auto constexpr Key = TR_KEY_encryption;
static auto constexpr ExpectedValue = TR_ENCRYPTION_REQUIRED;
auto settings = std::make_unique<tr_session_settings>();
ASSERT_NE(ExpectedValue, settings->encryption_mode);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddInt(&dict, Key, ExpectedValue);
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings->encryption_mode);
settings = std::make_unique<tr_session_settings>();
tr_variantInitDict(&dict, 1);
tr_variantDictAddStrView(&dict, Key, "required");
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings->encryption_mode);
}
TEST_F(SettingsTest, canSaveEncryptionMode)
{
static auto constexpr Key = TR_KEY_encryption;
static auto constexpr ExpectedValue = TR_ENCRYPTION_REQUIRED;
auto settings = tr_session_settings{};
EXPECT_NE(ExpectedValue, settings.seed_queue_enabled);
settings.encryption_mode = ExpectedValue;
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.save(&dict);
auto val = std::string_view{};
EXPECT_TRUE(tr_variantDictFindStrView(&dict, Key, &val));
EXPECT_EQ("required"sv, val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadLogLevel)
{
static auto constexpr Key = TR_KEY_message_level;
auto settings = std::make_unique<tr_session_settings>();
auto const default_value = settings->log_level;
auto constexpr ExpectedValue = TR_LOG_DEBUG;
ASSERT_NE(ExpectedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddInt(&dict, Key, ExpectedValue);
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings->log_level);
settings = std::make_unique<tr_session_settings>();
tr_variantInitDict(&dict, 1);
tr_variantDictAddStrView(&dict, Key, "debug");
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings->log_level);
}
TEST_F(SettingsTest, canSaveLogLevel)
{
static auto constexpr Key = TR_KEY_message_level;
auto settings = tr_session_settings{};
auto const default_value = settings.log_level;
auto constexpr ExpectedValue = TR_LOG_DEBUG;
ASSERT_NE(ExpectedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.log_level = ExpectedValue;
settings.save(&dict);
auto val = std::string_view{};
EXPECT_TRUE(tr_variantDictFindStrView(&dict, Key, &val));
EXPECT_EQ("debug", val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadMode)
{
static auto constexpr Key = TR_KEY_umask;
auto settings = std::make_unique<tr_session_settings>();
auto const default_value = settings->umask;
auto constexpr ExpectedValue = tr_mode_t{ 0777 };
ASSERT_NE(ExpectedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddInt(&dict, Key, ExpectedValue);
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings->umask);
settings = std::make_unique<tr_session_settings>();
tr_variantInitDict(&dict, 1);
tr_variantDictAddStrView(&dict, Key, "0777");
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings->umask);
}
TEST_F(SettingsTest, canSaveMode)
{
static auto constexpr Key = TR_KEY_umask;
auto settings = tr_session_settings{};
auto const default_value = settings.log_level;
auto constexpr ExpectedValue = tr_mode_t{ 0777 };
ASSERT_NE(ExpectedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.umask = ExpectedValue;
settings.save(&dict);
auto val = std::string_view{};
EXPECT_TRUE(tr_variantDictFindStrView(&dict, Key, &val));
EXPECT_EQ("0777", val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadPort)
{
static auto constexpr Key = TR_KEY_peer_port;
auto settings = tr_session_settings{};
auto const default_value = settings.peer_port;
auto constexpr ExpectedValue = tr_port::fromHost(8080);
ASSERT_NE(ExpectedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddInt(&dict, Key, ExpectedValue.host());
settings.load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings.peer_port);
}
TEST_F(SettingsTest, canSavePort)
{
static auto constexpr Key = TR_KEY_peer_port;
auto settings = tr_session_settings{};
auto const default_value = settings.peer_port;
auto constexpr ExpectedValue = tr_port::fromHost(8080);
ASSERT_NE(ExpectedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.peer_port = ExpectedValue;
settings.save(&dict);
auto val = int64_t{};
EXPECT_TRUE(tr_variantDictFindInt(&dict, Key, &val));
EXPECT_EQ(ExpectedValue.host(), val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadPreallocation)
{
static auto constexpr Key = TR_KEY_preallocation;
auto settings = std::make_unique<tr_session_settings>();
auto const default_value = settings->preallocation_mode;
auto constexpr ExpectedValue = TR_PREALLOCATE_FULL;
ASSERT_NE(ExpectedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddInt(&dict, Key, ExpectedValue);
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings->preallocation_mode);
settings = std::make_unique<tr_session_settings>();
tr_variantInitDict(&dict, 1);
tr_variantDictAddStrView(&dict, Key, "full");
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ExpectedValue, settings->preallocation_mode);
}
TEST_F(SettingsTest, canSavePreallocation)
{
static auto constexpr Key = TR_KEY_preallocation;
auto settings = tr_session_settings{};
auto const default_value = settings.preallocation_mode;
auto constexpr ExpectedValue = TR_PREALLOCATE_FULL;
ASSERT_NE(ExpectedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.preallocation_mode = ExpectedValue;
settings.save(&dict);
auto val = std::string_view{};
EXPECT_TRUE(tr_variantDictFindStrView(&dict, Key, &val));
EXPECT_EQ("full", val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadSizeT)
{
static auto constexpr Key = TR_KEY_queue_stalled_minutes;
auto settings = tr_session_settings{};
auto const expected_value = settings.queue_stalled_minutes + 5U;
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddInt(&dict, Key, expected_value);
settings.load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(expected_value, settings.queue_stalled_minutes);
}
TEST_F(SettingsTest, canSaveSizeT)
{
static auto constexpr Key = TR_KEY_queue_stalled_minutes;
auto settings = tr_session_settings{};
auto const expected_value = settings.queue_stalled_minutes + 5U;
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.queue_stalled_minutes = expected_value;
settings.save(&dict);
auto val = int64_t{};
EXPECT_TRUE(tr_variantDictFindInt(&dict, Key, &val));
EXPECT_EQ(expected_value, val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadString)
{
static auto constexpr Key = TR_KEY_bind_address_ipv4;
static auto constexpr ChangedValue = std::string_view{ "127.0.0.1" };
auto settings = tr_session_settings{};
EXPECT_NE(ChangedValue, tr_session_settings{}.bind_address_ipv4);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddStrView(&dict, Key, ChangedValue);
settings.load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ChangedValue, settings.bind_address_ipv4);
}
TEST_F(SettingsTest, canSaveString)
{
static auto constexpr Key = TR_KEY_bind_address_ipv4;
static auto constexpr ChangedValue = std::string_view{ "127.0.0.1" };
auto settings = tr_session_settings{};
EXPECT_NE(ChangedValue, tr_session_settings{}.bind_address_ipv4);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.bind_address_ipv4 = ChangedValue;
settings.save(&dict);
auto val = std::string_view{};
EXPECT_TRUE(tr_variantDictFindStrView(&dict, Key, &val));
EXPECT_EQ(ChangedValue, val);
tr_variantClear(&dict);
}
TEST_F(SettingsTest, canLoadTos)
{
static auto constexpr Key = TR_KEY_peer_socket_tos;
static auto constexpr ChangedValue = tr_tos_t{ 0x20 };
auto settings = std::make_unique<tr_session_settings>();
auto const default_value = settings->peer_socket_tos;
ASSERT_NE(ChangedValue, default_value);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 1);
tr_variantDictAddInt(&dict, Key, 0x20);
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ChangedValue, settings->peer_socket_tos);
settings = std::make_unique<tr_session_settings>();
tr_variantInitDict(&dict, 1);
tr_variantDictAddStrView(&dict, Key, "cs1");
settings->load(&dict);
tr_variantClear(&dict);
EXPECT_EQ(ChangedValue, settings->peer_socket_tos);
}
TEST_F(SettingsTest, canSaveTos)
{
static auto constexpr Key = TR_KEY_peer_socket_tos;
static auto constexpr ChangedValue = tr_tos_t{ 0x20 };
auto settings = tr_session_settings{};
ASSERT_NE(ChangedValue, settings.peer_socket_tos);
auto dict = tr_variant{};
tr_variantInitDict(&dict, 100);
settings.peer_socket_tos = tr_tos_t(0x20);
settings.save(&dict);
auto val = std::string_view{};
EXPECT_TRUE(tr_variantDictFindStrView(&dict, Key, &val));
EXPECT_EQ(ChangedValue.toString(), val);
tr_variantClear(&dict);
}