test: add platform tests (#3514)
* test: add tr_getDefaultDownloadDir() tests this also indirectly tests xdg and homedir * test: add PlatformTest.defaultConfigDirEnv * test: add PlatformTest.defaultConfigDirXdgConfig test: add PlatformTest.defaultConfigDirXdgConfigHome * test: add PlatformTest.webClientDirEnvClutch test: add PlatformTest.webClientDirEnvTr test: add PlatformTest.webClientDirXdgDataHome * fixup! test: add PlatformTest.webClientDirEnvClutch fix: win32 breakage
This commit is contained in:
parent
9bf2918ad0
commit
445aad56a0
|
@ -202,7 +202,6 @@ static char const* getConfigDir(int argc, char const** argv)
|
|||
int tr_main(int argc, char* argv[])
|
||||
{
|
||||
tr_variant settings;
|
||||
char const* configDir;
|
||||
|
||||
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);
|
||||
tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr);
|
||||
|
@ -219,7 +218,7 @@ int tr_main(int argc, char* argv[])
|
|||
|
||||
/* load the defaults from config file + libtransmission defaults */
|
||||
tr_variantInitDict(&settings, 0);
|
||||
configDir = getConfigDir(argc, (char const**)argv);
|
||||
char const* const configDir = getConfigDir(argc, (char const**)argv);
|
||||
tr_sessionLoadSettings(&settings, configDir, MyConfigName);
|
||||
|
||||
/* the command line overrides defaults */
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <glibmm/i18n.h>
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
|
||||
#include <libtransmission/utils.h>
|
||||
#include <libtransmission/variant.h>
|
||||
|
||||
#include "Prefs.h"
|
||||
|
@ -47,7 +49,9 @@ static void tr_prefs_init_defaults(tr_variant* d)
|
|||
|
||||
if (dir.empty())
|
||||
{
|
||||
dir = tr_getDefaultDownloadDir();
|
||||
auto* const tmp = tr_getDefaultDownloadDir();
|
||||
dir = tmp;
|
||||
tr_free(tmp);
|
||||
}
|
||||
|
||||
tr_variantDictReserve(d, 31);
|
||||
|
|
|
@ -94,9 +94,11 @@ int main(int argc, char** argv)
|
|||
tr_formatter_speed_init(speed_K, _(speed_K_str), _(speed_M_str), _(speed_G_str), _(speed_T_str));
|
||||
|
||||
/* set up the config dir */
|
||||
if (config_dir.empty())
|
||||
if (std::empty(config_dir))
|
||||
{
|
||||
config_dir = tr_getDefaultConfigDir(AppConfigDirName);
|
||||
auto* const default_config_dir = tr_getDefaultConfigDir(AppConfigDirName);
|
||||
config_dir = default_config_dir;
|
||||
tr_free(default_config_dir);
|
||||
}
|
||||
|
||||
gtr_pref_init(config_dir);
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __HAIKU__
|
||||
|
@ -51,65 +51,59 @@ using namespace std::literals;
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
static char* win32_get_known_folder_ex(REFKNOWNFOLDERID folder_id, DWORD flags)
|
||||
static std::string win32_get_known_folder_ex(REFKNOWNFOLDERID folder_id, DWORD flags)
|
||||
{
|
||||
char* ret = nullptr;
|
||||
PWSTR path;
|
||||
|
||||
if (SHGetKnownFolderPath(folder_id, flags | KF_FLAG_DONT_UNEXPAND, nullptr, &path) == S_OK)
|
||||
{
|
||||
ret = tr_win32_native_to_utf8(path, -1);
|
||||
auto* utf8_cstr = tr_win32_native_to_utf8(path, -1);
|
||||
auto ret = std::string{ utf8_cstr };
|
||||
tr_free(utf8_cstr);
|
||||
CoTaskMemFree(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return {};
|
||||
}
|
||||
|
||||
static char* win32_get_known_folder(REFKNOWNFOLDERID folder_id)
|
||||
static auto win32_get_known_folder(REFKNOWNFOLDERID folder_id)
|
||||
{
|
||||
return win32_get_known_folder_ex(folder_id, KF_FLAG_DONT_VERIFY);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static char const* getHomeDir()
|
||||
static std::string getHomeDir()
|
||||
{
|
||||
static char const* home = nullptr;
|
||||
|
||||
if (home == nullptr)
|
||||
if (auto* const dir = tr_env_get_string("HOME", nullptr); dir != nullptr)
|
||||
{
|
||||
home = tr_env_get_string("HOME", nullptr);
|
||||
auto ret = std::string{ dir };
|
||||
tr_free(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
if (home == nullptr)
|
||||
if (auto dir = win32_get_known_folder(FOLDERID_Profile); !std::empty(dir))
|
||||
{
|
||||
home = win32_get_known_folder(FOLDERID_Profile);
|
||||
return dir;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (home == nullptr)
|
||||
struct passwd pwent;
|
||||
struct passwd* pw = nullptr;
|
||||
char buf[4096];
|
||||
getpwuid_r(getuid(), &pwent, buf, sizeof buf, &pw);
|
||||
if (pw != nullptr)
|
||||
{
|
||||
struct passwd pwent;
|
||||
struct passwd* pw = nullptr;
|
||||
char buf[4096];
|
||||
getpwuid_r(getuid(), &pwent, buf, sizeof buf, &pw);
|
||||
if (pw != nullptr)
|
||||
{
|
||||
home = tr_strdup(pw->pw_dir);
|
||||
}
|
||||
return pw->pw_dir;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (home == nullptr)
|
||||
{
|
||||
home = tr_strdup("");
|
||||
}
|
||||
|
||||
return home;
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::string xdgConfigHome()
|
||||
|
@ -151,46 +145,38 @@ char const* tr_getTorrentDir(tr_session const* session)
|
|||
return session->torrent_dir.c_str();
|
||||
}
|
||||
|
||||
char const* tr_getDefaultConfigDir(char const* appname)
|
||||
char* tr_getDefaultConfigDir(char const* appname)
|
||||
{
|
||||
static char const* s = nullptr;
|
||||
if (auto* dir = tr_env_get_string("TRANSMISSION_HOME", nullptr); dir != nullptr)
|
||||
{
|
||||
return dir;
|
||||
}
|
||||
|
||||
if (tr_str_is_empty(appname))
|
||||
{
|
||||
appname = "Transmission";
|
||||
}
|
||||
|
||||
if (s == nullptr)
|
||||
{
|
||||
s = tr_env_get_string("TRANSMISSION_HOME", nullptr);
|
||||
|
||||
if (s == nullptr)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
|
||||
s = tr_strvDup(fmt::format("{:s}/Library/Application Support/{:s}"sv, getHomeDir(), appname));
|
||||
return tr_strvDup(fmt::format("{:s}/Library/Application Support/{:s}"sv, getHomeDir(), appname));
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
char* appdata = win32_get_known_folder(FOLDERID_LocalAppData);
|
||||
s = tr_strvDup(fmt::format("{:s}/{:s}"sv, appdata, appname));
|
||||
tr_free(appdata);
|
||||
auto const appdata = win32_get_known_folder(FOLDERID_LocalAppData);
|
||||
return tr_strvDup(fmt::format("{:s}/{:s}"sv, appdata, appname));
|
||||
|
||||
#elif defined(__HAIKU__)
|
||||
|
||||
char buf[PATH_MAX];
|
||||
find_directory(B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf));
|
||||
s = tr_strvDup(fmt::format("{:s}/{:s}"sv, buf, appname);
|
||||
char buf[PATH_MAX];
|
||||
find_directory(B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf));
|
||||
return tr_strvDup(fmt::format("{:s}/{:s}"sv, buf, appname);
|
||||
|
||||
#else
|
||||
|
||||
s = tr_strvDup(fmt::format("{:s}/{:s}"sv, xdgConfigHome(), appname));
|
||||
return tr_strvDup(fmt::format("{:s}/{:s}"sv, xdgConfigHome(), appname));
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::string getXdgEntryFromUserDirs(std::string_view key)
|
||||
|
@ -221,41 +207,31 @@ static std::string getXdgEntryFromUserDirs(std::string_view key)
|
|||
auto constexpr Home = "$HOME"sv;
|
||||
if (auto const it = std::search(std::begin(val), std::end(val), std::begin(Home), std::end(Home)); it != std::end(val))
|
||||
{
|
||||
val.replace(it, it + std::size(Home), std::string_view{ getHomeDir() });
|
||||
val.replace(it, it + std::size(Home), getHomeDir());
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
char const* tr_getDefaultDownloadDir()
|
||||
char* tr_getDefaultDownloadDir()
|
||||
{
|
||||
static char const* user_dir = nullptr;
|
||||
|
||||
if (user_dir == nullptr)
|
||||
if (auto const dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(dir))
|
||||
{
|
||||
if (auto const xdg_user_dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(xdg_user_dir))
|
||||
{
|
||||
user_dir = tr_strvDup(xdg_user_dir);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (user_dir == nullptr)
|
||||
{
|
||||
user_dir = win32_get_known_folder(FOLDERID_Downloads);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (user_dir == nullptr)
|
||||
{
|
||||
#ifdef __HAIKU__
|
||||
user_dir = tr_strvDup(fmt::format("{:s}/Desktop"sv, getHomeDir()));
|
||||
#else
|
||||
user_dir = tr_strvDup(fmt::format("{:s}/Downloads"sv, getHomeDir()));
|
||||
#endif
|
||||
}
|
||||
return tr_strvDup(dir);
|
||||
}
|
||||
|
||||
return user_dir;
|
||||
#ifdef _WIN32
|
||||
if (auto dir = win32_get_known_folder(FOLDERID_Downloads); !std::empty(dir))
|
||||
{
|
||||
return tr_strvDup(dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __HAIKU__
|
||||
return tr_strvDup(fmt::format("{:s}/Desktop"sv, getHomeDir()));
|
||||
#endif
|
||||
|
||||
return tr_strvDup(fmt::format("{:s}/Downloads"sv, getHomeDir()));
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -270,144 +246,127 @@ static bool isWebClientDir(std::string_view path)
|
|||
return found;
|
||||
}
|
||||
|
||||
char const* tr_getWebClientDir([[maybe_unused]] tr_session const* session)
|
||||
std::string tr_getWebClientDir([[maybe_unused]] tr_session const* session)
|
||||
{
|
||||
static char const* s = nullptr;
|
||||
|
||||
if (s == nullptr)
|
||||
if (auto* const dir = tr_env_get_string("CLUTCH_HOME", nullptr); dir != nullptr)
|
||||
{
|
||||
s = tr_env_get_string("CLUTCH_HOME", nullptr);
|
||||
auto ret = std::string{ dir };
|
||||
tr_free(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (s == nullptr)
|
||||
if (auto* const dir = tr_env_get_string("TRANSMISSION_WEB_HOME", nullptr); dir != nullptr)
|
||||
{
|
||||
s = tr_env_get_string("TRANSMISSION_WEB_HOME", nullptr);
|
||||
auto ret = std::string{ dir };
|
||||
tr_free(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef BUILD_MAC_CLIENT
|
||||
|
||||
// look in the Application Support folder
|
||||
if (s == nullptr)
|
||||
if (auto path = tr_pathbuf{ session->config_dir, "/public_html"sv }; isWebClientDir(path))
|
||||
{
|
||||
if (auto path = tr_pathbuf{ session->config_dir, "/public_html"sv }; isWebClientDir(path))
|
||||
{
|
||||
s = tr_strvDup(path);
|
||||
}
|
||||
return std::string{ path };
|
||||
}
|
||||
|
||||
// look in the resource bundle
|
||||
if (s == nullptr)
|
||||
auto app_url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||
auto app_ref = CFURLCopyFileSystemPath(app_url, kCFURLPOSIXPathStyle);
|
||||
auto const buflen = CFStringGetMaximumSizeOfFileSystemRepresentation(app_ref);
|
||||
auto buf = std::vector<char>(buflen, '\0');
|
||||
bool const success = CFStringGetFileSystemRepresentation(app_ref, std::data(buf), std::size(buf));
|
||||
TR_ASSERT(success);
|
||||
CFRelease(app_url);
|
||||
CFRelease(app_ref);
|
||||
if (auto const path = tr_pathbuf{ std::string_view{ std::data(buf) }, "/Contents/Resources/public_html"sv };
|
||||
isWebClientDir(path))
|
||||
{
|
||||
auto app_url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||
auto app_ref = CFURLCopyFileSystemPath(app_url, kCFURLPOSIXPathStyle);
|
||||
auto const buflen = CFStringGetMaximumSizeOfFileSystemRepresentation(app_ref);
|
||||
auto buf = std::vector<char>(buflen, '\0');
|
||||
bool const success = CFStringGetFileSystemRepresentation(app_ref, std::data(buf), std::size(buf));
|
||||
TR_ASSERT(success);
|
||||
CFRelease(app_url);
|
||||
CFRelease(app_ref);
|
||||
|
||||
if (auto const path = tr_pathbuf{ std::string_view{ std::data(buf) }, "/Contents/Resources/public_html"sv };
|
||||
isWebClientDir(path))
|
||||
{
|
||||
s = tr_strvDup(path);
|
||||
}
|
||||
return std::string{ path };
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
if (s == nullptr)
|
||||
/* Generally, Web interface should be stored in a Web subdir of
|
||||
* calling executable dir. */
|
||||
|
||||
static auto constexpr KnownFolderIds = std::array<KNOWNFOLDERID const* const, 3>{
|
||||
&FOLDERID_LocalAppData,
|
||||
&FOLDERID_RoamingAppData,
|
||||
&FOLDERID_ProgramData,
|
||||
};
|
||||
|
||||
for (auto const* const folder_id : KnownFolderIds)
|
||||
{
|
||||
/* Generally, Web interface should be stored in a Web subdir of
|
||||
* calling executable dir. */
|
||||
auto const dir = win32_get_known_folder(*folder_id);
|
||||
|
||||
static KNOWNFOLDERID const* const known_folder_ids[] = {
|
||||
&FOLDERID_LocalAppData,
|
||||
&FOLDERID_RoamingAppData,
|
||||
&FOLDERID_ProgramData,
|
||||
};
|
||||
|
||||
for (size_t i = 0; s == nullptr && i < TR_N_ELEMENTS(known_folder_ids); ++i)
|
||||
if (auto const path = tr_pathbuf{ dir, "/Transmission/Web"sv }; isWebClientDir(path))
|
||||
{
|
||||
char* dir = win32_get_known_folder(*known_folder_ids[i]);
|
||||
|
||||
if (auto const path = tr_pathbuf{ std::string_view{ dir }, "/Transmission/Web"sv }; isWebClientDir(path))
|
||||
{
|
||||
s = tr_strvDup(path);
|
||||
}
|
||||
|
||||
tr_free(dir);
|
||||
return std::string{ path };
|
||||
}
|
||||
}
|
||||
|
||||
if (s == nullptr) /* check calling module place */
|
||||
/* check calling module place */
|
||||
wchar_t wide_module_path[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, wide_module_path, TR_N_ELEMENTS(wide_module_path));
|
||||
char* module_path = tr_win32_native_to_utf8(wide_module_path, -1);
|
||||
if (auto const dir = tr_sys_path_dirname(module_path); !std::empty(dir))
|
||||
{
|
||||
wchar_t wide_module_path[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, wide_module_path, TR_N_ELEMENTS(wide_module_path));
|
||||
char* module_path = tr_win32_native_to_utf8(wide_module_path, -1);
|
||||
|
||||
if (auto const dir = tr_sys_path_dirname(module_path); !std::empty(dir))
|
||||
if (auto const path = tr_pathbuf{ dir, "/Web"sv }; isWebClientDir(path))
|
||||
{
|
||||
if (auto const path = tr_pathbuf{ dir, "/Web"sv }; isWebClientDir(path))
|
||||
{
|
||||
s = tr_strvDup(path);
|
||||
}
|
||||
tr_free(module_path);
|
||||
return std::string{ path };
|
||||
}
|
||||
|
||||
tr_free(module_path);
|
||||
}
|
||||
tr_free(module_path);
|
||||
|
||||
#else // everyone else, follow the XDG spec
|
||||
|
||||
if (s == nullptr)
|
||||
auto candidates = std::list<std::string>{};
|
||||
|
||||
/* XDG_DATA_HOME should be the first in the list of candidates */
|
||||
char* tmp = tr_env_get_string("XDG_DATA_HOME", nullptr);
|
||||
if (!tr_str_is_empty(tmp))
|
||||
{
|
||||
auto candidates = std::list<std::string>{};
|
||||
candidates.emplace_back(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
candidates.emplace_back(fmt::format("{:s}/.local/share"sv, getHomeDir()));
|
||||
}
|
||||
tr_free(tmp);
|
||||
|
||||
/* XDG_DATA_HOME should be the first in the list of candidates */
|
||||
char* tmp = tr_env_get_string("XDG_DATA_HOME", nullptr);
|
||||
if (!tr_str_is_empty(tmp))
|
||||
{
|
||||
candidates.emplace_back(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
candidates.emplace_back(fmt::format("{:s}/.local/share"sv, getHomeDir()));
|
||||
}
|
||||
tr_free(tmp);
|
||||
/* XDG_DATA_DIRS are the backup directories */
|
||||
{
|
||||
char const* const pkg = PACKAGE_DATA_DIR;
|
||||
auto* xdg = tr_env_get_string("XDG_DATA_DIRS", "");
|
||||
auto const buf = fmt::format(FMT_STRING("{:s}:{:s}:/usr/local/share:/usr/share"), pkg, xdg);
|
||||
tr_free(xdg);
|
||||
|
||||
/* XDG_DATA_DIRS are the backup directories */
|
||||
auto sv = std::string_view{ buf };
|
||||
auto token = std::string_view{};
|
||||
while (tr_strvSep(&sv, &token, ':'))
|
||||
{
|
||||
char const* const pkg = PACKAGE_DATA_DIR;
|
||||
auto* xdg = tr_env_get_string("XDG_DATA_DIRS", "");
|
||||
auto const buf = fmt::format(FMT_STRING("{:s}:{:s}:/usr/local/share:/usr/share"), pkg, xdg);
|
||||
tr_free(xdg);
|
||||
|
||||
auto sv = std::string_view{ buf };
|
||||
auto token = std::string_view{};
|
||||
while (tr_strvSep(&sv, &token, ':'))
|
||||
token = tr_strvStrip(token);
|
||||
if (!std::empty(token))
|
||||
{
|
||||
token = tr_strvStrip(token);
|
||||
if (!std::empty(token))
|
||||
{
|
||||
candidates.emplace_back(token);
|
||||
}
|
||||
candidates.emplace_back(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* walk through the candidates & look for a match */
|
||||
for (auto const& dir : candidates)
|
||||
/* walk through the candidates & look for a match */
|
||||
for (auto const& dir : candidates)
|
||||
{
|
||||
if (auto const path = tr_pathbuf{ dir, "/transmission/public_html"sv }; isWebClientDir(path))
|
||||
{
|
||||
if (auto const path = tr_pathbuf{ dir, "/transmission/public_html"sv }; isWebClientDir(path))
|
||||
{
|
||||
s = tr_strvDup(path);
|
||||
break;
|
||||
}
|
||||
return std::string{ path };
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return s;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string tr_getSessionIdDir()
|
||||
|
@ -418,9 +377,8 @@ std::string tr_getSessionIdDir()
|
|||
|
||||
#else
|
||||
|
||||
char* program_data_dir = win32_get_known_folder_ex(FOLDERID_ProgramData, KF_FLAG_CREATE);
|
||||
auto const program_data_dir = win32_get_known_folder_ex(FOLDERID_ProgramData, KF_FLAG_CREATE);
|
||||
auto result = fmt::format("{:s}/Transmission"sv, program_data_dir);
|
||||
tr_free(program_data_dir);
|
||||
tr_sys_dir_create(result, 0, 0);
|
||||
return result;
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ void tr_setConfigDir(tr_session* session, std::string_view config_dir);
|
|||
char const* tr_getTorrentDir(tr_session const*);
|
||||
|
||||
/** @brief return the directory where the Web Client's web ui files are kept */
|
||||
char const* tr_getWebClientDir(tr_session const*);
|
||||
std::string tr_getWebClientDir(tr_session const*);
|
||||
|
||||
/** @brief return the directory where session id lock files are stored */
|
||||
std::string tr_getSessionIdDir();
|
||||
|
|
|
@ -256,9 +256,7 @@ static void serve_file(struct evhttp_request* req, tr_rpc_server* server, std::s
|
|||
|
||||
static void handle_web_client(struct evhttp_request* req, tr_rpc_server* server)
|
||||
{
|
||||
char const* webClientDir = tr_getWebClientDir(server->session);
|
||||
|
||||
if (tr_str_is_empty(webClientDir))
|
||||
if (std::empty(server->web_client_dir_))
|
||||
{
|
||||
send_simple_response(
|
||||
req,
|
||||
|
@ -287,7 +285,7 @@ static void handle_web_client(struct evhttp_request* req, tr_rpc_server* server)
|
|||
}
|
||||
else
|
||||
{
|
||||
auto const filename = tr_pathbuf{ webClientDir, "/"sv, tr_str_is_empty(subpath) ? "index.html" : subpath };
|
||||
auto const filename = tr_pathbuf{ server->web_client_dir_, '/', tr_str_is_empty(subpath) ? "index.html" : subpath };
|
||||
serve_file(req, server, filename.sv());
|
||||
}
|
||||
|
||||
|
@ -940,6 +938,7 @@ static void missing_settings_key(tr_quark const 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>())
|
||||
, session{ session_in }
|
||||
{
|
||||
|
@ -1145,10 +1144,9 @@ tr_rpc_server::tr_rpc_server(tr_session* session_in, tr_variant* settings)
|
|||
}
|
||||
}
|
||||
|
||||
char const* webClientDir = tr_getWebClientDir(this->session);
|
||||
if (!tr_str_is_empty(webClientDir))
|
||||
if (!std::empty(web_client_dir_))
|
||||
{
|
||||
tr_logAddInfo(fmt::format(_("Serving RPC and Web requests from '{path}'"), fmt::arg("path", webClientDir)));
|
||||
tr_logAddInfo(fmt::format(_("Serving RPC and Web requests from '{path}'"), fmt::arg("path", web_client_dir_)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ public:
|
|||
|
||||
std::vector<std::string> hostWhitelist;
|
||||
std::vector<std::string> whitelist_;
|
||||
std::string const web_client_dir_;
|
||||
std::string salted_password_;
|
||||
std::string username_;
|
||||
std::string whitelist_str_;
|
||||
|
|
|
@ -319,6 +319,7 @@ tr_address const* tr_sessionGetPublicAddress(tr_session const* session, int tr_a
|
|||
|
||||
void tr_sessionGetDefaultSettings(tr_variant* d)
|
||||
{
|
||||
auto* const download_dir = tr_getDefaultDownloadDir();
|
||||
TR_ASSERT(tr_variantIsDict(d));
|
||||
|
||||
tr_variantDictReserve(d, 71);
|
||||
|
@ -328,14 +329,14 @@ void tr_sessionGetDefaultSettings(tr_variant* d)
|
|||
tr_variantDictAddBool(d, TR_KEY_dht_enabled, true);
|
||||
tr_variantDictAddBool(d, TR_KEY_utp_enabled, true);
|
||||
tr_variantDictAddBool(d, TR_KEY_lpd_enabled, false);
|
||||
tr_variantDictAddStr(d, TR_KEY_download_dir, tr_getDefaultDownloadDir());
|
||||
tr_variantDictAddStr(d, TR_KEY_download_dir, download_dir);
|
||||
tr_variantDictAddStr(d, TR_KEY_default_trackers, "");
|
||||
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, 100);
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
|
||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_DEFAULT_ENCRYPTION);
|
||||
tr_variantDictAddInt(d, TR_KEY_idle_seeding_limit, 30);
|
||||
tr_variantDictAddBool(d, TR_KEY_idle_seeding_limit_enabled, false);
|
||||
tr_variantDictAddStr(d, TR_KEY_incomplete_dir, tr_getDefaultDownloadDir());
|
||||
tr_variantDictAddStr(d, TR_KEY_incomplete_dir, download_dir);
|
||||
tr_variantDictAddBool(d, TR_KEY_incomplete_dir_enabled, false);
|
||||
tr_variantDictAddInt(d, TR_KEY_message_level, TR_LOG_INFO);
|
||||
tr_variantDictAddInt(d, TR_KEY_download_queue_size, 5);
|
||||
|
@ -397,6 +398,8 @@ void tr_sessionGetDefaultSettings(tr_variant* d)
|
|||
tr_variantDictAddBool(d, TR_KEY_anti_brute_force_enabled, true);
|
||||
tr_variantDictAddStrView(d, TR_KEY_announce_ip, "");
|
||||
tr_variantDictAddBool(d, TR_KEY_announce_ip_enabled, false);
|
||||
|
||||
tr_free(download_dir);
|
||||
}
|
||||
|
||||
void tr_sessionGetSettings(tr_session const* s, tr_variant* d)
|
||||
|
@ -479,6 +482,19 @@ void tr_sessionGetSettings(tr_session const* s, tr_variant* d)
|
|||
}
|
||||
}
|
||||
|
||||
static void getSettingsFilename(tr_pathbuf& setme, char const* config_dir, char const* appname)
|
||||
{
|
||||
if (!tr_str_is_empty(config_dir))
|
||||
{
|
||||
setme.assign(std::string_view{ config_dir }, "/settings.json"sv);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const default_config_dir = tr_getDefaultConfigDir(appname);
|
||||
setme.assign(std::string_view{ default_config_dir }, "/settings.json"sv);
|
||||
tr_free(default_config_dir);
|
||||
}
|
||||
|
||||
bool tr_sessionLoadSettings(tr_variant* dict, char const* config_dir, char const* appName)
|
||||
{
|
||||
TR_ASSERT(tr_variantIsDict(dict));
|
||||
|
@ -491,17 +507,16 @@ bool tr_sessionLoadSettings(tr_variant* dict, char const* config_dir, char const
|
|||
tr_variantMergeDicts(dict, &oldDict);
|
||||
tr_variantFree(&oldDict);
|
||||
|
||||
/* if caller didn't specify a config dir, use the default */
|
||||
if (tr_str_is_empty(config_dir))
|
||||
{
|
||||
config_dir = tr_getDefaultConfigDir(appName);
|
||||
}
|
||||
|
||||
/* file settings override the defaults */
|
||||
auto fileSettings = tr_variant{};
|
||||
auto const filename = tr_pathbuf{ config_dir, "/settings.json"sv };
|
||||
auto success = bool{};
|
||||
if (tr_error* error = nullptr; tr_variantFromFile(&fileSettings, TR_VARIANT_PARSE_JSON, filename, &error))
|
||||
auto filename = tr_pathbuf{};
|
||||
getSettingsFilename(filename, config_dir, appName);
|
||||
if (!tr_sys_path_exists(filename))
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
else if (tr_variantFromFile(&fileSettings, TR_VARIANT_PARSE_JSON, filename))
|
||||
{
|
||||
tr_variantMergeDicts(dict, &fileSettings);
|
||||
tr_variantFree(&fileSettings);
|
||||
|
@ -509,8 +524,7 @@ bool tr_sessionLoadSettings(tr_variant* dict, char const* config_dir, char const
|
|||
}
|
||||
else
|
||||
{
|
||||
success = TR_ERROR_IS_ENOENT(error->code);
|
||||
tr_error_free(error);
|
||||
success = false;
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
|
|
|
@ -99,6 +99,8 @@ enum tr_encryption_mode
|
|||
/**
|
||||
* @brief returns Transmission's default configuration file directory.
|
||||
*
|
||||
* Use tr_free() to free the string when done.
|
||||
*
|
||||
* The default configuration directory is determined this way:
|
||||
* -# If the TRANSMISSION_HOME environment variable is set, its value is used.
|
||||
* -# On Darwin, "${HOME}/Library/Application Support/${appname}" is used.
|
||||
|
@ -106,17 +108,19 @@ enum tr_encryption_mode
|
|||
* -# If XDG_CONFIG_HOME is set, "${XDG_CONFIG_HOME}/${appname}" is used.
|
||||
* -# ${HOME}/.config/${appname}" is used as a last resort.
|
||||
*/
|
||||
char const* tr_getDefaultConfigDir(char const* appname);
|
||||
char* tr_getDefaultConfigDir(char const* appname);
|
||||
|
||||
/**
|
||||
* @brief returns Transmisson's default download directory.
|
||||
*
|
||||
* Use tr_free() to free the string when done.
|
||||
*
|
||||
* The default download directory is determined this way:
|
||||
* -# If the HOME environment variable is set, "${HOME}/Downloads" is used.
|
||||
* -# On Windows, "${CSIDL_MYDOCUMENTS}/Downloads" is used.
|
||||
* -# Otherwise, getpwuid(getuid())->pw_dir + "/Downloads" is used.
|
||||
*/
|
||||
char const* tr_getDefaultDownloadDir(void);
|
||||
char* tr_getDefaultDownloadDir();
|
||||
|
||||
#define TR_DEFAULT_BIND_ADDRESS_IPV4 "0.0.0.0"
|
||||
#define TR_DEFAULT_BIND_ADDRESS_IPV6 "::"
|
||||
|
|
|
@ -508,11 +508,11 @@ static void removeKeRangerRansomware()
|
|||
|
||||
tr_formatter_mem_init(1000, kbString.UTF8String, mbString.UTF8String, gbString.UTF8String, tbString.UTF8String);
|
||||
|
||||
char const* configDir = tr_getDefaultConfigDir("Transmission");
|
||||
_fLib = tr_sessionInit(configDir, YES, &settings);
|
||||
char* const default_config_dir = tr_getDefaultConfigDir("Transmission");
|
||||
_fLib = tr_sessionInit(default_config_dir, YES, &settings);
|
||||
tr_variantFree(&settings);
|
||||
|
||||
_fConfigDirectory = @(configDir);
|
||||
_fConfigDirectory = @(default_config_dir);
|
||||
tr_free(default_config_dir);
|
||||
|
||||
NSApp.delegate = self;
|
||||
|
||||
|
|
|
@ -218,7 +218,9 @@ Application::Application(int& argc, char** argv)
|
|||
// set the fallback config dir
|
||||
if (config_dir.isNull())
|
||||
{
|
||||
config_dir = QString::fromUtf8(tr_getDefaultConfigDir("transmission"));
|
||||
auto* const default_config_dir = tr_getDefaultConfigDir("transmission");
|
||||
config_dir = QString::fromUtf8(default_config_dir);
|
||||
tr_free(default_config_dir);
|
||||
}
|
||||
|
||||
// ensure our config directory exists
|
||||
|
|
|
@ -430,7 +430,7 @@ void Prefs::initDefaults(tr_variant* d) const
|
|||
auto constexpr StatsMode = std::string_view{ "total-ratio" };
|
||||
auto constexpr WindowLayout = std::string_view{ "menu,toolbar,filter,list,statusbar" };
|
||||
|
||||
auto const download_dir = std::string_view{ tr_getDefaultDownloadDir() };
|
||||
auto* const download_dir = tr_getDefaultDownloadDir();
|
||||
|
||||
tr_variantDictReserve(d, 38);
|
||||
dictAdd(d, TR_KEY_blocklist_updates_enabled, true);
|
||||
|
@ -460,7 +460,7 @@ void Prefs::initDefaults(tr_variant* d) const
|
|||
dictAdd(d, TR_KEY_main_window_x, 50);
|
||||
dictAdd(d, TR_KEY_main_window_y, 50);
|
||||
dictAdd(d, TR_KEY_remote_session_port, TR_DEFAULT_RPC_PORT);
|
||||
dictAdd(d, TR_KEY_download_dir, download_dir);
|
||||
dictAdd(d, TR_KEY_download_dir, std::string_view{ download_dir });
|
||||
dictAdd(d, TR_KEY_filter_mode, FilterMode);
|
||||
dictAdd(d, TR_KEY_main_window_layout_order, WindowLayout);
|
||||
dictAdd(d, TR_KEY_open_dialog_dir, QDir::home().absolutePath());
|
||||
|
@ -469,8 +469,10 @@ void Prefs::initDefaults(tr_variant* d) const
|
|||
dictAdd(d, TR_KEY_remote_session_username, SessionUsername);
|
||||
dictAdd(d, TR_KEY_sort_mode, SortMode);
|
||||
dictAdd(d, TR_KEY_statusbar_stats, StatsMode);
|
||||
dictAdd(d, TR_KEY_watch_dir, download_dir);
|
||||
dictAdd(d, TR_KEY_watch_dir, std::string_view{ download_dir });
|
||||
dictAdd(d, TR_KEY_read_clipboard, false);
|
||||
|
||||
tr_free(download_dir);
|
||||
}
|
||||
|
||||
/***
|
||||
|
|
|
@ -24,6 +24,7 @@ add_executable(libtransmission-test
|
|||
peer-mgr-active-requests-test.cc
|
||||
peer-mgr-wishlist-test.cc
|
||||
peer-msgs-test.cc
|
||||
platform-test.cc
|
||||
quark-test.cc
|
||||
remove-test.cc
|
||||
rename-test.cc
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# This file is written by xdg-user-dirs-update
|
||||
# If you want to change or add directories, just edit the line you're
|
||||
# interested in. All local changes will be retained on the next run.
|
||||
# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
|
||||
# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
|
||||
# absolute path. No other format is supported.
|
||||
#
|
||||
XDG_DESKTOP_DIR="$HOME/Desktop"
|
||||
XDG_DOWNLOAD_DIR="$HOME/UserDirsDownloads"
|
||||
XDG_TEMPLATES_DIR="$HOME/Templates"
|
||||
XDG_PUBLICSHARE_DIR="$HOME/Public"
|
||||
XDG_DOCUMENTS_DIR="$HOME/Documents"
|
||||
XDG_MUSIC_DIR="$HOME/Music"
|
||||
XDG_PICTURES_DIR="$HOME/Pictures"
|
||||
XDG_VIDEOS_DIR="$HOME/Videos"
|
|
@ -0,0 +1,122 @@
|
|||
// This file Copyright (C) 2022 Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "file.h"
|
||||
#include "tr-strbuf.h"
|
||||
|
||||
#include "test-fixtures.h"
|
||||
|
||||
using namespace std::literals;
|
||||
using PlatformTest = ::libtransmission::test::SessionTest;
|
||||
using ::libtransmission::test::makeString;
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define setenv(key, value, unused) SetEnvironmentVariableA(key, value)
|
||||
#define unsetenv(key) SetEnvironmentVariableA(key, nullptr)
|
||||
#endif
|
||||
|
||||
TEST_F(PlatformTest, defaultDownloadDirXdg)
|
||||
{
|
||||
setenv("HOME", sandboxDir().c_str(), 1);
|
||||
setenv("XDG_CONFIG_HOME", LIBTRANSMISSION_TEST_ASSETS_DIR, 1);
|
||||
|
||||
auto actual = makeString(tr_getDefaultDownloadDir());
|
||||
auto expected = fmt::format("{:s}/UserDirsDownloads"sv, sandboxDir());
|
||||
EXPECT_EQ(expected, actual);
|
||||
|
||||
unsetenv("XDG_CONFIG_HOME");
|
||||
unsetenv("HOME");
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && !defined(__HAIKU__)
|
||||
TEST_F(PlatformTest, defaultDownloadDir)
|
||||
{
|
||||
setenv("HOME", sandboxDir().c_str(), 1);
|
||||
|
||||
auto expected = fmt::format("{:s}/Downloads"sv, sandboxDir());
|
||||
auto actual = makeString(tr_getDefaultDownloadDir());
|
||||
EXPECT_EQ(expected, actual);
|
||||
|
||||
unsetenv("HOME");
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(PlatformTest, defaultConfigDirEnv)
|
||||
{
|
||||
setenv("TRANSMISSION_HOME", sandboxDir().c_str(), 1);
|
||||
|
||||
auto actual = makeString(tr_getDefaultConfigDir("appname"));
|
||||
auto expected = sandboxDir();
|
||||
EXPECT_EQ(expected, actual);
|
||||
|
||||
unsetenv("TRANSMISSION_HOME");
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__) && !defined(_WIN32) && !defined(__HAIKU__)
|
||||
|
||||
TEST_F(PlatformTest, defaultConfigDirXdgConfig)
|
||||
{
|
||||
setenv("XDG_CONFIG_HOME", sandboxDir().c_str(), 1);
|
||||
|
||||
auto expected = fmt::format("{:s}/appname", sandboxDir());
|
||||
auto actual = makeString(tr_getDefaultConfigDir("appname"));
|
||||
EXPECT_EQ(expected, actual);
|
||||
|
||||
unsetenv("XDG_CONFIG_HOME");
|
||||
}
|
||||
|
||||
TEST_F(PlatformTest, defaultConfigDirXdgConfigHome)
|
||||
{
|
||||
auto const home = tr_pathbuf{ sandboxDir(), "/home/user" };
|
||||
setenv("HOME", home, 1);
|
||||
|
||||
auto expected = fmt::format("{:s}/.config/appname", home.sv());
|
||||
auto actual = makeString(tr_getDefaultConfigDir("appname"));
|
||||
EXPECT_EQ(expected, actual);
|
||||
|
||||
unsetenv("HOME");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST_F(PlatformTest, webClientDirEnvClutch)
|
||||
{
|
||||
setenv("CLUTCH_HOME", sandboxDir().c_str(), 1);
|
||||
|
||||
EXPECT_EQ(sandboxDir(), tr_getWebClientDir(session_));
|
||||
|
||||
unsetenv("CLUTCH_HOME");
|
||||
}
|
||||
|
||||
TEST_F(PlatformTest, webClientDirEnvTr)
|
||||
{
|
||||
setenv("TRANSMISSION_WEB_HOME", sandboxDir().c_str(), 1);
|
||||
|
||||
EXPECT_EQ(sandboxDir(), tr_getWebClientDir(session_));
|
||||
|
||||
unsetenv("TRANSMISSION_WEB_HOME");
|
||||
}
|
||||
|
||||
#if !defined(BUILD_MAC_CLIENT) && !defined(_WIN32)
|
||||
TEST_F(PlatformTest, webClientDirXdgDataHome)
|
||||
{
|
||||
setenv("XDG_DATA_HOME", sandboxDir().c_str(), 1);
|
||||
|
||||
auto const expected = tr_pathbuf{ sandboxDir(), "/transmission/public_html"sv };
|
||||
auto const index_html = tr_pathbuf{ expected, "/index.html"sv };
|
||||
EXPECT_TRUE(tr_sys_dir_create(expected, TR_SYS_DIR_CREATE_PARENTS, 0777));
|
||||
EXPECT_TRUE(tr_saveFile(index_html, "<html></html>"sv));
|
||||
|
||||
EXPECT_EQ(expected, tr_getWebClientDir(session_));
|
||||
|
||||
unsetenv("XDG_DATA_HOME");
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue