mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-05 17:52:29 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b78fd9aef5 | |||
| 21e399d099 | |||
| 08a907b547 | |||
| a0b8702895 | |||
| ad3c31862e | |||
| 2711e4c044 | |||
| 8d50aee96f | |||
| 71bb51c3fd | |||
| 63ea08bde6 | |||
| a82bdf22ac | |||
| 9f6731b4e2 | |||
| d935e0ffe9 | |||
| ba239bc8c5 | |||
| ea16276773 | |||
| f767053297 | |||
| 598914908f | |||
| 38fc815d52 | |||
| 674ae767aa | |||
| 0aaa470053 | |||
| 6f27159627 | |||
| de163293ab | |||
| fb4e775689 | |||
| 39f186e523 | |||
| 70e6964823 |
+3
-2
@@ -1,6 +1,6 @@
|
||||
m4_pattern_allow([PKG_CHECK_EXISTS])
|
||||
|
||||
AC_INIT([rtorrent],[0.16.7],[sundell.software@gmail.com])
|
||||
AC_INIT([rtorrent],[0.16.9],[sundell.software@gmail.com])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIRS([scripts])
|
||||
@@ -48,7 +48,7 @@ if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.7])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.9])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
TORRENT_WITH_XMLRPC_C
|
||||
@@ -56,6 +56,7 @@ AC_LANG_POP(C++)
|
||||
|
||||
TORRENT_WITH_LUA
|
||||
TORRENT_WITH_TINYXML2
|
||||
TORRENT_WITH_SYSTEMD
|
||||
|
||||
if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then
|
||||
AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one])
|
||||
|
||||
@@ -358,3 +358,20 @@ AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
|
||||
]
|
||||
)
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_SYSTEMD], [
|
||||
AC_ARG_WITH(systemd,
|
||||
AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]),
|
||||
[
|
||||
if test "$withval" = "yes"; then
|
||||
PKG_CHECK_MODULES([SYSTEMD], [libsystemd],
|
||||
[
|
||||
CXXFLAGS="$CXXFLAGS $SYSTEMD_CFLAGS"
|
||||
LIBS="$LIBS $SYSTEMD_LIBS"
|
||||
AC_DEFINE(HAVE_SYSTEMD, 1, [Support for systemd socket activation.])
|
||||
],
|
||||
[AC_MSG_ERROR([libsystemd not found. Install libsystemd-dev (or the equivalent for your distribution).])])
|
||||
fi
|
||||
])
|
||||
])
|
||||
|
||||
@@ -884,4 +884,91 @@ initialize_command_download() {
|
||||
CMD2_DL_LIST ("t.multicall", std::bind(&t_multicall, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("p.call_target", std::bind(&p_call_target, std::placeholders::_2));
|
||||
|
||||
rpc::rpc.mark_safe("add_peer");
|
||||
rpc::rpc.mark_safe("d.hash");
|
||||
rpc::rpc.mark_safe("d.local_id");
|
||||
rpc::rpc.mark_safe("d.local_id_html");
|
||||
rpc::rpc.mark_safe("d.bitfield");
|
||||
rpc::rpc.mark_safe("d.base_path");
|
||||
rpc::rpc.mark_safe("d.base_filename");
|
||||
rpc::rpc.mark_safe("d.name");
|
||||
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");
|
||||
rpc::rpc.mark_safe("d.up.total");
|
||||
rpc::rpc.mark_safe("d.down.rate");
|
||||
rpc::rpc.mark_safe("d.down.total");
|
||||
rpc::rpc.mark_safe("d.skip.rate");
|
||||
rpc::rpc.mark_safe("d.skip.total");
|
||||
rpc::rpc.mark_safe("d.is_open");
|
||||
rpc::rpc.mark_safe("d.is_active");
|
||||
rpc::rpc.mark_safe("d.is_hash_checked");
|
||||
rpc::rpc.mark_safe("d.is_hash_checking");
|
||||
rpc::rpc.mark_safe("d.is_multi_file");
|
||||
rpc::rpc.mark_safe("d.is_private");
|
||||
rpc::rpc.mark_safe("d.is_pex_active");
|
||||
rpc::rpc.mark_safe("d.is_partially_done");
|
||||
rpc::rpc.mark_safe("d.is_not_partially_done");
|
||||
rpc::rpc.mark_safe("d.is_meta");
|
||||
rpc::rpc.mark_safe("d.peer_exchange");
|
||||
rpc::rpc.mark_safe("d.resume");
|
||||
rpc::rpc.mark_safe("d.pause");
|
||||
rpc::rpc.mark_safe("d.open");
|
||||
rpc::rpc.mark_safe("d.close");
|
||||
rpc::rpc.mark_safe("d.close.directly");
|
||||
rpc::rpc.mark_safe("d.erase");
|
||||
rpc::rpc.mark_safe("d.check_hash");
|
||||
rpc::rpc.mark_safe("d.save_resume");
|
||||
rpc::rpc.mark_safe("d.save_full_session");
|
||||
rpc::rpc.mark_safe("d.update_priorities");
|
||||
rpc::rpc.mark_safe("d.custom");
|
||||
rpc::rpc.mark_safe("d.custom1");
|
||||
rpc::rpc.mark_safe("d.custom2");
|
||||
rpc::rpc.mark_safe("d.custom3");
|
||||
rpc::rpc.mark_safe("d.custom4");
|
||||
rpc::rpc.mark_safe("d.custom5");
|
||||
rpc::rpc.mark_safe("d.size_bytes");
|
||||
rpc::rpc.mark_safe("d.size_chunks");
|
||||
rpc::rpc.mark_safe("d.size_pex");
|
||||
rpc::rpc.mark_safe("d.completed_bytes");
|
||||
rpc::rpc.mark_safe("d.bytes_done");
|
||||
rpc::rpc.mark_safe("d.peers_accounted");
|
||||
rpc::rpc.mark_safe("d.chunks_hashed");
|
||||
rpc::rpc.mark_safe("d.tracker_size");
|
||||
rpc::rpc.mark_safe("d.completed_chunks");
|
||||
rpc::rpc.mark_safe("d.left_bytes");
|
||||
rpc::rpc.mark_safe("d.chunk_size");
|
||||
rpc::rpc.mark_safe("d.priority");
|
||||
rpc::rpc.mark_safe("d.priority_str");
|
||||
rpc::rpc.mark_safe("d.state");
|
||||
rpc::rpc.mark_safe("d.state_changed");
|
||||
rpc::rpc.mark_safe("d.state_counter");
|
||||
rpc::rpc.mark_safe("d.connection_current");
|
||||
rpc::rpc.mark_safe("d.connection_leech");
|
||||
rpc::rpc.mark_safe("d.connection_seed");
|
||||
rpc::rpc.mark_safe("d.throttle_name");
|
||||
rpc::rpc.mark_safe("d.uploads_max");
|
||||
rpc::rpc.mark_safe("d.downloads_max");
|
||||
rpc::rpc.mark_safe("d.peers_min");
|
||||
rpc::rpc.mark_safe("d.peers_max");
|
||||
rpc::rpc.mark_safe("d.peers_connected");
|
||||
rpc::rpc.mark_safe("d.peers_not_connected");
|
||||
rpc::rpc.mark_safe("d.peers_complete");
|
||||
rpc::rpc.mark_safe("d.tracker_numwant");
|
||||
rpc::rpc.mark_safe("d.tracker_focus");
|
||||
rpc::rpc.mark_safe("d.message");
|
||||
rpc::rpc.mark_safe("d.hashing");
|
||||
rpc::rpc.mark_safe("d.hashing_failed");
|
||||
rpc::rpc.mark_safe("d.free_diskspace");
|
||||
rpc::rpc.mark_safe("d.views");
|
||||
rpc::rpc.mark_safe("d.views.remove");
|
||||
rpc::rpc.mark_safe("d.views.push_back_unique");
|
||||
rpc::rpc.mark_safe("d.ratio");
|
||||
rpc::rpc.mark_safe("f.multicall");
|
||||
rpc::rpc.mark_safe("p.multicall");
|
||||
rpc::rpc.mark_safe("p.call_target");
|
||||
rpc::rpc.mark_safe("t.multicall");
|
||||
}
|
||||
|
||||
@@ -455,4 +455,27 @@ initialize_command_dynamic() {
|
||||
CMD2_ANY ("strings.tracker_event", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_EVENT));
|
||||
CMD2_ANY ("strings.tracker_mode", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_MODE));
|
||||
// clang-format on
|
||||
|
||||
#ifdef HAVE_XMLRPC_TINYXML2
|
||||
rpc::rpc.mark_safe("system.listMethods");
|
||||
#endif
|
||||
|
||||
rpc::rpc.mark_safe("method.use_deprecated");
|
||||
rpc::rpc.mark_safe("method.const");
|
||||
rpc::rpc.mark_safe("method.has_key");
|
||||
rpc::rpc.mark_safe("method.list_keys");
|
||||
rpc::rpc.mark_safe("method.get");
|
||||
rpc::rpc.mark_safe("method.rlookup");
|
||||
rpc::rpc.mark_safe("catch");
|
||||
|
||||
rpc::rpc.mark_safe("strings.choke_heuristics");
|
||||
rpc::rpc.mark_safe("strings.choke_heuristics.upload");
|
||||
rpc::rpc.mark_safe("strings.choke_heuristics.download");
|
||||
rpc::rpc.mark_safe("strings.connection_type");
|
||||
rpc::rpc.mark_safe("strings.encryption");
|
||||
rpc::rpc.mark_safe("strings.ip_filter");
|
||||
rpc::rpc.mark_safe("strings.ip_tos");
|
||||
rpc::rpc.mark_safe("strings.log_group");
|
||||
rpc::rpc.mark_safe("strings.tracker_event");
|
||||
rpc::rpc.mark_safe("strings.tracker_mode");
|
||||
}
|
||||
|
||||
@@ -344,4 +344,15 @@ initialize_command_events() {
|
||||
CMD2_ANY_LIST ("d.multicall.filtered", std::bind(&d_multicall_filtered, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("directory.watch.added", std::bind(&directory_watch_added, std::placeholders::_2));
|
||||
|
||||
rpc::rpc.mark_safe("start_tied");
|
||||
rpc::rpc.mark_safe("stop_untied");
|
||||
rpc::rpc.mark_safe("close_untied");
|
||||
rpc::rpc.mark_safe("remove_untied");
|
||||
|
||||
rpc::rpc.mark_safe("close_low_diskspace");
|
||||
rpc::rpc.mark_safe("close_low_diskspace.normal");
|
||||
rpc::rpc.mark_safe("download_list");
|
||||
rpc::rpc.mark_safe("d.multicall2");
|
||||
rpc::rpc.mark_safe("d.multicall.filtered");
|
||||
}
|
||||
|
||||
@@ -103,4 +103,32 @@ initialize_command_file() {
|
||||
|
||||
CMD2_FILEITR("fi.filename_last", std::bind(&apply_fi_filename_last, std::placeholders::_1));
|
||||
CMD2_FILEITR("fi.is_file", std::bind(&torrent::FileListIterator::is_file, std::placeholders::_1));
|
||||
|
||||
rpc::rpc.mark_safe("f.path");
|
||||
rpc::rpc.mark_safe("f.path_components");
|
||||
rpc::rpc.mark_safe("f.path_depth");
|
||||
rpc::rpc.mark_safe("f.frozen_path");
|
||||
rpc::rpc.mark_safe("f.offset");
|
||||
rpc::rpc.mark_safe("f.size_bytes");
|
||||
rpc::rpc.mark_safe("f.size_chunks");
|
||||
rpc::rpc.mark_safe("f.completed_chunks");
|
||||
rpc::rpc.mark_safe("f.range_first");
|
||||
rpc::rpc.mark_safe("f.range_second");
|
||||
rpc::rpc.mark_safe("f.priority");
|
||||
rpc::rpc.mark_safe("f.priority.set");
|
||||
rpc::rpc.mark_safe("f.is_created");
|
||||
rpc::rpc.mark_safe("f.is_open");
|
||||
rpc::rpc.mark_safe("f.is_create_queued");
|
||||
rpc::rpc.mark_safe("f.is_resize_queued");
|
||||
rpc::rpc.mark_safe("f.prioritize_first");
|
||||
rpc::rpc.mark_safe("f.prioritize_first.enable");
|
||||
rpc::rpc.mark_safe("f.prioritize_first.disable");
|
||||
rpc::rpc.mark_safe("f.prioritize_last");
|
||||
rpc::rpc.mark_safe("f.prioritize_last.enable");
|
||||
rpc::rpc.mark_safe("f.prioritize_last.disable");
|
||||
rpc::rpc.mark_safe("f.last_touched");
|
||||
rpc::rpc.mark_safe("f.match_depth_prev");
|
||||
rpc::rpc.mark_safe("f.match_depth_next");
|
||||
rpc::rpc.mark_safe("fi.filename_last");
|
||||
rpc::rpc.mark_safe("fi.is_file");
|
||||
}
|
||||
|
||||
@@ -385,4 +385,24 @@ initialize_command_groups() {
|
||||
CMD2_ANY ("choke_group.down.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS,
|
||||
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue))));
|
||||
CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false));
|
||||
|
||||
rpc::rpc.mark_safe("choke_group.list");
|
||||
rpc::rpc.mark_safe("choke_group.size");
|
||||
rpc::rpc.mark_safe("choke_group.index_of");
|
||||
rpc::rpc.mark_safe("choke_group.general.size");
|
||||
rpc::rpc.mark_safe("choke_group.tracker.mode");
|
||||
rpc::rpc.mark_safe("choke_group.up.rate");
|
||||
rpc::rpc.mark_safe("choke_group.down.rate");
|
||||
rpc::rpc.mark_safe("choke_group.up.max");
|
||||
rpc::rpc.mark_safe("choke_group.up.max.unlimited");
|
||||
rpc::rpc.mark_safe("choke_group.up.total");
|
||||
rpc::rpc.mark_safe("choke_group.up.queued");
|
||||
rpc::rpc.mark_safe("choke_group.up.unchoked");
|
||||
rpc::rpc.mark_safe("choke_group.up.heuristics");
|
||||
rpc::rpc.mark_safe("choke_group.down.max");
|
||||
rpc::rpc.mark_safe("choke_group.down.max.unlimited");
|
||||
rpc::rpc.mark_safe("choke_group.down.total");
|
||||
rpc::rpc.mark_safe("choke_group.down.queued");
|
||||
rpc::rpc.mark_safe("choke_group.down.unchoked");
|
||||
rpc::rpc.mark_safe("choke_group.down.heuristics");
|
||||
}
|
||||
|
||||
+37
-3
@@ -120,6 +120,15 @@ group_insert(const torrent::Object::list_type& args) {
|
||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.max", "value", (int64_t)300));
|
||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.upload", "value", (int64_t)20 << 20));
|
||||
|
||||
rpc::rpc.mark_safe("group." + name + ".view");
|
||||
rpc::rpc.mark_safe("group." + name + ".view.set");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.min");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.min.set");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.max");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.max.set");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.upload");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.upload.set");
|
||||
|
||||
if (rpc::call_command_value("method.use_intermediate") == 3) {
|
||||
// Cleaned up in 0.16.1:
|
||||
|
||||
@@ -173,9 +182,13 @@ cmd_file_append(const torrent::Object::list_type& args) {
|
||||
if (output == nullptr)
|
||||
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + std::strerror(errno));
|
||||
|
||||
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
|
||||
|
||||
fprintf(output, "\n");
|
||||
try {
|
||||
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
|
||||
fprintf(output, "\n");
|
||||
} catch (...) {
|
||||
fclose(output);
|
||||
throw;
|
||||
}
|
||||
fclose(output);
|
||||
return torrent::Object();
|
||||
}
|
||||
@@ -319,4 +332,25 @@ initialize_command_local() {
|
||||
CMD2_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3));
|
||||
|
||||
CMD2_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("directory.default");
|
||||
rpc::rpc.mark_safe("session.path");
|
||||
rpc::rpc.mark_safe("session.use_lock");
|
||||
rpc::rpc.mark_safe("session.on_completion");
|
||||
|
||||
rpc::rpc.mark_safe("pieces.sync.always_safe");
|
||||
rpc::rpc.mark_safe("pieces.sync.timeout");
|
||||
rpc::rpc.mark_safe("pieces.sync.timeout_safe");
|
||||
rpc::rpc.mark_safe("pieces.preload.type");
|
||||
rpc::rpc.mark_safe("pieces.preload.min_size");
|
||||
rpc::rpc.mark_safe("pieces.preload.min_rate");
|
||||
rpc::rpc.mark_safe("pieces.memory.max");
|
||||
rpc::rpc.mark_safe("pieces.hash.on_completion");
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#include <functional>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <sys/socket.h>
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
#include <rak/address_info.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/rate.h>
|
||||
@@ -143,6 +147,60 @@ apply_scgi(const std::string& arg, int type) {
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_scgi_systemd() {
|
||||
#ifdef HAVE_SYSTEMD
|
||||
if (scgi_thread::scgi() != nullptr)
|
||||
throw torrent::input_error("SCGI already enabled.");
|
||||
|
||||
int n = sd_listen_fds(0);
|
||||
if (n < 1)
|
||||
throw torrent::input_error("No systemd socket(s) provided (sd_listen_fds returned " +
|
||||
std::to_string(n) + ").");
|
||||
|
||||
// Iterate over all provided fds. Use the first listening stream socket;
|
||||
// close the rest. The systemd docs say unused fds should be closed.
|
||||
int selected_fd = -1;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int fd = SD_LISTEN_FDS_START + i;
|
||||
|
||||
if (selected_fd != -1) {
|
||||
::close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto err = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1);
|
||||
|
||||
if (err < 0) {
|
||||
// Safe to ignore errors here - we just skip it and move on.
|
||||
::close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
// Not the socket we're looking for.
|
||||
::close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
selected_fd = fd;
|
||||
}
|
||||
|
||||
if (selected_fd == -1)
|
||||
throw torrent::input_error("No listening stream socket found among systemd-provided fds.");
|
||||
|
||||
initialize_rpc_handlers();
|
||||
|
||||
rpc::SCgi* scgi = new rpc::SCgi;
|
||||
scgi->open_fd(selected_fd);
|
||||
|
||||
scgi_thread::set_scgi(scgi);
|
||||
return torrent::Object();
|
||||
#else
|
||||
throw torrent::input_error("Systemd SCGI endpoint is not supported.");
|
||||
#endif
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_xmlrpc_dialect(const std::string& arg) {
|
||||
int value;
|
||||
@@ -243,6 +301,7 @@ initialize_command_network() {
|
||||
CMD2_ANY_STRING ("network.scgi.open_port", std::bind(&apply_scgi, std::placeholders::_2, 1));
|
||||
CMD2_ANY_STRING ("network.scgi.open_local", std::bind(&apply_scgi, std::placeholders::_2, 2));
|
||||
CMD2_VAR_BOOL ("network.scgi.dont_route", false);
|
||||
CMD2_ANY ("network.scgi.open_systemd", [](auto, auto) { return apply_scgi_systemd(); });
|
||||
|
||||
CMD2_ANY_STRING ("network.xmlrpc.dialect.set", [](const auto&, const auto& arg) { return apply_xmlrpc_dialect(arg); })
|
||||
CMD2_ANY ("network.xmlrpc.size_limit", [](const auto&, const auto&) { return rpc::rpc.size_limit(); });
|
||||
@@ -261,4 +320,36 @@ initialize_command_network() {
|
||||
CMD2_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); });
|
||||
CMD2_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); });
|
||||
CMD2_ANY_VALUE_V ("network.prefer.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_prefer_ipv6(value); });
|
||||
|
||||
rpc::rpc.mark_safe("network.port_open");
|
||||
rpc::rpc.mark_safe("network.port_random");
|
||||
rpc::rpc.mark_safe("network.port_range");
|
||||
rpc::rpc.mark_safe("network.listen.port");
|
||||
rpc::rpc.mark_safe("network.listen.backlog");
|
||||
|
||||
rpc::rpc.mark_safe("network.http.current_open");
|
||||
rpc::rpc.mark_safe("network.http.max_cache_connections");
|
||||
rpc::rpc.mark_safe("network.http.max_host_connections");
|
||||
rpc::rpc.mark_safe("network.http.max_total_connections");
|
||||
|
||||
rpc::rpc.mark_safe("network.open_files");
|
||||
rpc::rpc.mark_safe("network.max_open_files");
|
||||
rpc::rpc.mark_safe("network.max_open_sockets");
|
||||
rpc::rpc.mark_safe("network.total_handshakes");
|
||||
|
||||
rpc::rpc.mark_safe("network.send_buffer.size");
|
||||
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.xmlrpc.size_limit");
|
||||
rpc::rpc.mark_safe("network.open_sockets");
|
||||
rpc::rpc.mark_safe("network.http.cacert");
|
||||
rpc::rpc.mark_safe("network.http.capath");
|
||||
rpc::rpc.mark_safe("network.http.proxy_address");
|
||||
rpc::rpc.mark_safe("network.proxy_address");
|
||||
rpc::rpc.mark_safe("network.scgi.dont_route");
|
||||
rpc::rpc.mark_safe("protocol.pex");
|
||||
|
||||
rpc::rpc.mark_safe("network.rpc.use_xmlrpc");
|
||||
rpc::rpc.mark_safe("network.rpc.use_jsonrpc");
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ retrieve_p_options_str(torrent::Peer* peer) {
|
||||
|
||||
torrent::Object
|
||||
retrieve_p_completed_percent(torrent::Peer* peer) {
|
||||
if (peer->bitfield()->size_bits() == 0)
|
||||
return int64_t(0);
|
||||
return (100 * peer->bitfield()->size_set()) / peer->bitfield()->size_bits();
|
||||
}
|
||||
|
||||
@@ -98,4 +100,30 @@ initialize_command_peer() {
|
||||
|
||||
CMD2_PEER_V("p.disconnect", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, 0));
|
||||
CMD2_PEER_V("p.disconnect_delayed", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, torrent::ConnectionList::disconnect_delayed));
|
||||
|
||||
rpc::rpc.mark_safe("p.address");
|
||||
rpc::rpc.mark_safe("p.port");
|
||||
rpc::rpc.mark_safe("p.client_version");
|
||||
rpc::rpc.mark_safe("p.options_str");
|
||||
rpc::rpc.mark_safe("p.id");
|
||||
rpc::rpc.mark_safe("p.id_html");
|
||||
rpc::rpc.mark_safe("p.up_rate");
|
||||
rpc::rpc.mark_safe("p.up_total");
|
||||
rpc::rpc.mark_safe("p.down_rate");
|
||||
rpc::rpc.mark_safe("p.down_total");
|
||||
rpc::rpc.mark_safe("p.peer_rate");
|
||||
rpc::rpc.mark_safe("p.peer_total");
|
||||
rpc::rpc.mark_safe("p.is_encrypted");
|
||||
rpc::rpc.mark_safe("p.is_incoming");
|
||||
rpc::rpc.mark_safe("p.is_obfuscated");
|
||||
rpc::rpc.mark_safe("p.is_snubbed");
|
||||
rpc::rpc.mark_safe("p.is_unwanted");
|
||||
rpc::rpc.mark_safe("p.is_preferred");
|
||||
rpc::rpc.mark_safe("p.snubbed");
|
||||
rpc::rpc.mark_safe("p.snubbed.set");
|
||||
rpc::rpc.mark_safe("p.banned");
|
||||
rpc::rpc.mark_safe("p.banned.set");
|
||||
rpc::rpc.mark_safe("p.completed_percent");
|
||||
rpc::rpc.mark_safe("p.disconnect");
|
||||
rpc::rpc.mark_safe("p.disconnect_delayed");
|
||||
}
|
||||
|
||||
@@ -199,4 +199,60 @@ initialize_command_throttle() {
|
||||
CMD2_ANY_STRING ("throttle.up.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_rate));
|
||||
CMD2_ANY_STRING ("throttle.down.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_max));
|
||||
CMD2_ANY_STRING ("throttle.down.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_rate));
|
||||
|
||||
rpc::rpc.mark_safe("throttle.unchoked_uploads");
|
||||
rpc::rpc.mark_safe("throttle.max_unchoked_uploads");
|
||||
rpc::rpc.mark_safe("throttle.unchoked_downloads");
|
||||
rpc::rpc.mark_safe("throttle.max_unchoked_downloads");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.min_peers.normal");
|
||||
rpc::rpc.mark_safe("throttle.min_peers.normal.set");
|
||||
rpc::rpc.mark_safe("throttle.max_peers.normal");
|
||||
rpc::rpc.mark_safe("throttle.max_peers.normal.set");
|
||||
rpc::rpc.mark_safe("throttle.min_peers.seed");
|
||||
rpc::rpc.mark_safe("throttle.min_peers.seed.set");
|
||||
rpc::rpc.mark_safe("throttle.max_peers.seed");
|
||||
rpc::rpc.mark_safe("throttle.max_peers.seed.set");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.min_uploads");
|
||||
rpc::rpc.mark_safe("throttle.min_uploads.set");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.set");
|
||||
rpc::rpc.mark_safe("throttle.min_downloads");
|
||||
rpc::rpc.mark_safe("throttle.min_downloads.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.set");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.div");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.div.set");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.div._val");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.div._val.set");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.global");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.global.set");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.global._val");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.global._val.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.div");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.div.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.div._val");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.div._val.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.global");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.global.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.global._val");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.global._val.set");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.global_up.rate");
|
||||
rpc::rpc.mark_safe("throttle.global_up.total");
|
||||
rpc::rpc.mark_safe("throttle.global_up.max_rate");
|
||||
rpc::rpc.mark_safe("throttle.global_up.max_rate.set");
|
||||
rpc::rpc.mark_safe("throttle.global_up.max_rate.set_kb");
|
||||
rpc::rpc.mark_safe("throttle.global_down.rate");
|
||||
rpc::rpc.mark_safe("throttle.global_down.total");
|
||||
rpc::rpc.mark_safe("throttle.global_down.max_rate");
|
||||
rpc::rpc.mark_safe("throttle.global_down.max_rate.set");
|
||||
rpc::rpc.mark_safe("throttle.global_down.max_rate.set_kb");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.up.max");
|
||||
rpc::rpc.mark_safe("throttle.up.rate");
|
||||
rpc::rpc.mark_safe("throttle.down.max");
|
||||
rpc::rpc.mark_safe("throttle.down.rate");
|
||||
}
|
||||
|
||||
@@ -151,4 +151,35 @@ initialize_command_tracker() {
|
||||
|
||||
CMD2_ANY_STRING ("dht.add_node", std::bind(&apply_dht_add_node, std::placeholders::_2));
|
||||
CMD2_ANY ("dht.statistics", std::bind(&core::DhtManager::dht_statistics, dht_manager));
|
||||
|
||||
rpc::rpc.mark_safe("t.url");
|
||||
rpc::rpc.mark_safe("t.group");
|
||||
rpc::rpc.mark_safe("t.id");
|
||||
rpc::rpc.mark_safe("t.type");
|
||||
rpc::rpc.mark_safe("t.is_usable");
|
||||
rpc::rpc.mark_safe("t.is_busy");
|
||||
rpc::rpc.mark_safe("t.is_enabled");
|
||||
rpc::rpc.mark_safe("t.is_enabled.set");
|
||||
rpc::rpc.mark_safe("t.is_extra_tracker");
|
||||
rpc::rpc.mark_safe("t.is_open");
|
||||
rpc::rpc.mark_safe("t.normal_interval");
|
||||
rpc::rpc.mark_safe("t.scrape_time_last");
|
||||
rpc::rpc.mark_safe("t.scrape_counter");
|
||||
rpc::rpc.mark_safe("t.success_time_last");
|
||||
rpc::rpc.mark_safe("t.success_counter");
|
||||
rpc::rpc.mark_safe("t.failed_time_last");
|
||||
rpc::rpc.mark_safe("t.failed_counter");
|
||||
rpc::rpc.mark_safe("t.activity_time_last");
|
||||
rpc::rpc.mark_safe("t.activity_time_next");
|
||||
rpc::rpc.mark_safe("t.scrape_complete");
|
||||
rpc::rpc.mark_safe("t.scrape_incomplete");
|
||||
rpc::rpc.mark_safe("t.scrape_downloaded");
|
||||
|
||||
rpc::rpc.mark_safe("dht.mode.set");
|
||||
rpc::rpc.mark_safe("dht.port");
|
||||
rpc::rpc.mark_safe("dht.override_port");
|
||||
rpc::rpc.mark_safe("dht.add_node");
|
||||
rpc::rpc.mark_safe("dht.statistics");
|
||||
rpc::rpc.mark_safe("trackers.numwant");
|
||||
rpc::rpc.mark_safe("trackers.use_udp");
|
||||
}
|
||||
|
||||
@@ -888,4 +888,28 @@ initialize_command_ui() {
|
||||
return control->object_storage()->get_str(display::color_vars[color_id]);
|
||||
});
|
||||
}
|
||||
|
||||
rpc::rpc.mark_safe("view.set_visible");
|
||||
rpc::rpc.mark_safe("view.set_not_visible");
|
||||
|
||||
rpc::rpc.mark_safe("cat");
|
||||
rpc::rpc.mark_safe("if");
|
||||
rpc::rpc.mark_safe("branch");
|
||||
rpc::rpc.mark_safe("and");
|
||||
rpc::rpc.mark_safe("or");
|
||||
rpc::rpc.mark_safe("not");
|
||||
rpc::rpc.mark_safe("value");
|
||||
rpc::rpc.mark_safe("compare");
|
||||
rpc::rpc.mark_safe("elapsed.less");
|
||||
rpc::rpc.mark_safe("elapsed.greater");
|
||||
|
||||
rpc::rpc.mark_safe("convert.gm_time");
|
||||
rpc::rpc.mark_safe("convert.gm_date");
|
||||
rpc::rpc.mark_safe("convert.time");
|
||||
rpc::rpc.mark_safe("convert.date");
|
||||
rpc::rpc.mark_safe("convert.elapsed_time");
|
||||
rpc::rpc.mark_safe("convert.kb");
|
||||
rpc::rpc.mark_safe("convert.mb");
|
||||
rpc::rpc.mark_safe("convert.xb");
|
||||
rpc::rpc.mark_safe("convert.throttle");
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ Download::set_root_directory(const std::string& path) {
|
||||
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 atter the files have been moved.");
|
||||
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);
|
||||
|
||||
@@ -361,7 +361,7 @@ Frame::balance_row(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||
(*itr)->balance(x, y, m_width, std::min((*itr)->m_height, height));
|
||||
|
||||
y += (*itr)->m_height;
|
||||
height -= (*itr)->m_height;
|
||||
height -= std::min(height, (*itr)->m_height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ Frame::balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||
(*itr)->balance(x, y, std::min((*itr)->m_width, width), m_height);
|
||||
|
||||
x += (*itr)->m_width;
|
||||
width -= (*itr)->m_width;
|
||||
width -= std::min(width, (*itr)->m_width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ print_download_status(char* first, char* last, core::Download* d) {
|
||||
|
||||
if (d->is_hash_checking()) {
|
||||
first = print_buffer(first, last, "Checking hash [%2i%%]",
|
||||
(d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks());
|
||||
d->download()->file_list()->size_chunks() != 0 ? (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks() : 0);
|
||||
|
||||
} else if (d->tracker_controller().has_active_trackers_not_scrape()) {
|
||||
auto tracker = d->tracker_controller().find_if([](const auto& t) {
|
||||
@@ -208,7 +208,7 @@ print_download_info_compact(char* first, char* last, core::Download* d) {
|
||||
if (d->is_done())
|
||||
first = print_buffer(first, last, " 100%% ");
|
||||
else if (d->is_open())
|
||||
first = print_buffer(first, last, " %2u%% ",(d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks());
|
||||
first = print_buffer(first, last, " %2u%% ", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
|
||||
else
|
||||
first = print_buffer(first, last, " ");
|
||||
|
||||
@@ -260,7 +260,7 @@ print_download_percentage_done(char* first, char* last, core::Download* d) {
|
||||
//return print_buffer(first, last, "[--%%]");
|
||||
return print_buffer(first, last, " ");
|
||||
else
|
||||
return print_buffer(first, last, "[%2u%%]", (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks());
|
||||
return print_buffer(first, last, "[%2u%%]", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
|
||||
}
|
||||
|
||||
char*
|
||||
|
||||
@@ -19,7 +19,7 @@ WindowHttpQueue::WindowHttpQueue(core::HttpQueue* q) :
|
||||
set_active(false);
|
||||
|
||||
m_conn_insert = m_queue->signal_insert().insert(m_queue->signal_insert().end(), [this](auto h) { receive_insert(h); });
|
||||
m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_insert().end(), [this](auto h) { receive_erase(h); });
|
||||
m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_erase().end(), [this](auto h) { receive_erase(h); });
|
||||
|
||||
m_task_deactivate.slot() = [this] {
|
||||
if (!m_container.empty())
|
||||
|
||||
@@ -12,7 +12,7 @@ WindowInput::redraw() {
|
||||
m_canvas->erase();
|
||||
m_canvas->print(0, 0, "%s> %s", m_title.c_str(), m_input != NULL ? m_input->c_str() : "<NULL>");
|
||||
|
||||
if (m_focus)
|
||||
if (m_focus && m_input != NULL)
|
||||
m_canvas->set_attr(m_input->get_pos() + 2 + m_title.size(), 0, 1, A_REVERSE, COLOR_PAIR(0));
|
||||
}
|
||||
|
||||
|
||||
@@ -419,6 +419,7 @@ main(int argc, char** argv) {
|
||||
// Deprecate:
|
||||
|
||||
CMD2_VAR_STRING("dht.throttle.name", "deprecated");
|
||||
rpc::rpc.mark_safe("dht.throttle.name");
|
||||
|
||||
CMD2_REDIRECT("network.http.max_open", "network.http.max_total_connections");
|
||||
CMD2_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set");
|
||||
|
||||
+4
-35
@@ -1,37 +1,3 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2011, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -137,8 +103,11 @@ void
|
||||
OptionParser::call_int_pair(slot_int_pair slot, const std::string& arg) {
|
||||
int a, b;
|
||||
|
||||
if (std::sscanf(arg.c_str(), "%u-%u", &a, &b) != 2)
|
||||
if (std::sscanf(arg.c_str(), "%d-%d", &a, &b) != 2)
|
||||
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be \"a-b\"");
|
||||
|
||||
if (a < 0 || b < 0)
|
||||
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be positive numbers");
|
||||
|
||||
slot(a, b);
|
||||
}
|
||||
|
||||
@@ -98,11 +98,17 @@ CommandMap::call_command(const key_type& key, const mapped_type& arg, const targ
|
||||
if (itr == base_type::end())
|
||||
throw torrent::input_error("Command \"" + std::string(key) + "\" does not exist.");
|
||||
|
||||
if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
|
||||
throw untrusted_error("Command \"" + std::string(key) + "\" is not allowed for untrusted connections.");
|
||||
|
||||
return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
|
||||
}
|
||||
|
||||
const CommandMap::mapped_type
|
||||
CommandMap::call_command(iterator itr, const mapped_type& arg, const target_type& target) {
|
||||
if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
|
||||
throw untrusted_error("Command \"" + itr->first + "\" is not allowed for untrusted connections.");
|
||||
|
||||
return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
static const int flag_file_target = 0x100;
|
||||
static const int flag_tracker_target = 0x200;
|
||||
|
||||
static const int flag_untrusted_safe = 0x400;
|
||||
|
||||
CommandMap() = default;
|
||||
|
||||
bool has(const std::string& key) const { return base_type::find(key) != base_type::end(); }
|
||||
|
||||
@@ -41,8 +41,13 @@ ExecFile::execute(const char* file, char* const* argv, int flags) {
|
||||
|
||||
pid_t childPid = fork();
|
||||
|
||||
if (childPid == -1)
|
||||
if (childPid == -1) {
|
||||
if (flags & flag_capture) {
|
||||
::close(pipeFd[0]);
|
||||
::close(pipeFd[1]);
|
||||
}
|
||||
throw torrent::input_error("ExecFile::execute(...) Fork failed.");
|
||||
}
|
||||
|
||||
if (childPid == 0) {
|
||||
if (flags & flag_background) {
|
||||
|
||||
+6
-3
@@ -133,9 +133,12 @@ jsonrpc_call_command(const std::string& method, const json& params) {
|
||||
|
||||
params_object_list.erase(params_object_list.begin());
|
||||
|
||||
const auto& result = rpc::commands.call_command(itr, params_object, target);
|
||||
|
||||
return object_to_json(result);
|
||||
try {
|
||||
const auto& result = rpc::commands.call_command(itr, params_object, target);
|
||||
return object_to_json(result);
|
||||
} catch (untrusted_error& e) {
|
||||
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
json
|
||||
|
||||
+9
-3
@@ -120,11 +120,17 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
||||
case 'b':
|
||||
case 'B': ++last; break;
|
||||
case 'k':
|
||||
case 'K': *value = *value << 10; ++last; break;
|
||||
case 'K':
|
||||
if (*value > (int64_t)0x1FFFFFFFFFFFFF) return src; // overflow guard
|
||||
*value = *value << 10; ++last; break;
|
||||
case 'm':
|
||||
case 'M': *value = *value << 20; ++last; break;
|
||||
case 'M':
|
||||
if (*value > (int64_t)0x7FFFFFFFFFF) return src; // overflow guard
|
||||
*value = *value << 20; ++last; break;
|
||||
case 'g':
|
||||
case 'G': *value = *value << 30; ++last; break;
|
||||
case 'G':
|
||||
if (*value > (int64_t)0x1FFFFFFFF) 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.");
|
||||
|
||||
@@ -13,6 +13,19 @@ CommandMap commands;
|
||||
RpcManager rpc;
|
||||
ExecFile execFile;
|
||||
|
||||
// Trusted/untrusted XMLRPC connection model.
|
||||
//
|
||||
// The trust state is set per-request by the SCGI layer based on the
|
||||
// UNTRUSTED_CONNECTION header. Commands without flag_untrusted_safe
|
||||
// are blocked for untrusted connections. The check is in
|
||||
// CommandMap::call_command(), which catches all command execution
|
||||
// including nested calls through argument expansion.
|
||||
|
||||
bool
|
||||
RpcManager::is_trusted() const {
|
||||
return m_trusted;
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* target, std::function<void()>* deleter) {
|
||||
if (!obj.is_string())
|
||||
@@ -124,6 +137,21 @@ RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_r
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RpcManager::process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
|
||||
bool previous = m_trusted;
|
||||
m_trusted = false;
|
||||
|
||||
try {
|
||||
bool result = process(type, in_buffer, length, callback);
|
||||
m_trusted = previous;
|
||||
return result;
|
||||
} catch (...) {
|
||||
m_trusted = previous;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::initialize_handlers() {
|
||||
if (m_handlers_initialized)
|
||||
@@ -175,4 +203,14 @@ RpcManager::insert_command(const char* name, const char* parm, const char* doc)
|
||||
m_jsonrpc.insert_command(name, parm, doc);
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::mark_safe(const std::string& key) {
|
||||
auto itr = commands.find(key);
|
||||
|
||||
if (itr == commands.end())
|
||||
return;
|
||||
|
||||
itr->second.m_flags |= CommandMap::flag_untrusted_safe;
|
||||
}
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "rpc/command.h"
|
||||
#include "rpc/command_map.h"
|
||||
@@ -34,6 +35,12 @@ private:
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
class untrusted_error : public torrent::input_error {
|
||||
public:
|
||||
using torrent::input_error::input_error;
|
||||
virtual ~untrusted_error() throw() = default;
|
||||
};
|
||||
|
||||
class RpcManager {
|
||||
public:
|
||||
using slot_download = std::function<core::Download*(const char*)>;
|
||||
@@ -63,17 +70,26 @@ public:
|
||||
void set_type_enabled(RPCType type, bool enabled);
|
||||
|
||||
bool process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
|
||||
bool process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
|
||||
|
||||
void insert_command(const char* name, const char* parm, const char* doc);
|
||||
void mark_safe(const std::string& key);
|
||||
|
||||
slot_download& slot_find_download() { return m_slot_find_download; }
|
||||
slot_file& slot_find_file() { return m_slot_find_file; }
|
||||
slot_tracker& slot_find_tracker() { return m_slot_find_tracker; }
|
||||
slot_peer& slot_find_peer() { return m_slot_find_peer; }
|
||||
|
||||
// Trusted/untrusted XMLRPC connection model.
|
||||
// When an SCGI request includes the UNTRUSTED_CONNECTION header,
|
||||
// commands without flag_untrusted_safe are blocked.
|
||||
bool is_trusted() const;
|
||||
|
||||
static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target, std::function<void()>* deleter);
|
||||
|
||||
private:
|
||||
bool m_trusted{true};
|
||||
|
||||
XmlRpc m_xmlrpc;
|
||||
JsonRpc m_jsonrpc;
|
||||
|
||||
|
||||
+43
-8
@@ -1,6 +1,8 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/un.h>
|
||||
#include <torrent/connection_manager.h>
|
||||
@@ -20,6 +22,12 @@
|
||||
|
||||
namespace rpc {
|
||||
|
||||
SCgi::SCgi() {
|
||||
std::generate(m_tasks.begin(), m_tasks.end(), []() { return std::make_unique<SCgiTask>(); });
|
||||
|
||||
m_current = m_tasks.begin();
|
||||
}
|
||||
|
||||
SCgi::~SCgi() {
|
||||
assert(!is_open() && "SCgi::~SCgi() called while open");
|
||||
}
|
||||
@@ -73,6 +81,19 @@ SCgi::open_named(const std::string& filename) {
|
||||
m_path = filename;
|
||||
}
|
||||
|
||||
void
|
||||
SCgi::open_fd(int fd) {
|
||||
torrent::runtime::socket_manager()->open_event_or_throw(this, [&]() {
|
||||
if (!torrent::fd_set_nonblock(fd))
|
||||
throw torrent::resource_error("Could not set non-blocking on systemd fd: " +
|
||||
std::string(std::strerror(errno)));
|
||||
set_file_descriptor(fd);
|
||||
// fd is already bound and listening; no bind()/listen() needed.
|
||||
});
|
||||
|
||||
torrent::connection_manager()->inc_socket_count();
|
||||
}
|
||||
|
||||
void
|
||||
SCgi::open(sockaddr* sa, unsigned int length) {
|
||||
try {
|
||||
@@ -106,9 +127,10 @@ SCgi::stop() {
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
for (SCgiTask* itr = m_task, *last = m_task + max_tasks; itr != last; ++itr)
|
||||
for (auto& itr : m_tasks) {
|
||||
if (itr->is_open())
|
||||
itr->close();
|
||||
}
|
||||
|
||||
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
@@ -125,10 +147,19 @@ SCgi::stop() {
|
||||
|
||||
void
|
||||
SCgi::event_read() {
|
||||
while (true) {
|
||||
auto* task = std::find_if(m_task, m_task + max_tasks, std::mem_fn(&SCgiTask::is_available));
|
||||
if (m_current < m_tasks.begin() || m_current >= m_tasks.end())
|
||||
throw torrent::internal_error("SCgi::event_read() m_current is out of bounds");
|
||||
|
||||
if (task == m_task + max_tasks) {
|
||||
while (true) {
|
||||
// TODO: Optimize this by keeping track of count.
|
||||
auto prev = m_current;
|
||||
|
||||
m_current = std::find_if(m_current + 1, m_tasks.end(), [](const auto& task) { return !task->is_open(); });
|
||||
|
||||
if (m_current == m_tasks.end())
|
||||
m_current = std::find_if(m_tasks.begin(), prev, [](const auto& task) { return !task->is_open(); });
|
||||
|
||||
if (m_current == prev) {
|
||||
// TODO: Currently just close, although we should remove ourselves from read.
|
||||
int fd = torrent::fd_accept(file_descriptor());
|
||||
|
||||
@@ -138,11 +169,15 @@ SCgi::event_read() {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto open_func = [this, task]() {
|
||||
auto open_func = [this, task = m_current->get()]() {
|
||||
int fd = torrent::fd_accept(file_descriptor());
|
||||
|
||||
if (fd == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
return;
|
||||
|
||||
// Force a new event_read() call just to be sure we don't enter an infinite loop.
|
||||
if (errno == ECONNABORTED)
|
||||
return;
|
||||
|
||||
throw torrent::resource_error("Listener port accept() failed: " + std::string(std::strerror(errno)));
|
||||
@@ -151,11 +186,11 @@ SCgi::event_read() {
|
||||
task->open(this, fd);
|
||||
};
|
||||
|
||||
auto cleanup_func = [task]() {
|
||||
auto cleanup_func = [task = m_current->get()]() {
|
||||
task->cancel_open();
|
||||
};
|
||||
|
||||
bool result = torrent::runtime::socket_manager()->open_event_or_cleanup(task, open_func, cleanup_func);
|
||||
bool result = torrent::runtime::socket_manager()->open_event_or_cleanup(m_current->get(), open_func, cleanup_func);
|
||||
|
||||
if (!result)
|
||||
break;
|
||||
|
||||
+9
-1
@@ -1,7 +1,9 @@
|
||||
#ifndef RTORRENT_RPC_SCGI_H
|
||||
#define RTORRENT_RPC_SCGI_H
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <torrent/event.h>
|
||||
|
||||
#include "rpc/scgi_task.h"
|
||||
@@ -12,12 +14,14 @@ class SCgi : public torrent::Event {
|
||||
public:
|
||||
static const int max_tasks = 100;
|
||||
|
||||
SCgi();
|
||||
~SCgi() override;
|
||||
|
||||
const char* type_name() const override { return "scgi"; }
|
||||
|
||||
void open_port(sockaddr* sa, unsigned int length, bool dont_route);
|
||||
void open_named(const std::string& filename);
|
||||
void open_fd(int fd);
|
||||
|
||||
void activate();
|
||||
|
||||
@@ -33,11 +37,15 @@ public:
|
||||
void event_error() override;
|
||||
|
||||
private:
|
||||
using task_list = std::array<std::unique_ptr<SCgiTask>, max_tasks>;
|
||||
|
||||
void open(sockaddr* sa, unsigned int length);
|
||||
|
||||
std::string m_path;
|
||||
int m_logFd{-1};
|
||||
SCgiTask m_task[max_tasks];
|
||||
|
||||
task_list m_tasks;
|
||||
task_list::iterator m_current;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+26
-22
@@ -43,12 +43,10 @@ SCgiTask::cancel_open() {
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
|
||||
torrent::fd_close(file_descriptor());
|
||||
set_file_descriptor(-1);
|
||||
});
|
||||
torrent::fd_close(file_descriptor());
|
||||
set_file_descriptor(-1);
|
||||
};
|
||||
|
||||
void
|
||||
@@ -113,6 +111,9 @@ SCgiTask::event_read() {
|
||||
size_t content_length = 0;
|
||||
const char* header_end = current + header_size;
|
||||
|
||||
// Assume trusted until we find the UNTRUSTED_CONNECTION header.
|
||||
m_trusted = true;
|
||||
|
||||
// Parse out the null-terminated header keys and values, with
|
||||
// checks to ensure it doesn't scan beyond the limits of the
|
||||
// header
|
||||
@@ -143,6 +144,8 @@ SCgiTask::event_read() {
|
||||
goto event_read_failed;
|
||||
} else if (strcmp(key, "CONTENT_TYPE") == 0) {
|
||||
content_type = value;
|
||||
} else if (strcmp(key, "UNTRUSTED_CONNECTION") == 0 && strcmp(value, "1") == 0) {
|
||||
m_trusted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +205,7 @@ event_read_failed:
|
||||
|
||||
void
|
||||
SCgiTask::event_write() {
|
||||
int bytes = ::send(m_fileDesc, m_position, m_buffer_size, MSG_NOSIGNAL);
|
||||
int bytes = ::send(m_fileDesc, m_position, m_buffer_size, 0);
|
||||
|
||||
if (bytes == -1) {
|
||||
if (!(errno == EAGAIN || errno == EINTR))
|
||||
@@ -272,6 +275,7 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
|
||||
// TODO: Rewrite RpcManager.process to pass the result buffer instead of having to copy it.
|
||||
|
||||
auto scgi_thread = torrent::utils::Thread::self();
|
||||
bool trusted = m_trusted;
|
||||
|
||||
auto result_callback = [this, scgi_thread](const char* b, uint32_t l) {
|
||||
receive_write(b, l);
|
||||
@@ -287,30 +291,30 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
|
||||
|
||||
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
||||
|
||||
RpcManager::RPCType rpc_type;
|
||||
|
||||
switch (content_type()) {
|
||||
case rpc::SCgiTask::ContentType::JSON:
|
||||
torrent::main_thread::thread()->callback_interrupt_polling(this, [buffer, length, result_callback]() {
|
||||
rpc.process(RpcManager::RPCType::JSON, buffer, length,
|
||||
[result_callback](const char* b, uint32_t l) {
|
||||
result_callback(b, l);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
rpc_type = RpcManager::RPCType::JSON;
|
||||
break;
|
||||
|
||||
case rpc::SCgiTask::ContentType::XML:
|
||||
torrent::main_thread::thread()->callback_interrupt_polling(this, [buffer, length, result_callback]() {
|
||||
rpc.process(RpcManager::RPCType::XML, buffer, length,
|
||||
[result_callback](const char* b, uint32_t l) {
|
||||
result_callback(b, l);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
rpc_type = RpcManager::RPCType::XML;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw torrent::internal_error("SCgiTask::receive_call(...) received bad input.");
|
||||
}
|
||||
|
||||
torrent::main_thread::thread()->callback_interrupt_polling(this, [buffer, length, result_callback, trusted, rpc_type]() {
|
||||
auto callback = [result_callback](const char* b, uint32_t l) {
|
||||
result_callback(b, l);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (trusted)
|
||||
rpc.process(rpc_type, buffer, length, callback);
|
||||
else
|
||||
rpc.process_untrusted(rpc_type, buffer, length, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -53,6 +53,7 @@ private:
|
||||
unsigned int m_buffer_size{0};
|
||||
|
||||
ContentType m_content_type{ XML };
|
||||
bool m_trusted{true};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -385,6 +385,10 @@ xmlrpc_call_command(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) {
|
||||
|
||||
return object_to_xmlrpc(env, rpc::commands.call_command(itr, object, target));
|
||||
|
||||
} catch (untrusted_error& e) {
|
||||
xmlrpc_env_set_fault(env, XMLRPC_REQUEST_REFUSED_ERROR, e.what());
|
||||
return NULL;
|
||||
|
||||
} catch (xmlrpc_error_c& e) {
|
||||
xmlrpc_env_set_fault(env, e.type(), e.what());
|
||||
return NULL;
|
||||
|
||||
@@ -31,7 +31,7 @@ const int XMLRPC_PARSE_ERROR = -503;
|
||||
// const int XMLRPC_NETWORK_ERROR = -504;
|
||||
// const int XMLRPC_TIMEOUT_ERROR = -505;
|
||||
const int XMLRPC_NO_SUCH_METHOD_ERROR = -506;
|
||||
// const int XMLRPC_REQUEST_REFUSED_ERROR = -507;
|
||||
const int XMLRPC_REQUEST_REFUSED_ERROR = -507;
|
||||
// const int XMLRPC_INTROSPECTION_DISABLED_ERROR = -508;
|
||||
const int XMLRPC_LIMIT_EXCEEDED_ERROR = -509;
|
||||
// const int XMLRPC_INVALID_UTF8_ERROR = -510;
|
||||
@@ -238,7 +238,11 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
||||
throw rpc_error(XMLRPC_TYPE_ERROR, "invalid parameters: too few");
|
||||
}
|
||||
|
||||
return rpc::commands.call_command(cmd_itr, params_raw, target);
|
||||
try {
|
||||
return rpc::commands.call_command(cmd_itr, params_raw, target);
|
||||
} catch (untrusted_error& e) {
|
||||
throw rpc_error(XMLRPC_REQUEST_REFUSED_ERROR, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -112,8 +112,10 @@ SessionManager::save_full_download(core::Download* download) {
|
||||
if (!m_active)
|
||||
throw torrent::internal_error("SessionManager::save_download() called while not active.");
|
||||
|
||||
if (replace_save_request_unsafe(save_request))
|
||||
throw torrent::internal_error("SessionManager::save_full_download() replacing existing save request, not supported?");
|
||||
if (replace_save_request_unsafe(save_request)) {
|
||||
LT_LOG("updated pending save request with full save data : download:%p", download);
|
||||
return;
|
||||
}
|
||||
|
||||
m_save_requests.push_back(std::move(save_request));
|
||||
m_save_request_counter = m_save_requests.size();
|
||||
@@ -426,11 +428,22 @@ SessionManager::replace_save_request_unsafe(SaveRequest& save_request) {
|
||||
if (itr->path != save_request.path)
|
||||
throw torrent::internal_error("SessionManager::replace_save_request_unsafe() path mismatch on replace: " + itr->path + " != " + save_request.path);
|
||||
|
||||
if (save_request.torrent_stream != nullptr)
|
||||
throw torrent::internal_error("SessionManager::replace_save_request_unsafe() cannot replace full save requests.");
|
||||
|
||||
itr->rtorrent_stream = std::move(save_request.rtorrent_stream);
|
||||
itr->libtorrent_stream = std::move(save_request.libtorrent_stream);
|
||||
// If the existing request is a full save (created during torrent initialization),
|
||||
// keep its torrent_stream but update the resume streams with newer data.
|
||||
// Otherwise, replace all streams.
|
||||
if (itr->torrent_stream != nullptr) {
|
||||
// Keep existing full-save torrent stream, only update resume streams
|
||||
if (save_request.rtorrent_stream != nullptr)
|
||||
itr->rtorrent_stream = std::move(save_request.rtorrent_stream);
|
||||
|
||||
if (save_request.libtorrent_stream != nullptr)
|
||||
itr->libtorrent_stream = std::move(save_request.libtorrent_stream);
|
||||
} else {
|
||||
// No full save pending, replace everything
|
||||
itr->torrent_stream = std::move(save_request.torrent_stream);
|
||||
itr->rtorrent_stream = std::move(save_request.rtorrent_stream);
|
||||
itr->libtorrent_stream = std::move(save_request.libtorrent_stream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -167,6 +167,8 @@ ElementDownloadList::receive_home() {
|
||||
|
||||
void
|
||||
ElementDownloadList::receive_end() {
|
||||
if (m_view->size_visible() == 0)
|
||||
return;
|
||||
m_view->set_focus(m_view->end_visible() - 1);
|
||||
m_view->set_last_changed();
|
||||
}
|
||||
@@ -221,10 +223,10 @@ ElementDownloadList::receive_change_view(const std::string& name) {
|
||||
|
||||
std::string old_name = view() ? view()->name() : "";
|
||||
if (!old_name.empty())
|
||||
rpc::commands.call_catch("event.view.hide", rpc::make_target(), name, "View hide event action failed: ");
|
||||
rpc::commands.call_catch("event.view.hide", rpc::make_target(), old_name, "View hide event action failed: ");
|
||||
set_view(*itr);
|
||||
if (!old_name.empty())
|
||||
rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name, "View show event action failed: ");
|
||||
if (!name.empty())
|
||||
rpc::commands.call_catch("event.view.show", rpc::make_target(), name, "View show event action failed: ");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+1
-1
@@ -496,7 +496,7 @@ Root::set_keymap_style(const std::string& style) {
|
||||
} else if (style == "emacs") {
|
||||
m_keymap = emacs_keymap;
|
||||
} else {
|
||||
throw torrent::input_error("Root::set_keymap_style() -> ui.keymap.style is configured with unknown keymap style: " + m_keymap_style);
|
||||
throw torrent::input_error("Root::set_keymap_style() -> ui.keymap.style is configured with unknown keymap style: " + style);
|
||||
}
|
||||
|
||||
m_keymap_style = style;
|
||||
|
||||
Reference in New Issue
Block a user