mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-04 21:29:21 +00:00
Merge branch 'master' into feature/ipc-worker
This commit is contained in:
+3
-3
@@ -1,6 +1,6 @@
|
||||
m4_pattern_allow([PKG_CHECK_EXISTS])
|
||||
|
||||
AC_INIT([rtorrent],[0.16.20],[sundell.software@gmail.com])
|
||||
AC_INIT([rtorrent],[0.16.22],[sundell.software@gmail.com])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIRS([scripts])
|
||||
@@ -14,7 +14,7 @@ AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
|
||||
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
AC_DEFINE([API_VERSION], [25], [api version])
|
||||
AC_DEFINE([API_VERSION], [26], [api version])
|
||||
|
||||
RAK_CHECK_CFLAGS
|
||||
RAK_CHECK_CXXFLAGS
|
||||
@@ -49,7 +49,7 @@ fi
|
||||
|
||||
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.20])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.22])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
TORRENT_WITH_XMLRPC_C
|
||||
|
||||
@@ -63,6 +63,14 @@
|
||||
#network.local_address.set = 127.0.0.1
|
||||
#network.local_address.set = rakshasa.no
|
||||
|
||||
# The port reported to the tracker, separate for ipv4/ipv6. A value of 0
|
||||
# (the default) falls back to reporting the listening port. network.local_port.set
|
||||
# sets both the ipv4 and ipv6 ports at once.
|
||||
#
|
||||
#network.local_port.set = 6881
|
||||
#network.local_port.ipv4.set = 6881
|
||||
#network.local_port.ipv6.set = 6882
|
||||
|
||||
# The IP address the listening socket and outgoing connections is
|
||||
# bound to.
|
||||
#
|
||||
|
||||
@@ -195,6 +195,7 @@ libsub_root_a_SOURCES = \
|
||||
command_tracker.cc \
|
||||
command_scheduler.cc \
|
||||
command_string.cc \
|
||||
command_system.cc \
|
||||
command_ui.cc \
|
||||
control.cc \
|
||||
control.h \
|
||||
|
||||
+6
-18
@@ -142,16 +142,6 @@ apply_d_delete_tied(core::Download* download) {
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
void
|
||||
apply_d_directory(core::Download* download, const std::string& name) {
|
||||
if (!download->file_list()->is_multi_file())
|
||||
download->set_root_directory(name);
|
||||
else if (name.empty() || *name.rbegin() == '/')
|
||||
download->set_root_directory(name + download->info()->name().str());
|
||||
else
|
||||
download->set_root_directory(name + "/" + download->info()->name().str());
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_d_connection_type(core::Download* download, const std::string& name) {
|
||||
torrent::Download::ConnectionType t =
|
||||
@@ -718,7 +708,7 @@ initialize_command_download() {
|
||||
CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.open", std::bind(&core::DownloadList::open_throw, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.close", std::bind(&core::DownloadList::close_throw, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.close.directly", std::bind(&core::DownloadList::close_directly, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.close.directly", std::bind(&core::DownloadList::close_files, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.erase", std::bind(&core::DownloadList::erase_ptr, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.check_hash", std::bind(&core::DownloadList::check_hash, control->core()->download_list(), std::placeholders::_1));
|
||||
|
||||
@@ -884,12 +874,11 @@ initialize_command_download() {
|
||||
CMD2_DL_LIST ("d.tracker.insert", std::bind(&download_tracker_insert, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_VALUE_V ("d.tracker.send_scrape", [](auto download, uint64_t arg) { download->tracker_controller().scrape_request(arg); });
|
||||
|
||||
CMD2_DL ("d.directory", CMD2_ON_FL(root_dir));
|
||||
CMD2_DL ("d.directory.realpath.or_empty", [](auto* download, auto) { return resolve_path(download->file_list()->root_dir()); });
|
||||
CMD2_DL ("d.directory.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(download->file_list()->root_dir()); });
|
||||
CMD2_DL_STRING_V("d.directory.set", std::bind(&apply_d_directory, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL ("d.directory_base", CMD2_ON_FL(root_dir));
|
||||
CMD2_DL_STRING_V("d.directory_base.set", std::bind(&core::Download::set_root_directory, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL ("d.directory", CMD2_ON_FL(root_dir));
|
||||
CMD2_DL ("d.directory.realpath.or_empty", [](auto* download, auto) { return resolve_path(download->file_list()->root_dir()); });
|
||||
CMD2_DL ("d.directory.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(download->file_list()->root_dir()); });
|
||||
CMD2_DL_STRING_V("d.directory.set", [](auto* download, auto arg) { download->set_directory(arg); });
|
||||
CMD2_DL_STRING_V("d.directory.base.set", [](auto* download, auto arg) { download->set_base_directory(arg); });
|
||||
|
||||
CMD2_DL ("d.priority", std::bind(&core::Download::priority, std::placeholders::_1));
|
||||
CMD2_DL ("d.priority_str", std::bind(&retrieve_d_priority_str, std::placeholders::_1));
|
||||
@@ -950,7 +939,6 @@ initialize_command_download() {
|
||||
rpc::rpc.mark_safe("d.name.or_base64");
|
||||
rpc::rpc.mark_safe("d.name.or_as_binary");
|
||||
rpc::rpc.mark_safe("d.directory");
|
||||
rpc::rpc.mark_safe("d.directory_base");
|
||||
rpc::rpc.mark_safe("d.creation_date");
|
||||
rpc::rpc.mark_safe("d.load_date");
|
||||
rpc::rpc.mark_safe("d.up.rate");
|
||||
|
||||
+23
-5
@@ -51,7 +51,7 @@ apply_on_ratio(const torrent::Object& rawArgs) {
|
||||
!(max_ratio > 0 && total_upload * 100 > total_done * max_ratio))
|
||||
continue;
|
||||
|
||||
downloads.push_back(*itr);
|
||||
downloads.push_back(itr->get());
|
||||
}
|
||||
|
||||
auto ratio_command = "group." + group_name + ".ratio.command";
|
||||
@@ -173,7 +173,7 @@ apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
|
||||
|
||||
torrent::FileList::cache_list cache;
|
||||
|
||||
for (auto download : *control->core()->download_list()) {
|
||||
for (const auto& download : *control->core()->download_list()) {
|
||||
if (!download->is_downloading())
|
||||
continue;
|
||||
if (download->priority() >= skip_priority)
|
||||
@@ -240,15 +240,25 @@ d_multicall(const torrent::Object::list_type& args) {
|
||||
|
||||
// Add some pre-parsing of the commands, so we don't spend time
|
||||
// parsing and searching command map for every single call.
|
||||
std::vector<core::Download*> dlist((*view_itr)->begin_visible(), (*view_itr)->end_visible());
|
||||
// Hold a reference to each download so a command that erases one does not
|
||||
// leave the rest of the loop dispatching on freed memory.
|
||||
core::View::base_type dlist((*view_itr)->begin_visible(), (*view_itr)->end_visible());
|
||||
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
|
||||
for (auto download : dlist) {
|
||||
for (const auto& download : dlist) {
|
||||
if (download.use_count() == 1)
|
||||
continue;
|
||||
|
||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
|
||||
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||
// A command may erase this download, which destroys the torrent object it
|
||||
// wraps; the list dropping its reference is what tells us.
|
||||
if (download.use_count() == 1)
|
||||
break;
|
||||
|
||||
auto& cmd = cItr->as_string();
|
||||
row.push_back(rpc::parse_command(rpc::make_target(download), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||
}
|
||||
@@ -271,10 +281,12 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
|
||||
if (view_itr == viewManager->end())
|
||||
throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
|
||||
|
||||
// Make a filtered copy of the current item list
|
||||
// Make a filtered copy of the current item list, holding a reference to each
|
||||
// download so a command that erases one cannot free it under us.
|
||||
core::View::base_type dlist;
|
||||
(*view_itr)->filter_by(*++arg, dlist);
|
||||
|
||||
|
||||
// Generate result by iterating over all items
|
||||
auto resultRaw = torrent::Object::create_list();
|
||||
auto& result = resultRaw.as_list();
|
||||
@@ -282,11 +294,17 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
|
||||
++arg; // skip to first command
|
||||
|
||||
for (const auto& item : dlist) {
|
||||
if (item.use_count() == 1)
|
||||
continue;
|
||||
|
||||
// Add empty row to result
|
||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
|
||||
// Call the provided commands and assemble their results
|
||||
for (torrent::Object::list_const_iterator command = arg; command != args.end(); command++) {
|
||||
if (item.use_count() == 1)
|
||||
break;
|
||||
|
||||
auto& cmdstr = command->as_string();
|
||||
row.push_back(rpc::parse_command(rpc::make_target(item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ void initialize_command_throttle();
|
||||
void initialize_command_tracker();
|
||||
void initialize_command_scheduler();
|
||||
void initialize_command_string();
|
||||
void initialize_command_system();
|
||||
void initialize_command_ui();
|
||||
|
||||
void
|
||||
@@ -39,4 +40,5 @@ initialize_commands() {
|
||||
initialize_command_tracker();
|
||||
initialize_command_scheduler();
|
||||
initialize_command_string();
|
||||
initialize_command_system();
|
||||
}
|
||||
|
||||
+4
-1
@@ -131,7 +131,7 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
|
||||
// copy everything up to '#' to address_copy and work from there
|
||||
while(address[address_start_index] != '#' && address[address_start_index] != '\r' &&
|
||||
address[address_start_index] != '\n' && address[address_start_index] != '\0' &&
|
||||
address_start_index < 4096 ) {
|
||||
address_start_index < 4095 ) {
|
||||
|
||||
address_copy[address_start_index] = address[address_start_index];
|
||||
address_start_index++;
|
||||
@@ -206,6 +206,9 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
|
||||
if (mask_bits == 0) {
|
||||
mask = 0;
|
||||
end_mask = ~(uint32_t)0;
|
||||
} else if (mask_bits == 32) {
|
||||
mask = ~(uint32_t)0;
|
||||
end_mask = 0;
|
||||
} else {
|
||||
mask = (~mask) << (32-mask_bits);
|
||||
end_mask = (~end_mask) >> mask_bits;
|
||||
|
||||
+8
-174
@@ -1,33 +1,16 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
#include <functional>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/data/file_manager.h>
|
||||
#include <torrent/data/chunk_utils.h>
|
||||
#include <torrent/runtime/runtime.h>
|
||||
#include <torrent/runtime/memory_manager.h>
|
||||
#include <torrent/runtime/socket_manager.h>
|
||||
#include <torrent/utils/chrono.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "command_helpers.h"
|
||||
#include "control.h"
|
||||
#include "core/download.h"
|
||||
#include "core/download_list.h"
|
||||
#include "core/manager.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "rpc/lua.h"
|
||||
#include "rpc/scgi.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "utils/file_status_cache.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "rpc/lua.h"
|
||||
#include "control.h"
|
||||
#include "command_helpers.h"
|
||||
|
||||
typedef torrent::ChunkManager CM_t;
|
||||
typedef torrent::FileManager FM_t;
|
||||
@@ -43,49 +26,6 @@ apply_pieces_stats_total_size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_env(const torrent::Object::string_type& arg) {
|
||||
if (arg.empty())
|
||||
throw torrent::input_error("system.env: Missing variable name.");
|
||||
|
||||
char* val = getenv(arg.c_str());
|
||||
return std::string(val ? val : "");
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_hostname() {
|
||||
char buffer[1024];
|
||||
|
||||
if (gethostname(buffer, 1023) == -1)
|
||||
throw torrent::input_error("Unable to read hostname.");
|
||||
|
||||
// if (shorten)
|
||||
// *std::find(buffer, buffer + 1023, '.') = '\0';
|
||||
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_get_cwd() {
|
||||
char* buffer = getcwd(NULL, 0);
|
||||
|
||||
if (buffer == NULL)
|
||||
throw torrent::input_error("Unable to read cwd.");
|
||||
|
||||
torrent::Object result = torrent::Object(std::string(buffer));
|
||||
free(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_set_cwd(const torrent::Object::string_type& rawArgs) {
|
||||
if (::chdir(rawArgs.c_str()) != 0)
|
||||
throw torrent::input_error("Could not change current working directory.");
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
inline torrent::Object::list_const_iterator
|
||||
post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object::list_const_iterator& last) {
|
||||
if (itr == last)
|
||||
@@ -187,94 +127,17 @@ cmd_file_append(const torrent::Object::list_type& args) {
|
||||
|
||||
void
|
||||
initialize_command_local() {
|
||||
core::DownloadList* dList = control->core()->download_list();
|
||||
torrent::FileManager* fileManager = torrent::file_manager();
|
||||
|
||||
CMD_ANY ("system.hostname", std::bind(&system_hostname));
|
||||
CMD_ANY ("system.pid", std::bind(&getpid));
|
||||
|
||||
CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
|
||||
CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
|
||||
CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
|
||||
|
||||
CMD_VAR_VALUE ("system.file.allocate", 0);
|
||||
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
|
||||
CMD_VAR_VALUE ("system.file.split_size", -1);
|
||||
CMD_VAR_STRING ("system.file.split_suffix", ".part");
|
||||
|
||||
CMD_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
|
||||
(utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
|
||||
CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
|
||||
|
||||
CMD_VAR_BOOL ("file.prioritize_toc", 0);
|
||||
CMD_VAR_LIST ("file.prioritize_toc.first");
|
||||
CMD_VAR_LIST ("file.prioritize_toc.last");
|
||||
|
||||
CMD_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager));
|
||||
CMD_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2));
|
||||
CMD_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager));
|
||||
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2));
|
||||
CMD_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
|
||||
CMD_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
|
||||
|
||||
CMD_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager));
|
||||
CMD_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager));
|
||||
CMD_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager));
|
||||
|
||||
CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
|
||||
|
||||
CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
|
||||
CMD_ANY ("system.time_seconds", [](auto, auto) { return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count(); });
|
||||
CMD_ANY ("system.time_usec", [](auto, auto) { return torrent::utils::time_since_epoch().count(); });
|
||||
|
||||
CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
|
||||
|
||||
CMD_VAR_BOOL ("system.daemon", false);
|
||||
|
||||
CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
|
||||
CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
|
||||
|
||||
CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
|
||||
|
||||
CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
|
||||
CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
|
||||
|
||||
CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
|
||||
CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
|
||||
CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(value); });
|
||||
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
|
||||
CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
|
||||
CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
|
||||
|
||||
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
||||
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
|
||||
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
||||
|
||||
CMD_ANY (category_name + ".size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_managed_size(category); });
|
||||
CMD_ANY (category_name + ".max_size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_size(category); });
|
||||
|
||||
if (i == 0) {
|
||||
CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
|
||||
continue;
|
||||
}
|
||||
|
||||
CMD_ANY (category_name + ".max_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_limit(category); });
|
||||
CMD_ANY (category_name + ".min_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_minimum(category); });
|
||||
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
|
||||
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
|
||||
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_min_allocation(category, value); });
|
||||
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_max_allocation(category, value); });
|
||||
}
|
||||
core::DownloadList* dList = control->core()->download_list();
|
||||
|
||||
CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); });
|
||||
CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); });
|
||||
CMD_ANY ("pieces.sync.safe_free_diskspace", [](auto, auto) { return torrent::runtime::memory_manager()->sync_safe_free_diskspace(); });
|
||||
CMD_ANY ("pieces.sync.timeout", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_sync().count(); });
|
||||
CMD_ANY_VALUE_V ("pieces.sync.timeout.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_sync(value); });
|
||||
// CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_safe_sync(); });
|
||||
// CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_safe_sync(value); });
|
||||
CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return 0; });
|
||||
CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto) { });
|
||||
CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto) {
|
||||
lt_log_print(torrent::LOG_WARN, "pieces.sync.timeout_safe.set is no longer supported and does nothing.");
|
||||
});
|
||||
CMD_ANY ("pieces.sync.queue_size", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_block_count(); });
|
||||
|
||||
CMD_ANY ("pieces.preload.type", [](auto, auto) { return torrent::runtime::memory_manager()->preload_type(); });
|
||||
@@ -349,35 +212,6 @@ initialize_command_local() {
|
||||
|
||||
CMD_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
|
||||
|
||||
rpc::rpc.mark_safe("system.api_version");
|
||||
rpc::rpc.mark_safe("system.client_version");
|
||||
rpc::rpc.mark_safe("system.library_version");
|
||||
rpc::rpc.mark_safe("system.file.max_size");
|
||||
rpc::rpc.mark_safe("system.file.split_size");
|
||||
rpc::rpc.mark_safe("system.file.split_suffix");
|
||||
|
||||
rpc::rpc.mark_safe("system.sockets.size");
|
||||
rpc::rpc.mark_safe("system.sockets.max_size");
|
||||
rpc::rpc.mark_safe("system.sockets.reserved_alloc");
|
||||
rpc::rpc.mark_safe("system.sockets.available_alloc");
|
||||
|
||||
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
||||
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
||||
|
||||
rpc::rpc.mark_safe(category_name + ".size");
|
||||
rpc::rpc.mark_safe(category_name + ".max_size");
|
||||
|
||||
if (i == 0) {
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
||||
continue;
|
||||
}
|
||||
|
||||
rpc::rpc.mark_safe(category_name + ".max_alloc.limit");
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc.limit");
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
||||
rpc::rpc.mark_safe(category_name + ".max_alloc");
|
||||
}
|
||||
|
||||
rpc::rpc.mark_safe("directory.default");
|
||||
rpc::rpc.mark_safe("session.path");
|
||||
rpc::rpc.mark_safe("session.path.realpath.or_empty");
|
||||
|
||||
@@ -135,7 +135,7 @@ initialize_command_logging() {
|
||||
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_file.flush", std::bind(&apply_log_open, log_flag_append_file | log_flag_flush, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file | log_flag_use_gz, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
|
||||
|
||||
|
||||
@@ -54,6 +54,14 @@ set_listen_port_range(const std::string& arg) {
|
||||
torrent::runtime::client_config()->set_listen_port_range(port_first, port_last);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
checked_local_port_value(int64_t value, const char* label) {
|
||||
if (value < 0 || value > 65535)
|
||||
throw torrent::input_error(std::string("Invalid ") + label + " port number.");
|
||||
|
||||
return static_cast<uint16_t>(value);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
get_encryption() {
|
||||
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
|
||||
@@ -379,6 +387,13 @@ initialize_command_network() {
|
||||
CMD_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_str(); });
|
||||
CMD_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); });
|
||||
|
||||
CMD_ANY ("network.local_port", [nw_config](auto, auto) { return nw_config->local_port_best_match(); });
|
||||
CMD_ANY_VALUE_V ("network.local_port.set", [nw_config](auto, auto& value) { return nw_config->set_local_port(checked_local_port_value(value, "local")); });
|
||||
CMD_ANY ("network.local_port.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_port(); });
|
||||
CMD_ANY_VALUE_V ("network.local_port.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_local_inet_port(checked_local_port_value(value, "local ipv4")); });
|
||||
CMD_ANY ("network.local_port.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_port(); });
|
||||
CMD_ANY_VALUE_V ("network.local_port.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_local_inet6_port(checked_local_port_value(value, "local ipv6")); });
|
||||
|
||||
CMD_ANY ("network.proxy.global", [](auto, auto) { return torrent::runtime::proxy_manager()->proxy_url(); });
|
||||
CMD_ANY_STRING_V("network.proxy.global.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_proxy_url(str); });
|
||||
CMD_ANY ("network.proxy.http", [](auto, auto) { return torrent::runtime::proxy_manager()->http_proxy_url(); });
|
||||
@@ -437,6 +452,9 @@ initialize_command_network() {
|
||||
rpc::rpc.mark_safe("network.receive_buffer.size");
|
||||
rpc::rpc.mark_safe("network.bind_address");
|
||||
rpc::rpc.mark_safe("network.local_address");
|
||||
rpc::rpc.mark_safe("network.local_port");
|
||||
rpc::rpc.mark_safe("network.local_port.ipv4");
|
||||
rpc::rpc.mark_safe("network.local_port.ipv6");
|
||||
rpc::rpc.mark_safe("network.xmlrpc.size_limit");
|
||||
rpc::rpc.mark_safe("network.open_sockets");
|
||||
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <torrent/data/file_manager.h>
|
||||
#include <torrent/runtime/client_config.h>
|
||||
#include <torrent/runtime/memory_manager.h>
|
||||
#include <torrent/runtime/socket_manager.h>
|
||||
#include <torrent/runtime/runtime.h>
|
||||
#include <torrent/utils/chrono.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
|
||||
#include "command_helpers.h"
|
||||
#include "control.h"
|
||||
#include "globals.h"
|
||||
#include "core/manager.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "utils/file_status_cache.h"
|
||||
|
||||
|
||||
torrent::Object
|
||||
system_env(const torrent::Object::string_type& arg) {
|
||||
if (arg.empty())
|
||||
throw torrent::input_error("system.env: Missing variable name.");
|
||||
|
||||
char* val = getenv(arg.c_str());
|
||||
return std::string(val ? val : "");
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_hostname() {
|
||||
char buffer[1024];
|
||||
|
||||
if (gethostname(buffer, 1023) == -1)
|
||||
throw torrent::input_error("Unable to read hostname.");
|
||||
|
||||
// if (shorten)
|
||||
// *std::find(buffer, buffer + 1023, '.') = '\0';
|
||||
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_get_cwd() {
|
||||
char* buffer = getcwd(NULL, 0);
|
||||
|
||||
if (buffer == NULL)
|
||||
throw torrent::input_error("Unable to read cwd.");
|
||||
|
||||
torrent::Object result = torrent::Object(std::string(buffer));
|
||||
free(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_set_cwd(const torrent::Object::string_type& rawArgs) {
|
||||
if (::chdir(rawArgs.c_str()) != 0)
|
||||
throw torrent::input_error("Could not change current working directory.");
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
checked_socket_value(int64_t value, const char* label) {
|
||||
if (value < 0 || value > std::numeric_limits<uint32_t>::max())
|
||||
throw torrent::input_error(std::string("Invalid ") + label + " value.");
|
||||
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
void
|
||||
initialize_command_system() {
|
||||
CMD_ANY ("system.hostname", std::bind(&system_hostname));
|
||||
CMD_ANY ("system.pid", std::bind(&getpid));
|
||||
|
||||
CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
|
||||
CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
|
||||
CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
|
||||
|
||||
CMD_ANY ("system.torrent_name.use_sanitized", [](auto, auto) { return torrent::runtime::client_config()->torrent_name_use_sanitized(); });
|
||||
CMD_ANY_VALUE_V ("system.torrent_name.use_sanitized.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_torrent_name_use_sanitized(value); });
|
||||
|
||||
CMD_VAR_VALUE ("system.file.allocate", 0);
|
||||
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
|
||||
CMD_VAR_VALUE ("system.file.split_size", -1);
|
||||
CMD_VAR_STRING ("system.file.split_suffix", ".part");
|
||||
|
||||
CMD_ANY ("system.file_name.replace_slash", [](auto, auto) { return torrent::runtime::client_config()->file_name_replace_slash(); });
|
||||
CMD_ANY_STRING_V("system.file_name.replace_slash.set", [](auto, auto& str) { return torrent::runtime::client_config()->set_file_name_replace_slash(str); });
|
||||
|
||||
CMD_ANY ("system.file_status_cache.size", [](auto, auto) { return control->core()->file_status_cache()->size(); });
|
||||
CMD_ANY_V ("system.file_status_cache.prune", [](auto, auto) { return control->core()->file_status_cache()->prune(); });
|
||||
|
||||
CMD_VAR_BOOL ("file.prioritize_toc", 0);
|
||||
CMD_VAR_LIST ("file.prioritize_toc.first");
|
||||
CMD_VAR_LIST ("file.prioritize_toc.last");
|
||||
|
||||
CMD_ANY ("system.files.advise_random", [](auto, auto) { return torrent::file_manager()->advise_random(); });
|
||||
CMD_ANY_VALUE_V ("system.files.advise_random.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random(value); });
|
||||
CMD_ANY ("system.files.advise_random.hashing", [](auto, auto) { return torrent::file_manager()->advise_random_hashing(); });
|
||||
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random_hashing(value); });
|
||||
CMD_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
|
||||
CMD_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
|
||||
|
||||
CMD_ANY ("system.files.close_idle", [](auto, auto) { return torrent::file_manager()->close_idle_timeout().count(); });
|
||||
CMD_ANY_VALUE_V ("system.files.close_idle.set", [](auto, auto& value) { return torrent::file_manager()->set_close_idle_timeout(value * 1s); });
|
||||
|
||||
CMD_ANY ("system.files.opened_counter", [](auto, auto) { return torrent::file_manager()->files_opened_counter(); });
|
||||
CMD_ANY ("system.files.closed_counter", [](auto, auto) { return torrent::file_manager()->files_closed_counter(); });
|
||||
CMD_ANY ("system.files.failed_counter", [](auto, auto) { return torrent::file_manager()->files_failed_counter(); });
|
||||
|
||||
CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
|
||||
|
||||
CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
|
||||
CMD_ANY ("system.time_seconds", [](auto, auto) { return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count(); });
|
||||
CMD_ANY ("system.time_usec", [](auto, auto) { return torrent::utils::time_since_epoch().count(); });
|
||||
|
||||
CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
|
||||
|
||||
CMD_VAR_BOOL ("system.daemon", false);
|
||||
|
||||
CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
|
||||
CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
|
||||
|
||||
CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
|
||||
|
||||
CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
|
||||
CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
|
||||
|
||||
CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
|
||||
CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
|
||||
CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) {
|
||||
return torrent::runtime::socket_manager()->set_max_size_and_adjust(checked_socket_value(value, "socket max size"));
|
||||
});
|
||||
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
|
||||
CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
|
||||
CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
|
||||
|
||||
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
||||
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
|
||||
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
||||
|
||||
CMD_ANY (category_name + ".size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_managed_size(category); });
|
||||
CMD_ANY (category_name + ".max_size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_size(category); });
|
||||
|
||||
if (i == 0) {
|
||||
CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
|
||||
continue;
|
||||
}
|
||||
|
||||
CMD_ANY (category_name + ".max_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_limit(category); });
|
||||
CMD_ANY (category_name + ".min_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_minimum(category); });
|
||||
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
|
||||
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
|
||||
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) {
|
||||
torrent::runtime::socket_manager()->set_category_min_allocation(category, checked_socket_value(value, "socket min alloc"));
|
||||
});
|
||||
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) {
|
||||
torrent::runtime::socket_manager()->set_category_max_allocation(category, checked_socket_value(value, "socket max alloc"));
|
||||
});
|
||||
}
|
||||
|
||||
rpc::rpc.mark_safe("system.api_version");
|
||||
rpc::rpc.mark_safe("system.client_version");
|
||||
rpc::rpc.mark_safe("system.library_version");
|
||||
rpc::rpc.mark_safe("system.time");
|
||||
rpc::rpc.mark_safe("system.time_seconds");
|
||||
rpc::rpc.mark_safe("system.time_usec");
|
||||
rpc::rpc.mark_safe("system.file.max_size");
|
||||
rpc::rpc.mark_safe("system.file.split_size");
|
||||
rpc::rpc.mark_safe("system.file.split_suffix");
|
||||
|
||||
rpc::rpc.mark_safe("system.sockets.size");
|
||||
rpc::rpc.mark_safe("system.sockets.max_size");
|
||||
rpc::rpc.mark_safe("system.sockets.reserved_alloc");
|
||||
rpc::rpc.mark_safe("system.sockets.available_alloc");
|
||||
|
||||
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
||||
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
||||
|
||||
rpc::rpc.mark_safe(category_name + ".size");
|
||||
rpc::rpc.mark_safe(category_name + ".max_size");
|
||||
|
||||
if (i == 0) {
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
||||
continue;
|
||||
}
|
||||
|
||||
rpc::rpc.mark_safe(category_name + ".max_alloc.limit");
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc.limit");
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
||||
rpc::rpc.mark_safe(category_name + ".max_alloc");
|
||||
}
|
||||
}
|
||||
@@ -68,11 +68,11 @@ apply_dht_add_node(const std::string& arg) {
|
||||
torrent::Object
|
||||
apply_enable_trackers(int64_t arg) {
|
||||
if (arg == 0) {
|
||||
for (auto download : *control->core()->download_list())
|
||||
for (const auto& download : *control->core()->download_list())
|
||||
download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
|
||||
|
||||
} else {
|
||||
for (auto download : *control->core()->download_list())
|
||||
for (const auto& download : *control->core()->download_list())
|
||||
download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -3,6 +3,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <regex>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
@@ -407,8 +408,9 @@ apply_to_time(const torrent::Object& rawArgs, int flags) {
|
||||
torrent::Object
|
||||
apply_to_elapsed_time(const torrent::Object& rawArgs) {
|
||||
auto cached_seconds = torrent::this_thread::cached_seconds().count();
|
||||
auto value = rawArgs.as_value();
|
||||
|
||||
uint64_t arg = cached_seconds - rawArgs.as_value();
|
||||
uint64_t arg = value >= 0 && value <= cached_seconds ? cached_seconds - value : 0;
|
||||
|
||||
char buffer[48];
|
||||
snprintf(buffer, 48, "%2d:%02d:%02d", (int)(arg / 3600), (int)((arg / 60) % 60), (int)(arg % 60));
|
||||
@@ -674,8 +676,13 @@ apply_math_basic(const char* name, Comp op, const torrent::Object::list_type& ar
|
||||
throw torrent::input_error(std::string(name) + ": Wrong argument type");
|
||||
}
|
||||
|
||||
if (divides && !rhs && itr != args.begin())
|
||||
throw torrent::input_error(std::string(name) + ": Division by zero!");
|
||||
if (divides && itr != args.begin()) {
|
||||
if (rhs == 0)
|
||||
throw torrent::input_error(std::string(name) + ": Division by zero!");
|
||||
|
||||
if (val == std::numeric_limits<int64_t>::min() && rhs == -1)
|
||||
throw torrent::input_error(std::string(name) + ": Division overflow!");
|
||||
}
|
||||
|
||||
val = itr == args.begin() ? rhs : op(val, rhs);
|
||||
|
||||
@@ -916,6 +923,7 @@ initialize_command_ui() {
|
||||
|
||||
rpc::rpc.mark_safe("view.set_visible");
|
||||
rpc::rpc.mark_safe("view.set_not_visible");
|
||||
rpc::rpc.mark_safe("view.list");
|
||||
|
||||
rpc::rpc.mark_safe("cat");
|
||||
rpc::rpc.mark_safe("if");
|
||||
|
||||
@@ -118,4 +118,6 @@ private:
|
||||
std::atomic<bool> m_shutdown_quick{};
|
||||
};
|
||||
|
||||
extern Control* control;
|
||||
|
||||
#endif
|
||||
|
||||
+13
-3
@@ -101,7 +101,18 @@ Download::set_throttle_name(const std::string& name) {
|
||||
}
|
||||
|
||||
void
|
||||
Download::set_root_directory(const std::string& path) {
|
||||
Download::set_directory(const std::string& path) {
|
||||
if (!m_download.file_list()->is_multi_file())
|
||||
return set_base_directory(path);
|
||||
|
||||
if (path.empty() || *path.rbegin() == '/')
|
||||
return set_base_directory(path + m_download.info()->name_sanitized());
|
||||
|
||||
set_base_directory(path + "/" + m_download.info()->name_sanitized());
|
||||
}
|
||||
|
||||
void
|
||||
Download::set_base_directory(const std::string& path) {
|
||||
// If the download is open, hashed and has completed chunks make
|
||||
// sure to verify that the download files are still present.
|
||||
//
|
||||
@@ -120,13 +131,12 @@ Download::set_root_directory(const std::string& path) {
|
||||
!file_stat.update(file_list->front()->frozen_path().str()))) {
|
||||
|
||||
set_message("Cannot change the directory of an open download after the files have been moved.");
|
||||
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this));
|
||||
control->core()->download_list()->close_directly(this);
|
||||
|
||||
throw torrent::input_error("Cannot change the directory of an open download after the files have been moved.");
|
||||
}
|
||||
|
||||
control->core()->download_list()->close_directly(this);
|
||||
control->core()->download_list()->close_files(this);
|
||||
file_list->set_root_dir(expand_path(path));
|
||||
|
||||
bencode()->get_key("rtorrent").insert_key("directory", path);
|
||||
|
||||
+3
-1
@@ -1,6 +1,7 @@
|
||||
#ifndef RTORRENT_CORE_DOWNLOAD_H
|
||||
#define RTORRENT_CORE_DOWNLOAD_H
|
||||
|
||||
#include <memory>
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/download.h>
|
||||
#include <torrent/download_info.h>
|
||||
@@ -78,7 +79,8 @@ public:
|
||||
uint32_t resume_flags() { return m_resumeFlags; }
|
||||
void set_resume_flags(uint32_t flags) { m_resumeFlags = flags; }
|
||||
|
||||
void set_root_directory(const std::string& path);
|
||||
void set_directory(const std::string& path);
|
||||
void set_base_directory(const std::string& path);
|
||||
|
||||
void set_throttle_name(const std::string& name);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <sys/stat.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/resume.h>
|
||||
#include <torrent/object.h>
|
||||
@@ -39,6 +40,11 @@ static constexpr const char* session_invalid_message = "Session data is invalid,
|
||||
|
||||
static std::unique_ptr<torrent::Object>
|
||||
download_factory_load_stream(const char* filename, bool* is_invalid) {
|
||||
struct stat sb;
|
||||
|
||||
if (stat(filename, &sb) != 0 || !S_ISREG(sb.st_mode))
|
||||
return std::unique_ptr<torrent::Object>();
|
||||
|
||||
std::fstream stream(filename, std::ios::in | std::ios::binary);
|
||||
|
||||
if (!stream.is_open())
|
||||
@@ -167,11 +173,19 @@ void
|
||||
DownloadFactory::receive_success() {
|
||||
bool session_invalid = false;
|
||||
|
||||
auto rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str(), &session_invalid);
|
||||
auto libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str(), &session_invalid);
|
||||
std::unique_ptr<torrent::Object> rtorrent_object;
|
||||
std::unique_ptr<torrent::Object> libtorrent_resume_object;
|
||||
|
||||
if (session_invalid)
|
||||
lt_log_print(torrent::LOG_ERROR, "%s: %s", session_invalid_message, m_uri.c_str());
|
||||
if (m_session) {
|
||||
if (m_uri.empty())
|
||||
throw torrent::input_error("Session torrent URI is empty.");
|
||||
|
||||
rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str(), &session_invalid);
|
||||
libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str(), &session_invalid);
|
||||
|
||||
if (session_invalid)
|
||||
lt_log_print(torrent::LOG_ERROR, "%s: %s", session_invalid_message, m_uri.c_str());
|
||||
}
|
||||
|
||||
uint32_t tracker_key;
|
||||
|
||||
|
||||
+54
-24
@@ -37,7 +37,7 @@ namespace core {
|
||||
inline void
|
||||
DownloadList::check_contains([[maybe_unused]] Download* d) {
|
||||
#ifdef USE_EXTRA_DEBUG
|
||||
if (std::find(begin(), end(), d) == end())
|
||||
if (std::find_if(begin(), end(), [d](const auto& entry) { return entry.get() == d; }) == end())
|
||||
throw torrent::internal_error("DownloadList::check_contains(...) failed.");
|
||||
#endif
|
||||
}
|
||||
@@ -54,7 +54,6 @@ DownloadList::clear() {
|
||||
base_type::pop_back();
|
||||
|
||||
torrent::download_remove(*download->download());
|
||||
delete download;
|
||||
|
||||
} catch (torrent::internal_error& e) {
|
||||
lt_log_print(torrent::LOG_ERROR, "DownloadList::clear() failed to close or remove download: %s", e.what());
|
||||
@@ -70,7 +69,7 @@ DownloadList::clear() {
|
||||
void
|
||||
DownloadList::session_save() {
|
||||
for (auto& download : *this)
|
||||
session_thread::manager()->save_resume_download(download);
|
||||
session_thread::manager()->save_resume_download(download.get());
|
||||
|
||||
control->dht_manager()->save_dht_cache();
|
||||
control->ui()->save_input_history();
|
||||
@@ -78,7 +77,7 @@ DownloadList::session_save() {
|
||||
|
||||
DownloadList::iterator
|
||||
DownloadList::find(const torrent::HashString& hash) {
|
||||
return std::find_if(begin(), end(), [hash](Download* d) { return hash == d->info()->hash(); });
|
||||
return std::find_if(begin(), end(), [hash](const auto& d) { return hash == d->info()->hash(); });
|
||||
}
|
||||
|
||||
DownloadList::iterator
|
||||
@@ -91,14 +90,14 @@ DownloadList::find_hex(const char* hash) {
|
||||
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
|
||||
return end();
|
||||
|
||||
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
|
||||
return std::find_if(begin(), end(), [key](const auto& d) { return key == d->info()->hash(); });
|
||||
}
|
||||
|
||||
Download*
|
||||
DownloadList::find_hex_ptr(const char* hash) {
|
||||
iterator itr = find_hex(hash);
|
||||
|
||||
return itr != end() ? *itr : NULL;
|
||||
return itr != end() ? itr->get() : NULL;
|
||||
}
|
||||
|
||||
Download*
|
||||
@@ -159,7 +158,7 @@ DownloadList::create(std::istream* str, uint32_t tracker_key, bool printLog) {
|
||||
|
||||
DownloadList::iterator
|
||||
DownloadList::insert(Download* download) {
|
||||
iterator itr = base_type::insert(end(), download);
|
||||
iterator itr = base_type::insert(end(), std::shared_ptr<Download>(download));
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Inserting download.");
|
||||
|
||||
@@ -170,7 +169,7 @@ DownloadList::insert(Download* download) {
|
||||
// This needs to be separated into two different calls to ensure
|
||||
// the download remains in the view.
|
||||
for (auto v : *control->view_manager())
|
||||
v->insert(download);
|
||||
v->insert(*itr);
|
||||
for (auto v : *control->view_manager())
|
||||
v->filter_download(download);
|
||||
|
||||
@@ -187,7 +186,7 @@ DownloadList::insert(Download* download) {
|
||||
|
||||
void
|
||||
DownloadList::erase_ptr(Download* download) {
|
||||
erase(std::find(begin(), end(), download));
|
||||
erase(std::find_if(begin(), end(), [download](const auto& entry) { return entry.get() == download; }));
|
||||
}
|
||||
|
||||
DownloadList::iterator
|
||||
@@ -201,15 +200,14 @@ DownloadList::erase(iterator itr) {
|
||||
(*itr)->set_hash_failed(true);
|
||||
|
||||
close(*itr);
|
||||
session_thread::manager()->remove_download(*itr);
|
||||
session_thread::manager()->remove_download(itr->get());
|
||||
|
||||
DL_TRIGGER_EVENT(*itr, "event.download.erased");
|
||||
|
||||
for (auto v : *control->view_manager())
|
||||
v->erase(*itr);
|
||||
v->erase(itr->get());
|
||||
|
||||
torrent::download_remove(*(*itr)->download());
|
||||
delete *itr;
|
||||
|
||||
return base_type::erase(itr);
|
||||
}
|
||||
@@ -256,9 +254,11 @@ DownloadList::close(Download* download) {
|
||||
}
|
||||
}
|
||||
|
||||
// Releases the files without changing the download's state, for callers that
|
||||
// need the files closed and will keep using the download.
|
||||
void
|
||||
DownloadList::close_directly(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
|
||||
DownloadList::close_files(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download files.");
|
||||
|
||||
if (download->download()->info()->is_active()) {
|
||||
download->download()->stop(torrent::Download::stop_skip_tracker);
|
||||
@@ -271,6 +271,44 @@ DownloadList::close_directly(Download* download) {
|
||||
download->download()->close();
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::close_directly(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
|
||||
|
||||
bool was_active = download->download()->info()->is_active();
|
||||
bool was_open = download->download()->info()->is_open();
|
||||
|
||||
close_files(download);
|
||||
set_state_stopped(download);
|
||||
|
||||
if (was_active) {
|
||||
DL_TRIGGER_EVENT(download, "event.download.paused");
|
||||
update_paused_state(download);
|
||||
}
|
||||
|
||||
if (was_open) {
|
||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
||||
DL_TRIGGER_EVENT(download, "event.download.closed");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::set_state_stopped(Download* download) {
|
||||
control->view_manager()->find_ptr_throw("stopped")->set_visible(download);
|
||||
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(download));
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::update_paused_state(Download* download) {
|
||||
rpc::call_command("d.state_changed.set", torrent::this_thread::cached_seconds().count(), rpc::make_target(download));
|
||||
rpc::call_command("d.state_counter.set", rpc::call_command_value("d.state_counter", rpc::make_target(download)), rpc::make_target(download));
|
||||
|
||||
// If initial seeding is complete, don't try it again when restarting.
|
||||
if (download->is_done() &&
|
||||
rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)).as_string() == "initial_seed")
|
||||
rpc::call_command("d.connection_seed.set", rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)), rpc::make_target(download));
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::close_quick(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download quickly.");
|
||||
@@ -445,15 +483,7 @@ DownloadList::pause(Download* download, int flags) {
|
||||
// view.
|
||||
DL_TRIGGER_EVENT(download, "event.download.paused");
|
||||
|
||||
auto cached_seconds = torrent::this_thread::cached_seconds().count();
|
||||
|
||||
rpc::call_command("d.state_changed.set", cached_seconds, rpc::make_target(download));
|
||||
rpc::call_command("d.state_counter.set", rpc::call_command_value("d.state_counter", rpc::make_target(download)), rpc::make_target(download));
|
||||
|
||||
// If initial seeding is complete, don't try it again when restarting.
|
||||
if (download->is_done() &&
|
||||
rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)).as_string() == "initial_seed")
|
||||
rpc::call_command("d.connection_seed.set", rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)), rpc::make_target(download));
|
||||
update_paused_state(download);
|
||||
|
||||
// Save the state after all the slots, etc have been called so we
|
||||
// include the modifications they may make.
|
||||
@@ -524,7 +554,7 @@ DownloadList::hash_done(Download* download) {
|
||||
// If the download was previously completed but the files were
|
||||
// f.ex deleted, then we clear the state and complete.
|
||||
if (rpc::call_command_value("d.complete", rpc::make_target(download)) && !download->is_done()) {
|
||||
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(download));
|
||||
set_state_stopped(download);
|
||||
download->set_message("Download registered as completed, but hash check returned unfinished chunks.");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <iosfwd>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace torrent {
|
||||
@@ -20,9 +21,9 @@ class Download;
|
||||
//
|
||||
// Fix apply_on_ratio if the base_type is changed.
|
||||
|
||||
class DownloadList : private std::list<Download*> {
|
||||
class DownloadList : private std::list<std::shared_ptr<Download>> {
|
||||
public:
|
||||
typedef std::list<Download*> base_type;
|
||||
typedef std::list<std::shared_ptr<Download>> base_type;
|
||||
|
||||
using base_type::iterator;
|
||||
using base_type::const_iterator;
|
||||
@@ -64,8 +65,17 @@ public:
|
||||
bool open(Download* d);
|
||||
void open_throw(Download* d);
|
||||
|
||||
// Overloads for the list's own entries, so callers iterating it do not need
|
||||
// to unwrap.
|
||||
bool open(const value_type& d) { return open(d.get()); }
|
||||
void close(const value_type& d) { close(d.get()); }
|
||||
void close_quick(const value_type& d) { close_quick(d.get()); }
|
||||
void pause(const value_type& d) { pause(d.get()); }
|
||||
void resume(const value_type& d) { resume(d.get()); }
|
||||
|
||||
void close(Download* d);
|
||||
void close_directly(Download* d);
|
||||
void close_files(Download* d);
|
||||
void close_quick(Download* d);
|
||||
void close_throw(Download* d);
|
||||
|
||||
@@ -123,6 +133,9 @@ private:
|
||||
void hash_done(Download* d);
|
||||
void hash_queue(Download* d, int type);
|
||||
|
||||
void set_state_stopped(Download* d);
|
||||
void update_paused_state(Download* d);
|
||||
|
||||
inline void check_contains(Download* d);
|
||||
|
||||
void received_finished(Download* d);
|
||||
|
||||
+4
-4
@@ -154,10 +154,10 @@ Manager::cleanup() {
|
||||
void
|
||||
Manager::shutdown(bool force) {
|
||||
if (!force) {
|
||||
for (auto d : *m_download_list)
|
||||
m_download_list->pause_default(d);
|
||||
for (const auto& d : *m_download_list)
|
||||
m_download_list->pause_default(d.get());
|
||||
} else {
|
||||
for (auto d : *m_download_list)
|
||||
for (const auto& d : *m_download_list)
|
||||
m_download_list->close_quick(d);
|
||||
}
|
||||
}
|
||||
@@ -405,7 +405,7 @@ Manager::receive_hashing_changed() {
|
||||
continue;
|
||||
|
||||
try {
|
||||
m_download_list->open_throw(*itr);
|
||||
m_download_list->open_throw(itr->get());
|
||||
|
||||
// Since the bitfield is allocated on loading of resume load or
|
||||
// hash start, and unallocated on close, we know that if it it
|
||||
|
||||
+40
-16
@@ -16,10 +16,20 @@
|
||||
namespace core {
|
||||
|
||||
// Also add focus thingie here?
|
||||
// Matches the list entry that owns a given download.
|
||||
inline auto
|
||||
entry_is(Download* download) {
|
||||
return [download](const std::shared_ptr<Download>& entry) { return entry.get() == download; };
|
||||
}
|
||||
|
||||
struct view_downloads_compare {
|
||||
view_downloads_compare(const torrent::Object& cmd) :
|
||||
m_command(cmd) {}
|
||||
|
||||
bool operator()(const std::shared_ptr<Download>& d1, const std::shared_ptr<Download>& d2) const {
|
||||
return (*this)(d1.get(), d2.get());
|
||||
}
|
||||
|
||||
bool operator()(Download* d1, Download* d2) const {
|
||||
try {
|
||||
if (m_command.is_empty())
|
||||
@@ -54,6 +64,10 @@ struct view_downloads_filter {
|
||||
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) :
|
||||
m_command(cmd), m_command2(cmd2) {}
|
||||
|
||||
bool operator()(const std::shared_ptr<Download>& d1) const {
|
||||
return (*this)(d1.get());
|
||||
}
|
||||
|
||||
bool operator()(Download* d1) const {
|
||||
return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1);
|
||||
}
|
||||
@@ -152,7 +166,7 @@ View::initialize(const std::string& name) {
|
||||
|
||||
void
|
||||
View::erase(Download* download) {
|
||||
iterator itr = std::find(base_type::begin(), base_type::end(), download);
|
||||
iterator itr = std::find_if(base_type::begin(), base_type::end(), entry_is(download));
|
||||
|
||||
if (itr >= end_visible()) {
|
||||
erase_internal(itr);
|
||||
@@ -165,22 +179,24 @@ View::erase(Download* download) {
|
||||
|
||||
void
|
||||
View::set_visible(Download* download) {
|
||||
iterator itr = std::find(begin_filtered(), end_filtered(), download);
|
||||
iterator itr = std::find_if(begin_filtered(), end_filtered(), entry_is(download));
|
||||
|
||||
if (itr == end_filtered())
|
||||
return;
|
||||
|
||||
// Don't optimize erase since we want to keep the order of the
|
||||
// non-visible elements.
|
||||
auto entry = *itr;
|
||||
|
||||
base_type::erase(itr);
|
||||
insert_visible(download);
|
||||
insert_visible(entry);
|
||||
|
||||
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
|
||||
}
|
||||
|
||||
void
|
||||
View::set_not_visible(Download* download) {
|
||||
iterator itr = std::find(begin_visible(), end_visible(), download);
|
||||
iterator itr = std::find_if(begin_visible(), end_visible(), entry_is(download));
|
||||
|
||||
if (itr == end_visible())
|
||||
return;
|
||||
@@ -190,8 +206,10 @@ View::set_not_visible(Download* download) {
|
||||
|
||||
// Don't optimize erase since we want to keep the order of the
|
||||
// non-visible elements.
|
||||
auto entry = *itr;
|
||||
|
||||
base_type::erase(itr);
|
||||
base_type::push_back(download);
|
||||
base_type::push_back(entry);
|
||||
|
||||
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
|
||||
}
|
||||
@@ -241,12 +259,12 @@ View::prev_focus(unsigned int i) {
|
||||
|
||||
void
|
||||
View::sort() {
|
||||
Download* curFocus = focus() != end_visible() ? *focus() : NULL;
|
||||
Download* curFocus = focus() != end_visible() ? focus()->get() : NULL;
|
||||
|
||||
// Don't go randomly switching around equivalent elements.
|
||||
std::stable_sort(begin(), end_visible(), view_downloads_compare(m_sortCurrent));
|
||||
|
||||
m_focus = position(std::find(begin(), end_visible(), curFocus));
|
||||
m_focus = position(std::find_if(begin(), end_visible(), entry_is(curFocus)));
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
@@ -280,10 +298,10 @@ View::filter() {
|
||||
// set the elements to NULL as we trigger commands on them. Or
|
||||
// perhaps always clear them, thus not throwing anything.
|
||||
if (!m_event_removed.is_empty())
|
||||
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_removed, std::placeholders::_1));
|
||||
std::for_each(changed.begin(), splitChanged, [this](const auto& d) { rpc::call_object_d_nothrow(m_event_removed, d.get()); });
|
||||
|
||||
if (!m_event_added.is_empty())
|
||||
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_added, std::placeholders::_1));
|
||||
std::for_each(changed.begin(), splitChanged, [this](const auto& d) { rpc::call_object_d_nothrow(m_event_added, d.get()); });
|
||||
|
||||
emit_changed();
|
||||
}
|
||||
@@ -294,21 +312,23 @@ View::filter_by(const torrent::Object& condition, View::base_type& result) {
|
||||
view_downloads_filter matches = view_downloads_filter(condition, m_temp_filter);
|
||||
|
||||
for (iterator itr = begin_visible(); itr != end_visible(); ++itr)
|
||||
if (matches(*itr))
|
||||
if (matches(itr->get()))
|
||||
result.push_back(*itr);
|
||||
}
|
||||
|
||||
void
|
||||
View::filter_download(core::Download* download) {
|
||||
iterator itr = std::find(base_type::begin(), base_type::end(), download);
|
||||
iterator itr = std::find_if(base_type::begin(), base_type::end(), entry_is(download));
|
||||
|
||||
if (itr == base_type::end())
|
||||
throw torrent::internal_error("View::filter_download(...) could not find download.");
|
||||
|
||||
if (view_downloads_filter(m_filter, m_temp_filter)(download)) {
|
||||
if (itr >= end_visible()) {
|
||||
auto entry = *itr;
|
||||
|
||||
erase_internal(itr);
|
||||
insert_visible(download);
|
||||
insert_visible(entry);
|
||||
|
||||
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
|
||||
|
||||
@@ -317,16 +337,20 @@ View::filter_download(core::Download* download) {
|
||||
// already visible.
|
||||
//
|
||||
// Consider removing this.
|
||||
auto entry = *itr;
|
||||
|
||||
erase_internal(itr);
|
||||
insert_visible(download);
|
||||
insert_visible(entry);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (itr >= end_visible())
|
||||
return;
|
||||
|
||||
auto entry = *itr;
|
||||
|
||||
erase_internal(itr);
|
||||
base_type::push_back(download);
|
||||
base_type::push_back(entry);
|
||||
|
||||
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
|
||||
}
|
||||
@@ -345,8 +369,8 @@ View::clear_filter_on() {
|
||||
}
|
||||
|
||||
inline void
|
||||
View::insert_visible(Download* d) {
|
||||
auto itr = std::find_if(begin_visible(), end_visible(), [this, d](auto d2) { return view_downloads_compare(m_sortNew)(d, d2); });
|
||||
View::insert_visible(const std::shared_ptr<Download>& d) {
|
||||
auto itr = std::find_if(begin_visible(), end_visible(), [this, &d](const auto& d2) { return view_downloads_compare(m_sortNew)(d.get(), d2.get()); });
|
||||
|
||||
m_size++;
|
||||
m_focus += (m_focus >= position(itr));
|
||||
|
||||
+6
-5
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <torrent/object.h>
|
||||
@@ -26,9 +27,9 @@ namespace core {
|
||||
|
||||
class Download;
|
||||
|
||||
class View : private std::vector<Download*> {
|
||||
class View : private std::vector<std::shared_ptr<Download>> {
|
||||
public:
|
||||
typedef std::vector<Download*> base_type;
|
||||
typedef std::vector<std::shared_ptr<Download>> base_type;
|
||||
typedef std::function<void()> slot_void;
|
||||
typedef std::list<slot_void> signal_void;
|
||||
|
||||
@@ -72,7 +73,7 @@ public:
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void insert(Download* download) { base_type::push_back(download); }
|
||||
void insert(const std::shared_ptr<Download>& download) { base_type::push_back(download); }
|
||||
void erase(Download* download);
|
||||
|
||||
void set_visible(Download* download);
|
||||
@@ -124,9 +125,9 @@ private:
|
||||
View(const View&);
|
||||
void operator=(const View&);
|
||||
|
||||
void push_back(Download* d) { base_type::push_back(d); }
|
||||
void push_back(const std::shared_ptr<Download>& d) { base_type::push_back(d); }
|
||||
|
||||
inline void insert_visible(Download* d);
|
||||
inline void insert_visible(const std::shared_ptr<Download>& d);
|
||||
inline void erase_internal(iterator itr);
|
||||
|
||||
void emit_changed();
|
||||
|
||||
@@ -37,7 +37,7 @@ WindowDownloadList::set_view(core::View* l) {
|
||||
// Return a pair of ints, representing a) the ncurses attributes and b) the ncurses color pair ID to use
|
||||
std::pair<int, int>
|
||||
WindowDownloadList::get_attr_color(core::View::iterator selected) {
|
||||
core::Download* item = *selected;
|
||||
core::Download* item = selected->get();
|
||||
unsigned long focus_attr = selected == m_view->focus() ? m_canvas->attr_map().at(RCOLOR_FOCUS) : 0;
|
||||
int offset = (((selected - m_view->begin_visible()) & 1) + 1) * RCOLOR_MAX; // Determine the even/odd offset for the color pair
|
||||
bool active = item->is_open() && item->is_active();
|
||||
@@ -132,15 +132,15 @@ WindowDownloadList::redraw() {
|
||||
ColorKind focus_color = is_focused ? RCOLOR_FOCUS : RCOLOR_LABEL;
|
||||
auto attr_color = get_attr_color(range.first);
|
||||
|
||||
print_download_title(buffer.data(), last, *range.first);
|
||||
print_download_title(buffer.data(), last, range.first->get());
|
||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||
m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second);
|
||||
|
||||
print_download_info_full(buffer.data(), last, *range.first);
|
||||
print_download_info_full(buffer.data(), last, range.first->get());
|
||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||
m_canvas->set_attr(2, pos++, -1, focus_color);
|
||||
|
||||
print_download_status(buffer.data(), last, *range.first);
|
||||
print_download_status(buffer.data(), last, range.first->get());
|
||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||
m_canvas->set_attr(2, pos++, -1, focus_color);
|
||||
|
||||
@@ -152,7 +152,7 @@ WindowDownloadList::redraw() {
|
||||
char focus_char = range.first == m_view->focus() ? '*' : ' ';
|
||||
auto attr_color = get_attr_color(range.first);
|
||||
|
||||
print_download_info_compact(buffer.data(), last, *range.first);
|
||||
print_download_info_compact(buffer.data(), last, range.first->get());
|
||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||
m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second);
|
||||
|
||||
|
||||
@@ -378,6 +378,12 @@ main(int argc, char** argv) {
|
||||
lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead.");
|
||||
});
|
||||
|
||||
// TODO: Keep d.directory_base for a while as it is widely used.
|
||||
CMD_REDIRECT("d.directory_base", "d.directory");
|
||||
CMD_REDIRECT("d.directory_base.set", "d.directory.base.set");
|
||||
|
||||
rpc::rpc.mark_safe("d.directory_base");
|
||||
|
||||
// if (rpc::call_command_value("method.use_intermediate") == 1) {
|
||||
|
||||
// } else if (rpc::call_command_value("method.use_intermediate") == 2) {
|
||||
|
||||
+9
-1
@@ -1,5 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "core/download.h"
|
||||
#include "parse.h"
|
||||
|
||||
@@ -41,7 +43,13 @@ command_base_call_value_base(command_base* command_raw, target_type target, cons
|
||||
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, val);
|
||||
}
|
||||
|
||||
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, unit * arg.as_value());
|
||||
auto value = arg.as_value();
|
||||
|
||||
if (value > std::numeric_limits<int64_t>::max() / unit ||
|
||||
value < std::numeric_limits<int64_t>::min() / unit)
|
||||
throw torrent::input_error("Value out of range.");
|
||||
|
||||
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, unit * value);
|
||||
}
|
||||
|
||||
template <typename T> const torrent::Object
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef RTORRENT_RPC_COMMAND_MAP_H
|
||||
#define RTORRENT_RPC_COMMAND_MAP_H
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
@@ -104,6 +105,10 @@ inline target_type make_target(T target) {
|
||||
return target_type((int)target_type_id<T>::value, target);
|
||||
}
|
||||
|
||||
inline target_type make_target(const std::shared_ptr<core::Download>& target) {
|
||||
return make_target(target.get());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline target_type make_target_pair(T target1, T target2) {
|
||||
return target_type((int)target_type_id<T, T>::value, target1, target2);
|
||||
|
||||
+12
-4
@@ -26,10 +26,12 @@ constexpr int JSONRPC_METHOD_NOT_FOUND_ERROR = -32601;
|
||||
constexpr int JSONRPC_INVALID_PARAMS_ERROR = -32602;
|
||||
constexpr int JSONRPC_INTERNAL_ERROR = -32000;
|
||||
|
||||
constexpr uint32_t max_json_depth = 1024;
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
torrent::Object
|
||||
json_to_object(const json& value) {
|
||||
json_to_object(const json& value, uint32_t depth = 0) {
|
||||
switch (value.type()) {
|
||||
case json::value_t::number_unsigned:
|
||||
case json::value_t::number_integer:
|
||||
@@ -42,20 +44,26 @@ json_to_object(const json& value) {
|
||||
return torrent::Object(value.get<std::string>());
|
||||
|
||||
case json::value_t::array: {
|
||||
if (++depth >= max_json_depth)
|
||||
throw torrent::input_error("invalid parameters: max depth reached");
|
||||
|
||||
auto array_raw = torrent::Object::create_list();
|
||||
auto& array = array_raw.as_list();
|
||||
|
||||
for (const auto& entry : value)
|
||||
array.push_back(json_to_object(entry));
|
||||
array.push_back(json_to_object(entry, depth));
|
||||
|
||||
return array_raw;
|
||||
}
|
||||
case json::value_t::object: {
|
||||
if (++depth >= max_json_depth)
|
||||
throw torrent::input_error("invalid parameters: max depth reached");
|
||||
|
||||
auto map_raw = torrent::Object::create_map();
|
||||
auto& map = map_raw.as_map();
|
||||
|
||||
for (const auto& entry : value.items())
|
||||
map[entry.key()] = json_to_object(entry.value());
|
||||
map[entry.key()] = json_to_object(entry.value(), depth);
|
||||
|
||||
return map_raw;
|
||||
}
|
||||
@@ -138,7 +146,7 @@ jsonrpc_call_command(const std::string& method, const json& params) {
|
||||
|
||||
CommandMap::iterator itr = commands.find(method.c_str());
|
||||
|
||||
if (itr == commands.end()) {
|
||||
if (itr == commands.end() || !(itr->second.m_flags & CommandMap::flag_public_rpc)) {
|
||||
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, "method not found: " + method);
|
||||
}
|
||||
|
||||
|
||||
+37
-19
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
@@ -10,6 +11,8 @@
|
||||
|
||||
namespace rpc {
|
||||
|
||||
constexpr uint32_t max_parse_depth = 1024;
|
||||
|
||||
const char*
|
||||
parse_skip_wspace(const char* first, const char* last) {
|
||||
while (first != last && parse_is_space(*first))
|
||||
@@ -99,6 +102,12 @@ parse_whole_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
value_fits_shifted(int64_t value, int shift) {
|
||||
return value <= (std::numeric_limits<int64_t>::max() >> shift) &&
|
||||
value >= (std::numeric_limits<int64_t>::min() >> shift);
|
||||
}
|
||||
|
||||
const char*
|
||||
parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
||||
if (unit <= 0)
|
||||
@@ -121,20 +130,26 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
||||
case 'B': ++last; break;
|
||||
case 'k':
|
||||
case 'K':
|
||||
if (*value > (int64_t)0x1FFFFFFFFFFFFF) return src; // overflow guard
|
||||
if (!value_fits_shifted(*value, 10)) return src; // overflow guard
|
||||
*value = *value << 10; ++last; break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
if (*value > (int64_t)0x7FFFFFFFFFF) return src; // overflow guard
|
||||
if (!value_fits_shifted(*value, 20)) return src; // overflow guard
|
||||
*value = *value << 20; ++last; break;
|
||||
case 'g':
|
||||
case 'G':
|
||||
if (*value > (int64_t)0x1FFFFFFFF) return src; // overflow guard
|
||||
if (!value_fits_shifted(*value, 30)) return src; // overflow guard
|
||||
*value = *value << 30; ++last; break;
|
||||
// case ' ':
|
||||
// case '\0': *value = *value * unit; break;
|
||||
// default: throw torrent::input_error("Could not parse value.");
|
||||
default: *value = *value * unit; break;
|
||||
default:
|
||||
if (*value > std::numeric_limits<int64_t>::max() / unit ||
|
||||
*value < std::numeric_limits<int64_t>::min() / unit)
|
||||
return src; // overflow guard
|
||||
|
||||
*value = *value * unit;
|
||||
break;
|
||||
}
|
||||
|
||||
return last;
|
||||
@@ -142,10 +157,13 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
||||
|
||||
// Somewhat ugly...
|
||||
const char*
|
||||
parse_object(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char)) {
|
||||
parse_object(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char), uint32_t depth) {
|
||||
if (++depth >= max_parse_depth)
|
||||
throw torrent::input_error("Max parse depth reached.");
|
||||
|
||||
if (*first == '{') {
|
||||
*dest = torrent::Object::create_list();
|
||||
first = parse_list(first + 1, last, dest, &parse_is_delim_block);
|
||||
first = parse_list(first + 1, last, dest, &parse_is_delim_block, depth);
|
||||
first = parse_skip_wspace(first, last);
|
||||
|
||||
if (first == last || *first != '}')
|
||||
@@ -154,18 +172,18 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (*
|
||||
return ++first;
|
||||
|
||||
} else if (*first == '(') {
|
||||
int32_t depth = 1;
|
||||
int32_t parentheses = 1;
|
||||
|
||||
while (first + 1 != last && *(first + 1) == '(') {
|
||||
first++;
|
||||
depth++;
|
||||
parentheses++;
|
||||
}
|
||||
|
||||
if (depth > 3)
|
||||
if (parentheses > 3)
|
||||
throw torrent::input_error("Max 3 parentheses per object allowed.");
|
||||
|
||||
*dest = torrent::Object::create_dict_key();
|
||||
dest->set_flags(torrent::Object::flag_function << (depth - 1));
|
||||
dest->set_flags(torrent::Object::flag_function << (parentheses - 1));
|
||||
|
||||
first = parse_string(first + 1, last, &dest->as_dict_key(), &parse_is_delim_func);
|
||||
first = parse_skip_wspace(first, last);
|
||||
@@ -176,16 +194,16 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (*
|
||||
if (*first == ',') {
|
||||
// This will always create a list even for single argument functions...
|
||||
dest->as_dict_obj() = torrent::Object::create_list();
|
||||
first = parse_list(first + 1, last, &dest->as_dict_obj(), &parse_is_delim_func);
|
||||
first = parse_list(first + 1, last, &dest->as_dict_obj(), &parse_is_delim_func, depth);
|
||||
first = parse_skip_wspace(first, last);
|
||||
}
|
||||
|
||||
while (depth != 0 && first != last && *first == ')') {
|
||||
while (parentheses != 0 && first != last && *first == ')') {
|
||||
first++;
|
||||
depth--;
|
||||
parentheses--;
|
||||
}
|
||||
|
||||
if (depth != 0)
|
||||
if (parentheses != 0)
|
||||
throw torrent::input_error("Parentheses mismatch.");
|
||||
|
||||
return first;
|
||||
@@ -198,7 +216,7 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (*
|
||||
}
|
||||
|
||||
const char*
|
||||
parse_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char)) {
|
||||
parse_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char), uint32_t depth) {
|
||||
if (!dest->is_list())
|
||||
throw torrent::internal_error("parse_list(...) !dest->is_list().");
|
||||
|
||||
@@ -206,7 +224,7 @@ parse_list(const char* first, const char* last, torrent::Object* dest, bool (*de
|
||||
torrent::Object tmp;
|
||||
|
||||
first = parse_skip_wspace(first, last);
|
||||
first = parse_object(first, last, &tmp, delim);
|
||||
first = parse_object(first, last, &tmp, delim, depth);
|
||||
first = parse_skip_wspace(first, last);
|
||||
|
||||
dest->as_list().push_back(tmp);
|
||||
@@ -221,9 +239,9 @@ parse_list(const char* first, const char* last, torrent::Object* dest, bool (*de
|
||||
}
|
||||
|
||||
const char*
|
||||
parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char)) {
|
||||
parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char), uint32_t depth) {
|
||||
first = parse_skip_wspace(first, last);
|
||||
first = parse_object(first, last, dest, delim);
|
||||
first = parse_object(first, last, dest, delim, depth);
|
||||
first = parse_skip_wspace(first, last);
|
||||
|
||||
if (first != last && parse_is_seperator(*first)) {
|
||||
@@ -231,7 +249,7 @@ parse_whole_list(const char* first, const char* last, torrent::Object* dest, boo
|
||||
tmp.swap(*dest);
|
||||
|
||||
dest->as_list().push_back(tmp);
|
||||
first = parse_list(++first, last, dest, delim);
|
||||
first = parse_list(++first, last, dest, delim, depth);
|
||||
}
|
||||
|
||||
return first;
|
||||
|
||||
+3
-3
@@ -77,9 +77,9 @@ const char* parse_value_nothrow(const char* first, const char* last, int64_t* va
|
||||
void parse_whole_value(const char* src, int64_t* value, int base = 0, int unit = 1);
|
||||
bool parse_whole_value_nothrow(const char* src, int64_t* value, int base = 0, int unit = 1);
|
||||
|
||||
const char* parse_object (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default);
|
||||
const char* parse_list (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default);
|
||||
const char* parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default);
|
||||
const char* parse_object (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default, uint32_t depth = 0);
|
||||
const char* parse_list (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default, uint32_t depth = 0);
|
||||
const char* parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default, uint32_t depth = 0);
|
||||
|
||||
std::string convert_to_string(const torrent::Object& src);
|
||||
|
||||
|
||||
@@ -167,6 +167,14 @@ RpcManager::cleanup() {
|
||||
m_jsonrpc.cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::set_size_limit(uint64_t size) {
|
||||
if (size > SCgiTask::max_content_size)
|
||||
throw torrent::input_error("XMLRPC size limit cannot exceed the SCGI content size limit.");
|
||||
|
||||
m_xmlrpc.set_size_limit(size);
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::insert_command(const char* name, const char* parm, const char* doc) {
|
||||
m_xmlrpc.insert_command(name, parm, doc);
|
||||
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
void cleanup();
|
||||
|
||||
int64_t size_limit() { return m_xmlrpc.size_limit(); };
|
||||
void set_size_limit(uint64_t size) { m_xmlrpc.set_size_limit(size); };
|
||||
void set_size_limit(uint64_t size);
|
||||
|
||||
int dialect() { return m_xmlrpc.dialect(); }
|
||||
void set_dialect(int dialect) { m_xmlrpc.set_dialect(dialect); }
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <torrent/runtime/socket_manager.h>
|
||||
#include <torrent/system/callbacks.h>
|
||||
#include <torrent/system/poll.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "control.h"
|
||||
@@ -27,6 +28,8 @@ namespace rpc {
|
||||
SCgiTask::SCgiTask()
|
||||
: m_callback_id(torrent::system::make_callback_id()) {
|
||||
|
||||
m_task_timeout.slot() = [this]() { close(); };
|
||||
|
||||
reset_file_descriptor();
|
||||
}
|
||||
|
||||
@@ -49,6 +52,8 @@ SCgiTask::open(SCgi* parent, int fd) {
|
||||
|
||||
torrent::this_thread::poll()->open_and_insert_read(this);
|
||||
|
||||
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&m_task_timeout, timeout_request);
|
||||
|
||||
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
||||
|
||||
// Leave room for terminating nul byte for parsing the header.
|
||||
@@ -60,6 +65,8 @@ SCgiTask::cancel_open() {
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
torrent::this_thread::scheduler()->erase(&m_task_timeout);
|
||||
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
|
||||
torrent::fd_close(file_descriptor());
|
||||
@@ -71,6 +78,8 @@ SCgiTask::close() {
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
torrent::this_thread::scheduler()->erase(&m_task_timeout);
|
||||
|
||||
torrent::system::cancel_callback_and_wait(m_callback_id, scgi_thread::thread(), torrent::main_thread::thread());
|
||||
|
||||
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
|
||||
@@ -157,6 +166,8 @@ SCgiTask::event_read() {
|
||||
|
||||
torrent::this_thread::poll()->remove_read(this);
|
||||
|
||||
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&m_task_timeout, timeout_request);
|
||||
|
||||
if (m_parent->log_fd() >= 0) {
|
||||
[[maybe_unused]] int result;
|
||||
|
||||
|
||||
+8
-3
@@ -1,10 +1,12 @@
|
||||
#ifndef RTORRENT_RPC_SCGI_TASK_H
|
||||
#define RTORRENT_RPC_SCGI_TASK_H
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <torrent/system/event.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
@@ -14,7 +16,9 @@ class SCgiTask : public torrent::system::Event {
|
||||
public:
|
||||
static constexpr int default_buffer_size = 8191;
|
||||
static constexpr int max_header_size = 2000;
|
||||
static constexpr int max_content_size = (2 << 23);
|
||||
static constexpr int max_content_size = (1 << 26);
|
||||
|
||||
static constexpr auto timeout_request = std::chrono::seconds(60);
|
||||
|
||||
enum ContentType { XML, JSON };
|
||||
|
||||
@@ -54,8 +58,9 @@ private:
|
||||
void plaintext_response(const char* buffer, uint32_t content_length);
|
||||
void gzip_response(const char* buffer, uint32_t content_length);
|
||||
|
||||
SCgi* m_parent{};
|
||||
torrent::system::callback_id m_callback_id;
|
||||
SCgi* m_parent{};
|
||||
torrent::system::callback_id m_callback_id;
|
||||
torrent::system::SchedulerEntry m_task_timeout;
|
||||
|
||||
std::mutex m_result_mutex;
|
||||
|
||||
|
||||
+3
-4
@@ -77,16 +77,18 @@ xmlrpc_list_entry_to_value(xmlrpc_env* env, xmlrpc_value* src, int index) {
|
||||
{
|
||||
const char* str;
|
||||
xmlrpc_read_string(env, tmp, &str);
|
||||
xmlrpc_DECREF(tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
const char* end = str;
|
||||
int64_t v3 = ::strtoll(str, (char**)&end, 0);
|
||||
bool invalid = *str == '\0' || *end != '\0';
|
||||
|
||||
::free((void*)str);
|
||||
|
||||
if (*str == '\0' || *end != '\0')
|
||||
if (invalid)
|
||||
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||
|
||||
return v3;
|
||||
@@ -529,9 +531,6 @@ XmlRpc::size_limit() {
|
||||
|
||||
void
|
||||
XmlRpc::set_size_limit(uint64_t size) {
|
||||
if (size >= (64 << 20))
|
||||
throw torrent::input_error("Invalid XMLRPC limit size.");
|
||||
|
||||
xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,8 +127,12 @@ save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream
|
||||
if (!output.good())
|
||||
throw torrent::storage_error("failed to write stream to file : " + path);
|
||||
|
||||
// The data only reaches the kernel here, so this is where a full disk is seen.
|
||||
output.close();
|
||||
|
||||
if (!output.good())
|
||||
throw torrent::storage_error("failed to flush stream to file : " + path);
|
||||
|
||||
// Ensure that the new file is actually written to the disk
|
||||
int fd = ::open(path.c_str(), O_WRONLY);
|
||||
|
||||
@@ -137,13 +141,19 @@ save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream
|
||||
|
||||
if (use_fsyncdisk) {
|
||||
#ifdef __APPLE__
|
||||
::fsync(fd);
|
||||
int sync_result = ::fsync(fd);
|
||||
#else
|
||||
::fdatasync(fd);
|
||||
int sync_result = ::fdatasync(fd);
|
||||
#endif
|
||||
|
||||
if (sync_result == -1) {
|
||||
::close(fd);
|
||||
throw torrent::storage_error("failed to sync file to disk : " + path);
|
||||
}
|
||||
}
|
||||
|
||||
::close(fd);
|
||||
if (::close(fd) == -1)
|
||||
throw torrent::storage_error("failed to close file descriptor : " + path);
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
@@ -126,7 +126,7 @@ private:
|
||||
|
||||
inline bool SessionManager::is_used() const { return !m_path.empty(); }
|
||||
inline std::string SessionManager::path() const { return m_path; }
|
||||
inline bool SessionManager::use_fsyncdisk() const { return true; }
|
||||
inline bool SessionManager::use_fsyncdisk() const { return m_use_fsyncdisk; }
|
||||
inline bool SessionManager::use_lock() const { return m_use_lock; }
|
||||
inline void SessionManager::flush_all_pending_builds() { process_pending_builds(true); }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
int parse_main_options(int argc, char** argv);
|
||||
void parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn);
|
||||
|
||||
+5
-2
@@ -1,3 +1,4 @@
|
||||
#include <memory>
|
||||
#include "config.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -30,8 +31,10 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
Download::Download(core::Download* d)
|
||||
: m_download(d) {
|
||||
Download::Download(const std::shared_ptr<core::Download>& download)
|
||||
: m_download(download.get()) {
|
||||
|
||||
auto* d = download.get();
|
||||
|
||||
m_windowDownloadStatus = std::make_unique<WDownloadStatus>(d);
|
||||
m_windowDownloadStatus->set_bottom(true);
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@
|
||||
#define RTORRENT_UI_DOWNLOAD_H
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <torrent/peer/peer.h>
|
||||
|
||||
#include "display/manager.h"
|
||||
@@ -36,7 +37,7 @@ public:
|
||||
DISPLAY_MAX_SIZE
|
||||
} Display;
|
||||
|
||||
Download(core::Download* d);
|
||||
Download(const std::shared_ptr<core::Download>& d);
|
||||
~Download();
|
||||
|
||||
void activate(display::Frame* frame, bool focus = true);
|
||||
|
||||
@@ -89,7 +89,7 @@ DownloadList::unfocus_download(core::Download* d) {
|
||||
if (m_state == DISPLAY_DOWNLOAD && d == static_cast<Download*>(m_uiArray[DISPLAY_DOWNLOAD])->download())
|
||||
activate_display(DISPLAY_DOWNLOAD_LIST);
|
||||
|
||||
if (current_view()->focus() < current_view()->end_visible() && *current_view()->focus() == d)
|
||||
if (current_view()->focus() < current_view()->end_visible() && current_view()->focus()->get() == d)
|
||||
current_view()->next_focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ ElementDownloadList::receive_cycle_throttle() {
|
||||
if (m_view->focus() == m_view->end_visible())
|
||||
return;
|
||||
|
||||
core::Download* download = *m_view->focus();
|
||||
core::Download* download = m_view->focus()->get();
|
||||
if (download->is_active()) {
|
||||
lt_log_print(torrent::LOG_TORRENT_WARN, "Cannot change throttle on active download.");
|
||||
return;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <zlib.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "utils/functional.h"
|
||||
|
||||
namespace utils {
|
||||
|
||||
void
|
||||
@@ -21,6 +23,8 @@ gzip_compress_to_vector(const char* buffer, unsigned int length, std::vector<cha
|
||||
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits | gzip_encoding, gzip_level, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
throw torrent::internal_error("gzip_compress_to_vector(...) could not initialize gzip deflate.");
|
||||
|
||||
scope_guard guard([&zs]() { deflateEnd(&zs); });
|
||||
|
||||
auto max_response_size = deflateBound(&zs, length);
|
||||
|
||||
output.resize(offset + max_response_size);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define RTORRENT_UTILS_GZIP_H
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace utils {
|
||||
|
||||
|
||||
+4
-2
@@ -51,8 +51,10 @@ rtorrent_Test_Rpc_SOURCES = $(rtorrent_Test_Common) \
|
||||
rtorrent_Test_Src_SOURCES = $(rtorrent_Test_Common) \
|
||||
src/test_command_dynamic.cc \
|
||||
src/test_command_dynamic.h \
|
||||
src/test_command_local.cc \
|
||||
src/test_command_local.h \
|
||||
src/test_command_ip.cc \
|
||||
src/test_command_ip.h \
|
||||
src/test_command_system.cc \
|
||||
src/test_command_system.h \
|
||||
src/test_command_path.cc \
|
||||
src/test_command_path.h \
|
||||
src/test_command_string.cc \
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "test/src/test_command_ip.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandIp);
|
||||
|
||||
bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end);
|
||||
|
||||
static uint32_t
|
||||
ipv4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
|
||||
return (a << 24) | (b << 16) | (c << 8) | d;
|
||||
}
|
||||
|
||||
#define RANGE_ASSERT(address, expected_start, expected_end) \
|
||||
{ \
|
||||
uint32_t start = 0; \
|
||||
uint32_t end = 0; \
|
||||
\
|
||||
CPPUNIT_ASSERT(ipv4_range_parse(address, &start, &end)); \
|
||||
CPPUNIT_ASSERT_EQUAL(expected_start, start); \
|
||||
CPPUNIT_ASSERT_EQUAL(expected_end, end); \
|
||||
}
|
||||
|
||||
void
|
||||
TestCommandIp::test_single_address() {
|
||||
RANGE_ASSERT("10.1.2.3", ipv4(10, 1, 2, 3), ipv4(10, 1, 2, 3));
|
||||
}
|
||||
|
||||
void
|
||||
TestCommandIp::test_explicit_range() {
|
||||
RANGE_ASSERT("10.1.2.3-10.1.2.9", ipv4(10, 1, 2, 3), ipv4(10, 1, 2, 9));
|
||||
}
|
||||
|
||||
void
|
||||
TestCommandIp::test_cidr() {
|
||||
RANGE_ASSERT("10.0.0.0/8", ipv4(10, 0, 0, 0), ipv4(10, 255, 255, 255));
|
||||
RANGE_ASSERT("10.1.2.0/24", ipv4(10, 1, 2, 0), ipv4(10, 1, 2, 255));
|
||||
RANGE_ASSERT("10.1.2.128/25", ipv4(10, 1, 2, 128), ipv4(10, 1, 2, 255));
|
||||
RANGE_ASSERT("10.1.2.3/31", ipv4(10, 1, 2, 2), ipv4(10, 1, 2, 3));
|
||||
}
|
||||
|
||||
void
|
||||
TestCommandIp::test_cidr_zero_mask() {
|
||||
RANGE_ASSERT("0.0.0.0/0", ipv4(0, 0, 0, 0), ipv4(255, 255, 255, 255));
|
||||
RANGE_ASSERT("10.1.2.3/0", ipv4(0, 0, 0, 0), ipv4(255, 255, 255, 255));
|
||||
}
|
||||
|
||||
void
|
||||
TestCommandIp::test_cidr_full_mask() {
|
||||
RANGE_ASSERT("10.1.2.3/32", ipv4(10, 1, 2, 3), ipv4(10, 1, 2, 3));
|
||||
RANGE_ASSERT("0.0.0.0/32", ipv4(0, 0, 0, 0), ipv4(0, 0, 0, 0));
|
||||
RANGE_ASSERT("255.255.255.255/32", ipv4(255, 255, 255, 255), ipv4(255, 255, 255, 255));
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
// ipv4_range_parse is a pure function, so this does not use test_fixture and
|
||||
// the mock and logging setup that comes with it.
|
||||
class TestCommandIp : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(TestCommandIp);
|
||||
|
||||
CPPUNIT_TEST(test_single_address);
|
||||
CPPUNIT_TEST(test_explicit_range);
|
||||
|
||||
CPPUNIT_TEST(test_cidr);
|
||||
CPPUNIT_TEST(test_cidr_zero_mask);
|
||||
CPPUNIT_TEST(test_cidr_full_mask);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
void test_single_address();
|
||||
void test_explicit_range();
|
||||
|
||||
void test_cidr();
|
||||
void test_cidr_zero_mask();
|
||||
void test_cidr_full_mask();
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "test/src/test_command_local.h"
|
||||
#include "test/src/test_command_system.h"
|
||||
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/runtime/socket_manager.h>
|
||||
@@ -10,12 +10,12 @@
|
||||
#include "globals.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandLocal);
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandSystem);
|
||||
|
||||
void initialize_command_local();
|
||||
void initialize_command_system();
|
||||
|
||||
void
|
||||
TestCommandLocal::setUp() {
|
||||
TestCommandSystem::setUp() {
|
||||
torrent::initialize_main_thread();
|
||||
torrent::initialize();
|
||||
|
||||
@@ -23,16 +23,16 @@ TestCommandLocal::setUp() {
|
||||
control = new Control;
|
||||
|
||||
if (!rpc::commands.has("system.sockets.size"))
|
||||
initialize_command_local();
|
||||
initialize_command_system();
|
||||
}
|
||||
|
||||
void
|
||||
TestCommandLocal::tearDown() {
|
||||
TestCommandSystem::tearDown() {
|
||||
torrent::cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
TestCommandLocal::test_socket_category_commands() {
|
||||
TestCommandSystem::test_socket_category_commands() {
|
||||
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
||||
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
|
||||
auto name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "test/helpers/test_fixture.h"
|
||||
|
||||
class TestCommandLocal : public test_fixture {
|
||||
CPPUNIT_TEST_SUITE(TestCommandLocal);
|
||||
class TestCommandSystem : public test_fixture {
|
||||
CPPUNIT_TEST_SUITE(TestCommandSystem);
|
||||
|
||||
CPPUNIT_TEST(test_socket_category_commands);
|
||||
|
||||
Reference in New Issue
Block a user