mirror of
https://github.com/transmission/transmission.git
synced 2026-05-14 12:40:33 +01:00
refactor: decouple session settings from the session class (#4053)
This commit is contained in:
@@ -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 */,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, {}, {} };
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+215
-234
@@ -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>();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
/* @} */
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user