mirror of
https://github.com/transmission/transmission.git
synced 2026-04-18 07:56:33 +01:00
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:
@@ -202,7 +202,6 @@ static char const* getConfigDir(int argc, char const** argv)
|
|||||||
int tr_main(int argc, char* argv[])
|
int tr_main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
tr_variant settings;
|
tr_variant settings;
|
||||||
char const* configDir;
|
|
||||||
|
|
||||||
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);
|
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);
|
||||||
tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr);
|
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 */
|
/* load the defaults from config file + libtransmission defaults */
|
||||||
tr_variantInitDict(&settings, 0);
|
tr_variantInitDict(&settings, 0);
|
||||||
configDir = getConfigDir(argc, (char const**)argv);
|
char const* const configDir = getConfigDir(argc, (char const**)argv);
|
||||||
tr_sessionLoadSettings(&settings, configDir, MyConfigName);
|
tr_sessionLoadSettings(&settings, configDir, MyConfigName);
|
||||||
|
|
||||||
/* the command line overrides defaults */
|
/* the command line overrides defaults */
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include <glibmm/i18n.h>
|
#include <glibmm/i18n.h>
|
||||||
|
|
||||||
#include <libtransmission/transmission.h>
|
#include <libtransmission/transmission.h>
|
||||||
|
|
||||||
|
#include <libtransmission/utils.h>
|
||||||
#include <libtransmission/variant.h>
|
#include <libtransmission/variant.h>
|
||||||
|
|
||||||
#include "Prefs.h"
|
#include "Prefs.h"
|
||||||
@@ -47,7 +49,9 @@ static void tr_prefs_init_defaults(tr_variant* d)
|
|||||||
|
|
||||||
if (dir.empty())
|
if (dir.empty())
|
||||||
{
|
{
|
||||||
dir = tr_getDefaultDownloadDir();
|
auto* const tmp = tr_getDefaultDownloadDir();
|
||||||
|
dir = tmp;
|
||||||
|
tr_free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
tr_variantDictReserve(d, 31);
|
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));
|
tr_formatter_speed_init(speed_K, _(speed_K_str), _(speed_M_str), _(speed_G_str), _(speed_T_str));
|
||||||
|
|
||||||
/* set up the config dir */
|
/* 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);
|
gtr_pref_init(config_dir);
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
// License text can be found in the licenses/ folder.
|
// License text can be found in the licenses/ folder.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <array>
|
||||||
|
#include <iterator>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifdef __HAIKU__
|
#ifdef __HAIKU__
|
||||||
@@ -51,65 +51,59 @@ using namespace std::literals;
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#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;
|
PWSTR path;
|
||||||
|
|
||||||
if (SHGetKnownFolderPath(folder_id, flags | KF_FLAG_DONT_UNEXPAND, nullptr, &path) == S_OK)
|
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);
|
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);
|
return win32_get_known_folder_ex(folder_id, KF_FLAG_DONT_VERIFY);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static char const* getHomeDir()
|
static std::string getHomeDir()
|
||||||
{
|
{
|
||||||
static char const* home = nullptr;
|
if (auto* const dir = tr_env_get_string("HOME", nullptr); dir != nullptr)
|
||||||
|
|
||||||
if (home == nullptr)
|
|
||||||
{
|
{
|
||||||
home = tr_env_get_string("HOME", nullptr);
|
auto ret = std::string{ dir };
|
||||||
|
tr_free(dir);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#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
|
#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;
|
return pw->pw_dir;
|
||||||
struct passwd* pw = nullptr;
|
|
||||||
char buf[4096];
|
|
||||||
getpwuid_r(getuid(), &pwent, buf, sizeof buf, &pw);
|
|
||||||
if (pw != nullptr)
|
|
||||||
{
|
|
||||||
home = tr_strdup(pw->pw_dir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (home == nullptr)
|
return {};
|
||||||
{
|
|
||||||
home = tr_strdup("");
|
|
||||||
}
|
|
||||||
|
|
||||||
return home;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string xdgConfigHome()
|
static std::string xdgConfigHome()
|
||||||
@@ -151,46 +145,38 @@ char const* tr_getTorrentDir(tr_session const* session)
|
|||||||
return session->torrent_dir.c_str();
|
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))
|
if (tr_str_is_empty(appname))
|
||||||
{
|
{
|
||||||
appname = "Transmission";
|
appname = "Transmission";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s == nullptr)
|
|
||||||
{
|
|
||||||
s = tr_env_get_string("TRANSMISSION_HOME", nullptr);
|
|
||||||
|
|
||||||
if (s == nullptr)
|
|
||||||
{
|
|
||||||
#ifdef __APPLE__
|
#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)
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
char* appdata = win32_get_known_folder(FOLDERID_LocalAppData);
|
auto const appdata = win32_get_known_folder(FOLDERID_LocalAppData);
|
||||||
s = tr_strvDup(fmt::format("{:s}/{:s}"sv, appdata, appname));
|
return tr_strvDup(fmt::format("{:s}/{:s}"sv, appdata, appname));
|
||||||
tr_free(appdata);
|
|
||||||
|
|
||||||
#elif defined(__HAIKU__)
|
#elif defined(__HAIKU__)
|
||||||
|
|
||||||
char buf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
find_directory(B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf));
|
find_directory(B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf));
|
||||||
s = tr_strvDup(fmt::format("{:s}/{:s}"sv, buf, appname);
|
return tr_strvDup(fmt::format("{:s}/{:s}"sv, buf, appname);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
s = tr_strvDup(fmt::format("{:s}/{:s}"sv, xdgConfigHome(), appname));
|
return tr_strvDup(fmt::format("{:s}/{:s}"sv, xdgConfigHome(), appname));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string getXdgEntryFromUserDirs(std::string_view key)
|
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;
|
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))
|
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;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const* tr_getDefaultDownloadDir()
|
char* tr_getDefaultDownloadDir()
|
||||||
{
|
{
|
||||||
static char const* user_dir = nullptr;
|
if (auto const dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(dir))
|
||||||
|
|
||||||
if (user_dir == nullptr)
|
|
||||||
{
|
{
|
||||||
if (auto const xdg_user_dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(xdg_user_dir))
|
return tr_strvDup(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 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;
|
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 (auto* const dir = tr_env_get_string("CLUTCH_HOME", nullptr); dir != nullptr)
|
||||||
|
|
||||||
if (s == 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
|
#ifdef BUILD_MAC_CLIENT
|
||||||
|
|
||||||
// look in the Application Support folder
|
// 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))
|
return std::string{ path };
|
||||||
{
|
|
||||||
s = tr_strvDup(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// look in the resource bundle
|
// 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());
|
return std::string{ path };
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(_WIN32)
|
#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
|
auto const dir = win32_get_known_folder(*folder_id);
|
||||||
* calling executable dir. */
|
|
||||||
|
|
||||||
static KNOWNFOLDERID const* const known_folder_ids[] = {
|
if (auto const path = tr_pathbuf{ dir, "/Transmission/Web"sv }; isWebClientDir(path))
|
||||||
&FOLDERID_LocalAppData,
|
|
||||||
&FOLDERID_RoamingAppData,
|
|
||||||
&FOLDERID_ProgramData,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (size_t i = 0; s == nullptr && i < TR_N_ELEMENTS(known_folder_ids); ++i)
|
|
||||||
{
|
{
|
||||||
char* dir = win32_get_known_folder(*known_folder_ids[i]);
|
return std::string{ path };
|
||||||
|
|
||||||
if (auto const path = tr_pathbuf{ std::string_view{ dir }, "/Transmission/Web"sv }; isWebClientDir(path))
|
|
||||||
{
|
|
||||||
s = tr_strvDup(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr_free(dir);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
if (auto const path = tr_pathbuf{ dir, "/Web"sv }; isWebClientDir(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))
|
tr_free(module_path);
|
||||||
{
|
return std::string{ path };
|
||||||
s = tr_strvDup(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tr_free(module_path);
|
|
||||||
}
|
}
|
||||||
|
tr_free(module_path);
|
||||||
|
|
||||||
#else // everyone else, follow the XDG spec
|
#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 */
|
/* XDG_DATA_DIRS are the backup directories */
|
||||||
char* tmp = tr_env_get_string("XDG_DATA_HOME", nullptr);
|
{
|
||||||
if (!tr_str_is_empty(tmp))
|
char const* const pkg = PACKAGE_DATA_DIR;
|
||||||
{
|
auto* xdg = tr_env_get_string("XDG_DATA_DIRS", "");
|
||||||
candidates.emplace_back(tmp);
|
auto const buf = fmt::format(FMT_STRING("{:s}:{:s}:/usr/local/share:/usr/share"), pkg, xdg);
|
||||||
}
|
tr_free(xdg);
|
||||||
else
|
|
||||||
{
|
|
||||||
candidates.emplace_back(fmt::format("{:s}/.local/share"sv, getHomeDir()));
|
|
||||||
}
|
|
||||||
tr_free(tmp);
|
|
||||||
|
|
||||||
/* 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;
|
token = tr_strvStrip(token);
|
||||||
auto* xdg = tr_env_get_string("XDG_DATA_DIRS", "");
|
if (!std::empty(token))
|
||||||
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);
|
candidates.emplace_back(token);
|
||||||
if (!std::empty(token))
|
|
||||||
{
|
|
||||||
candidates.emplace_back(token);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* walk through the candidates & look for a match */
|
/* walk through the candidates & look for a match */
|
||||||
for (auto const& dir : candidates)
|
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))
|
return std::string{ path };
|
||||||
{
|
|
||||||
s = tr_strvDup(path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return s;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string tr_getSessionIdDir()
|
std::string tr_getSessionIdDir()
|
||||||
@@ -418,9 +377,8 @@ std::string tr_getSessionIdDir()
|
|||||||
|
|
||||||
#else
|
#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);
|
auto result = fmt::format("{:s}/Transmission"sv, program_data_dir);
|
||||||
tr_free(program_data_dir);
|
|
||||||
tr_sys_dir_create(result, 0, 0);
|
tr_sys_dir_create(result, 0, 0);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ void tr_setConfigDir(tr_session* session, std::string_view config_dir);
|
|||||||
char const* tr_getTorrentDir(tr_session const*);
|
char const* tr_getTorrentDir(tr_session const*);
|
||||||
|
|
||||||
/** @brief return the directory where the Web Client's web ui files are kept */
|
/** @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 */
|
/** @brief return the directory where session id lock files are stored */
|
||||||
std::string tr_getSessionIdDir();
|
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)
|
static void handle_web_client(struct evhttp_request* req, tr_rpc_server* server)
|
||||||
{
|
{
|
||||||
char const* webClientDir = tr_getWebClientDir(server->session);
|
if (std::empty(server->web_client_dir_))
|
||||||
|
|
||||||
if (tr_str_is_empty(webClientDir))
|
|
||||||
{
|
{
|
||||||
send_simple_response(
|
send_simple_response(
|
||||||
req,
|
req,
|
||||||
@@ -287,7 +285,7 @@ static void handle_web_client(struct evhttp_request* req, tr_rpc_server* server)
|
|||||||
}
|
}
|
||||||
else
|
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());
|
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)
|
tr_rpc_server::tr_rpc_server(tr_session* session_in, tr_variant* settings)
|
||||||
: compressor{ libdeflate_alloc_compressor(DeflateLevel), libdeflate_free_compressor }
|
: compressor{ libdeflate_alloc_compressor(DeflateLevel), libdeflate_free_compressor }
|
||||||
|
, web_client_dir_{ tr_getWebClientDir(session_in) }
|
||||||
, bindAddress(std::make_unique<struct tr_rpc_address>())
|
, bindAddress(std::make_unique<struct tr_rpc_address>())
|
||||||
, session{ session_in }
|
, 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 (!std::empty(web_client_dir_))
|
||||||
if (!tr_str_is_empty(webClientDir))
|
|
||||||
{
|
{
|
||||||
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> hostWhitelist;
|
||||||
std::vector<std::string> whitelist_;
|
std::vector<std::string> whitelist_;
|
||||||
|
std::string const web_client_dir_;
|
||||||
std::string salted_password_;
|
std::string salted_password_;
|
||||||
std::string username_;
|
std::string username_;
|
||||||
std::string whitelist_str_;
|
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)
|
void tr_sessionGetDefaultSettings(tr_variant* d)
|
||||||
{
|
{
|
||||||
|
auto* const download_dir = tr_getDefaultDownloadDir();
|
||||||
TR_ASSERT(tr_variantIsDict(d));
|
TR_ASSERT(tr_variantIsDict(d));
|
||||||
|
|
||||||
tr_variantDictReserve(d, 71);
|
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_dht_enabled, true);
|
||||||
tr_variantDictAddBool(d, TR_KEY_utp_enabled, true);
|
tr_variantDictAddBool(d, TR_KEY_utp_enabled, true);
|
||||||
tr_variantDictAddBool(d, TR_KEY_lpd_enabled, false);
|
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_variantDictAddStr(d, TR_KEY_default_trackers, "");
|
||||||
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, 100);
|
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, 100);
|
||||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
|
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
|
||||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_DEFAULT_ENCRYPTION);
|
tr_variantDictAddInt(d, TR_KEY_encryption, TR_DEFAULT_ENCRYPTION);
|
||||||
tr_variantDictAddInt(d, TR_KEY_idle_seeding_limit, 30);
|
tr_variantDictAddInt(d, TR_KEY_idle_seeding_limit, 30);
|
||||||
tr_variantDictAddBool(d, TR_KEY_idle_seeding_limit_enabled, false);
|
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_variantDictAddBool(d, TR_KEY_incomplete_dir_enabled, false);
|
||||||
tr_variantDictAddInt(d, TR_KEY_message_level, TR_LOG_INFO);
|
tr_variantDictAddInt(d, TR_KEY_message_level, TR_LOG_INFO);
|
||||||
tr_variantDictAddInt(d, TR_KEY_download_queue_size, 5);
|
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_variantDictAddBool(d, TR_KEY_anti_brute_force_enabled, true);
|
||||||
tr_variantDictAddStrView(d, TR_KEY_announce_ip, "");
|
tr_variantDictAddStrView(d, TR_KEY_announce_ip, "");
|
||||||
tr_variantDictAddBool(d, TR_KEY_announce_ip_enabled, false);
|
tr_variantDictAddBool(d, TR_KEY_announce_ip_enabled, false);
|
||||||
|
|
||||||
|
tr_free(download_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tr_sessionGetSettings(tr_session const* s, tr_variant* d)
|
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)
|
bool tr_sessionLoadSettings(tr_variant* dict, char const* config_dir, char const* appName)
|
||||||
{
|
{
|
||||||
TR_ASSERT(tr_variantIsDict(dict));
|
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_variantMergeDicts(dict, &oldDict);
|
||||||
tr_variantFree(&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 */
|
/* file settings override the defaults */
|
||||||
auto fileSettings = tr_variant{};
|
auto fileSettings = tr_variant{};
|
||||||
auto const filename = tr_pathbuf{ config_dir, "/settings.json"sv };
|
|
||||||
auto success = bool{};
|
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_variantMergeDicts(dict, &fileSettings);
|
||||||
tr_variantFree(&fileSettings);
|
tr_variantFree(&fileSettings);
|
||||||
@@ -509,8 +524,7 @@ bool tr_sessionLoadSettings(tr_variant* dict, char const* config_dir, char const
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
success = TR_ERROR_IS_ENOENT(error->code);
|
success = false;
|
||||||
tr_error_free(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cleanup */
|
/* cleanup */
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ enum tr_encryption_mode
|
|||||||
/**
|
/**
|
||||||
* @brief returns Transmission's default configuration file directory.
|
* @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:
|
* The default configuration directory is determined this way:
|
||||||
* -# If the TRANSMISSION_HOME environment variable is set, its value is used.
|
* -# If the TRANSMISSION_HOME environment variable is set, its value is used.
|
||||||
* -# On Darwin, "${HOME}/Library/Application Support/${appname}" 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.
|
* -# If XDG_CONFIG_HOME is set, "${XDG_CONFIG_HOME}/${appname}" is used.
|
||||||
* -# ${HOME}/.config/${appname}" is used as a last resort.
|
* -# ${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.
|
* @brief returns Transmisson's default download directory.
|
||||||
*
|
*
|
||||||
|
* Use tr_free() to free the string when done.
|
||||||
|
*
|
||||||
* The default download directory is determined this way:
|
* The default download directory is determined this way:
|
||||||
* -# If the HOME environment variable is set, "${HOME}/Downloads" is used.
|
* -# If the HOME environment variable is set, "${HOME}/Downloads" is used.
|
||||||
* -# On Windows, "${CSIDL_MYDOCUMENTS}/Downloads" is used.
|
* -# On Windows, "${CSIDL_MYDOCUMENTS}/Downloads" is used.
|
||||||
* -# Otherwise, getpwuid(getuid())->pw_dir + "/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_IPV4 "0.0.0.0"
|
||||||
#define TR_DEFAULT_BIND_ADDRESS_IPV6 "::"
|
#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);
|
tr_formatter_mem_init(1000, kbString.UTF8String, mbString.UTF8String, gbString.UTF8String, tbString.UTF8String);
|
||||||
|
|
||||||
char const* configDir = tr_getDefaultConfigDir("Transmission");
|
char* const default_config_dir = tr_getDefaultConfigDir("Transmission");
|
||||||
_fLib = tr_sessionInit(configDir, YES, &settings);
|
_fLib = tr_sessionInit(default_config_dir, YES, &settings);
|
||||||
tr_variantFree(&settings);
|
tr_variantFree(&settings);
|
||||||
|
_fConfigDirectory = @(default_config_dir);
|
||||||
_fConfigDirectory = @(configDir);
|
tr_free(default_config_dir);
|
||||||
|
|
||||||
NSApp.delegate = self;
|
NSApp.delegate = self;
|
||||||
|
|
||||||
|
|||||||
@@ -218,7 +218,9 @@ Application::Application(int& argc, char** argv)
|
|||||||
// set the fallback config dir
|
// set the fallback config dir
|
||||||
if (config_dir.isNull())
|
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
|
// 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 StatsMode = std::string_view{ "total-ratio" };
|
||||||
auto constexpr WindowLayout = std::string_view{ "menu,toolbar,filter,list,statusbar" };
|
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);
|
tr_variantDictReserve(d, 38);
|
||||||
dictAdd(d, TR_KEY_blocklist_updates_enabled, true);
|
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_x, 50);
|
||||||
dictAdd(d, TR_KEY_main_window_y, 50);
|
dictAdd(d, TR_KEY_main_window_y, 50);
|
||||||
dictAdd(d, TR_KEY_remote_session_port, TR_DEFAULT_RPC_PORT);
|
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_filter_mode, FilterMode);
|
||||||
dictAdd(d, TR_KEY_main_window_layout_order, WindowLayout);
|
dictAdd(d, TR_KEY_main_window_layout_order, WindowLayout);
|
||||||
dictAdd(d, TR_KEY_open_dialog_dir, QDir::home().absolutePath());
|
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_remote_session_username, SessionUsername);
|
||||||
dictAdd(d, TR_KEY_sort_mode, SortMode);
|
dictAdd(d, TR_KEY_sort_mode, SortMode);
|
||||||
dictAdd(d, TR_KEY_statusbar_stats, StatsMode);
|
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);
|
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-active-requests-test.cc
|
||||||
peer-mgr-wishlist-test.cc
|
peer-mgr-wishlist-test.cc
|
||||||
peer-msgs-test.cc
|
peer-msgs-test.cc
|
||||||
|
platform-test.cc
|
||||||
quark-test.cc
|
quark-test.cc
|
||||||
remove-test.cc
|
remove-test.cc
|
||||||
rename-test.cc
|
rename-test.cc
|
||||||
|
|||||||
15
tests/libtransmission/assets/user-dirs.dirs
Normal file
15
tests/libtransmission/assets/user-dirs.dirs
Normal file
@@ -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"
|
||||||
122
tests/libtransmission/platform-test.cc
Normal file
122
tests/libtransmission/platform-test.cc
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user