// This file Copyright © Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #include #include #include #include /* isspace */ #include #include // floor #include // int64_t #include #include #include /* strcmp */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::literals; using namespace libtransmission::Values; #define SPEED_K_STR "kB/s" #define MEM_M_STR "MiB" namespace { auto constexpr DefaultPort = int{ TR_DEFAULT_RPC_PORT }; char constexpr DefaultHost[] = "localhost"; char constexpr DefaultUrl[] = TR_DEFAULT_RPC_URL_STR "rpc/"; char constexpr MyName[] = "transmission-remote"; char constexpr Usage[] = "transmission-remote " LONG_VERSION_STRING "\n" "A fast and easy BitTorrent client\n" "https://transmissionbt.com/\n" "\n" "Usage: transmission-remote [host] [options]\n" " transmission-remote [port] [options]\n" " transmission-remote [host:port] [options]\n" " transmission-remote [http(s?)://host:port/transmission/] [options]\n" "\n" "See the man page for detailed explanations and many examples."; auto constexpr Arguments = TR_KEY_arguments; struct RemoteConfig { std::string auth; std::string filter; std::string netrc; std::string session_id; std::string torrent_ids; std::string unix_socket_path; bool debug = false; bool json = false; bool use_ssl = false; }; // --- Display Utilities [[nodiscard]] std::string etaToString(int64_t eta) { if (eta < 0) { return "Unknown"; } if (eta < 60) { return fmt::format("{:d} sec", eta); } if (eta < (60 * 60)) { return fmt::format("{:d} min", eta / 60); } if (eta < (60 * 60 * 24)) { return fmt::format("{:d} hrs", eta / (60 * 60)); } if (eta < (60 * 60 * 24 * 30)) { return fmt::format("{:d} days", eta / (60 * 60 * 24)); } if (eta < (60 * 60 * 24 * 30 * 12)) { return fmt::format("{:d} months", eta / (60 * 60 * 24 * 30)); } if (eta < (60 * 60 * 24 * 365 * 1000LL)) // up to 999 years { return fmt::format("{:d} years", eta / (60 * 60 * 24 * 365)); } return "∞"; } [[nodiscard]] auto tr_strltime(time_t seconds) { if (seconds < 0) { seconds = 0; } auto const total_seconds = seconds; auto const days = seconds / 86400; auto const hours = (seconds % 86400) / 3600; auto const minutes = (seconds % 3600) / 60; seconds = (seconds % 3600) % 60; auto tmpstr = std::string{}; auto const hstr = fmt::format("{:d} {:s}", hours, tr_ngettext("hour", "hours", hours)); auto const mstr = fmt::format("{:d} {:s}", minutes, tr_ngettext("minute", "minutes", minutes)); auto const sstr = fmt::format("{:d} {:s}", seconds, tr_ngettext("seconds", "seconds", seconds)); if (days > 0) { auto const dstr = fmt::format("{:d} {:s}", days, tr_ngettext("day", "days", days)); tmpstr = days >= 4 || hours == 0 ? dstr : fmt::format("{:s}, {:s}", dstr, hstr); } else if (hours > 0) { tmpstr = hours >= 4 || minutes == 0 ? hstr : fmt::format("{:s}, {:s}", hstr, mstr); } else if (minutes > 0) { tmpstr = minutes >= 4 || seconds == 0 ? mstr : fmt::format("{:s}, {:s}", mstr, sstr); } else { tmpstr = sstr; } auto const totstr = fmt::format("{:d} {:s}", total_seconds, tr_ngettext("seconds", "seconds", total_seconds)); return fmt::format("{:s} ({:s})", tmpstr, totstr); } [[nodiscard]] auto strlpercent(double x) { return tr_strpercent(x); } [[nodiscard]] auto strlratio2(double ratio) { return tr_strratio(ratio, "Inf"); } [[nodiscard]] auto strlratio(int64_t numerator, int64_t denominator) { return strlratio2(tr_getRatio(numerator, denominator)); } [[nodiscard]] auto strlsize(int64_t bytes) { if (bytes < 0) { return "Unknown"s; } if (bytes == 0) { return "None"s; } return Storage{ bytes, Storage::Units::Bytes }.to_string(); } enum { TAG_SESSION, TAG_STATS, TAG_DETAILS, TAG_FILES, TAG_FILTER, TAG_GROUPS, TAG_LIST, TAG_PEERS, TAG_PIECES, TAG_PORTTEST, TAG_TORRENT_ADD, TAG_TRACKERS }; // --- Command-Line Arguments auto constexpr Options = std::array{ { { 'a', "add", "Add torrent files by filename or URL", "a", false, nullptr }, { 970, "alt-speed", "Use the alternate Limits", "as", false, nullptr }, { 971, "no-alt-speed", "Don't use the alternate Limits", "AS", false, nullptr }, { 972, "alt-speed-downlimit", "max alternate download speed (in " SPEED_K_STR ")", "asd", true, "" }, { 973, "alt-speed-uplimit", "max alternate upload speed (in " SPEED_K_STR ")", "asu", true, "" }, { 974, "alt-speed-scheduler", "Use the scheduled on/off times", "asc", false, nullptr }, { 975, "no-alt-speed-scheduler", "Don't use the scheduled on/off times", "ASC", false, nullptr }, { 976, "alt-speed-time-begin", "Time to start using the alt speed limits (in hhmm)", nullptr, true, "