mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-15 06:32:31 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 877e5f6ed3 | |||
| ee64f8ea50 | |||
| bf1265d954 | |||
| 6c3c77a2b7 | |||
| 120fcb0273 | |||
| c36956813d | |||
| 05563b4c9b | |||
| a544f8ad68 | |||
| b35413ea71 | |||
| f9f19fb041 |
@@ -53,8 +53,8 @@ rc.execute.throw(
|
|||||||
cfg.watch..'/start'}))
|
cfg.watch..'/start'}))
|
||||||
|
|
||||||
-- Listening port for incoming peer traffic (fixed; you can also randomize it)
|
-- Listening port for incoming peer traffic (fixed; you can also randomize it)
|
||||||
rc.network.listen.port.range = '50000-50000'
|
rc.network.port_range = '50000-50000'
|
||||||
rc.network.listen.port.random = false
|
rc.network.port_random = false
|
||||||
|
|
||||||
-- Tracker-less torrent and UDP tracker support
|
-- Tracker-less torrent and UDP tracker support
|
||||||
-- (conservative settings for 'private' trackers, change for 'public')
|
-- (conservative settings for 'private' trackers, change for 'public')
|
||||||
|
|||||||
+9
-11
@@ -1,7 +1,5 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <torrent/download/resource_manager.h>
|
#include <torrent/download/resource_manager.h>
|
||||||
#include <torrent/download/choke_group.h>
|
#include <torrent/download/choke_group.h>
|
||||||
#include <torrent/download/choke_queue.h>
|
#include <torrent/download/choke_queue.h>
|
||||||
@@ -113,7 +111,7 @@ apply_cg_all_update_balance(bool is_up) {
|
|||||||
//
|
//
|
||||||
#else
|
#else
|
||||||
|
|
||||||
std::vector<std::unique_ptr<torrent::choke_group>> cg_list_hack;
|
std::vector<torrent::choke_group*> cg_list_hack;
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
cg_get_index(const torrent::Object& raw_args) {
|
cg_get_index(const torrent::Object& raw_args) {
|
||||||
@@ -123,7 +121,7 @@ cg_get_index(const torrent::Object& raw_args) {
|
|||||||
|
|
||||||
if (arg.is_string()) {
|
if (arg.is_string()) {
|
||||||
if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) {
|
if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) {
|
||||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg.as_string() == cg->name(); });
|
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg.as_string() == cg->name(); });
|
||||||
|
|
||||||
if (itr == cg_list_hack.end())
|
if (itr == cg_list_hack.end())
|
||||||
throw torrent::input_error("Choke group not found.");
|
throw torrent::input_error("Choke group not found.");
|
||||||
@@ -151,7 +149,7 @@ cg_get_group(const torrent::Object& raw_args) {
|
|||||||
if ((size_t)index >= cg_list_hack.size())
|
if ((size_t)index >= cg_list_hack.size())
|
||||||
throw torrent::input_error("Choke group not found.");
|
throw torrent::input_error("Choke group not found.");
|
||||||
|
|
||||||
return cg_list_hack.at(index).get();
|
return cg_list_hack.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t cg_d_group(core::Download* download) { return download->group(); }
|
int64_t cg_d_group(core::Download* download) { return download->group(); }
|
||||||
@@ -164,7 +162,7 @@ torrent::Object
|
|||||||
apply_cg_list() {
|
apply_cg_list() {
|
||||||
torrent::Object::list_type result;
|
torrent::Object::list_type result;
|
||||||
|
|
||||||
for (const auto& itr : cg_list_hack)
|
for (auto itr : cg_list_hack)
|
||||||
result.push_back(itr->name());
|
result.push_back(itr->name());
|
||||||
|
|
||||||
return torrent::Object::from_list(result);
|
return torrent::Object::from_list(result);
|
||||||
@@ -182,10 +180,10 @@ apply_cg_insert(const std::string& arg) {
|
|||||||
if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
|
if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
|
||||||
throw torrent::input_error("Cannot use a value string as choke group name.");
|
throw torrent::input_error("Cannot use a value string as choke group name.");
|
||||||
|
|
||||||
if (arg.empty() || std::any_of(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg == cg->name(); }))
|
if (arg.empty() || std::any_of(cg_list_hack.begin(), cg_list_hack.end(), [&arg](auto cg) { return arg == cg->name(); }))
|
||||||
throw torrent::input_error("Duplicate name for choke group.");
|
throw torrent::input_error("Duplicate name for choke group.");
|
||||||
|
|
||||||
cg_list_hack.push_back(std::make_unique<torrent::choke_group>());
|
cg_list_hack.push_back(new torrent::choke_group());
|
||||||
cg_list_hack.back()->set_name(arg);
|
cg_list_hack.back()->set_name(arg);
|
||||||
|
|
||||||
cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
|
cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
|
||||||
@@ -196,7 +194,7 @@ apply_cg_insert(const std::string& arg) {
|
|||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
apply_cg_index_of(const std::string& arg) {
|
apply_cg_index_of(const std::string& arg) {
|
||||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg == cg->name(); });
|
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg == cg->name(); });
|
||||||
|
|
||||||
if (itr == cg_list_hack.end())
|
if (itr == cg_list_hack.end())
|
||||||
throw torrent::input_error("Choke group not found.");
|
throw torrent::input_error("Choke group not found.");
|
||||||
@@ -208,7 +206,7 @@ torrent::Object
|
|||||||
apply_cg_all_update_balance(bool is_up) {
|
apply_cg_all_update_balance(bool is_up) {
|
||||||
LT_LOG_SUBSYSTEM("apply update balance: hack is_up:%i", (int)is_up);
|
LT_LOG_SUBSYSTEM("apply update balance: hack is_up:%i", (int)is_up);
|
||||||
|
|
||||||
for (const auto& itr : cg_list_hack) {
|
for (auto itr : cg_list_hack) {
|
||||||
if (is_up)
|
if (is_up)
|
||||||
itr->up_queue()->balance();
|
itr->up_queue()->balance();
|
||||||
else
|
else
|
||||||
@@ -348,7 +346,7 @@ initialize_command_groups() {
|
|||||||
#else
|
#else
|
||||||
apply_cg_insert("default");
|
apply_cg_insert("default");
|
||||||
|
|
||||||
CMD_ANY ("choke_group.size", [](auto, auto) { return (int64_t)cg_list_hack.size(); });
|
CMD_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
|
||||||
CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
|
CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr int64_t max_string_pad_size = 1 << 14;
|
|
||||||
|
|
||||||
const std::string whitespace_characters = " \t\n\r\f\v";
|
const std::string whitespace_characters = " \t\n\r\f\v";
|
||||||
|
|
||||||
// The byte offset of every utf-8 character in 'text', terminated by the offset
|
// The byte offset of every utf-8 character in 'text', terminated by the offset
|
||||||
@@ -251,14 +249,10 @@ apply_string_pad(const char* name, const torrent::Object::list_type& args, bool
|
|||||||
if (pad_size <= text_length || padding.empty())
|
if (pad_size <= text_length || padding.empty())
|
||||||
return text;
|
return text;
|
||||||
|
|
||||||
if (pad_size - text_length > max_string_pad_size)
|
|
||||||
throw torrent::input_error(std::string(name) + ": padding is too large.");
|
|
||||||
|
|
||||||
auto padding_offsets = utf8_offsets(padding);
|
auto padding_offsets = utf8_offsets(padding);
|
||||||
auto padding_length = static_cast<int64_t>(padding_offsets.size() - 1);
|
auto padding_length = static_cast<int64_t>(padding_offsets.size() - 1);
|
||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
result.reserve(pad_size - text_length);
|
|
||||||
|
|
||||||
for (int64_t i = 0; i < pad_size - text_length; i++) {
|
for (int64_t i = 0; i < pad_size - text_length; i++) {
|
||||||
auto index = static_cast<size_t>(i % padding_length);
|
auto index = static_cast<size_t>(i % padding_length);
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ namespace input {
|
|||||||
|
|
||||||
void
|
void
|
||||||
InputEvent::insert() {
|
InputEvent::insert() {
|
||||||
torrent::this_thread::poll()->open(this);
|
torrent::this_thread::poll()->open_and_insert_read(this);
|
||||||
torrent::this_thread::poll()->insert_read(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
+16
-15
@@ -112,8 +112,9 @@ main(int argc, char** argv) {
|
|||||||
|
|
||||||
torrent::log_initialize();
|
torrent::log_initialize();
|
||||||
|
|
||||||
// TODO: Create a fake thread object for initializing other processes and enabling logging.
|
torrent::runtime::initialize_worker_process_and_main_thread([argc, argv]() {
|
||||||
torrent::initialize_main_thread();
|
parse_config_file(argc, argv, [](auto& path) { parse_config_file_comments("worker", path); });
|
||||||
|
});
|
||||||
|
|
||||||
// Block SIGCHLD until all threads are created, then unblock on main-thread, to avoid SIGCHLD
|
// Block SIGCHLD until all threads are created, then unblock on main-thread, to avoid SIGCHLD
|
||||||
// interrupting other threads.
|
// interrupting other threads.
|
||||||
@@ -125,9 +126,9 @@ main(int argc, char** argv) {
|
|||||||
SignalHandler::set_block(SIGCHLD);
|
SignalHandler::set_block(SIGCHLD);
|
||||||
|
|
||||||
// All signal handlers must restore errno if they return.
|
// All signal handlers must restore errno if they return.
|
||||||
SignalHandler::set_handler(SIGSEGV, std::bind(&do_panic, SIGSEGV));
|
SignalHandler::set_handler(SIGSEGV, []() { do_panic(SIGSEGV); });
|
||||||
SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL));
|
SignalHandler::set_handler(SIGILL, []() { do_panic(SIGILL); });
|
||||||
SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE));
|
SignalHandler::set_handler(SIGFPE, []() { do_panic(SIGFPE); });
|
||||||
|
|
||||||
// Limited list of commands with the following format:
|
// Limited list of commands with the following format:
|
||||||
//
|
//
|
||||||
@@ -136,19 +137,17 @@ main(int argc, char** argv) {
|
|||||||
// # do:log.open_file=system,/usr/rakshasa/system.log
|
// # do:log.open_file=system,/usr/rakshasa/system.log
|
||||||
// # do:log.add_output=system,system
|
// # do:log.add_output=system,system
|
||||||
//
|
//
|
||||||
parse_config_file(argc, argv, [](auto& path) {
|
// # do-worker:log.open_file=system,/usr/rakshasa/system-worker.log
|
||||||
if (path.empty())
|
// # do-worker:log.add_output=system,system
|
||||||
return;
|
//
|
||||||
|
parse_config_file(argc, argv, [](auto& path) { parse_config_file_comments("", path); });
|
||||||
parse_config_file_comments(path);
|
|
||||||
});
|
|
||||||
|
|
||||||
control = new Control;
|
control = new Control;
|
||||||
|
|
||||||
SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control));
|
SignalHandler::set_handler(SIGINT, []() { control->receive_normal_shutdown(); });
|
||||||
SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control));
|
SignalHandler::set_handler(SIGHUP, []() { control->receive_normal_shutdown(); });
|
||||||
SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control));
|
SignalHandler::set_handler(SIGTERM, []() { control->receive_quick_shutdown(); });
|
||||||
SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display()));
|
SignalHandler::set_handler(SIGWINCH, []() { control->display()->force_redraw(); });
|
||||||
|
|
||||||
SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus);
|
SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus);
|
||||||
|
|
||||||
@@ -463,6 +462,8 @@ main(int argc, char** argv) {
|
|||||||
control->ui()->load_input_history();
|
control->ui()->load_input_history();
|
||||||
|
|
||||||
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
|
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
|
||||||
|
|
||||||
|
torrent::runtime::initialize_worker_process();
|
||||||
torrent::runtime::initialize_network();
|
torrent::runtime::initialize_network();
|
||||||
|
|
||||||
// Load session torrents and perform scheduled tasks to ensure session torrents are loaded
|
// Load session torrents and perform scheduled tasks to ensure session torrents are loaded
|
||||||
|
|||||||
+5
-2
@@ -258,8 +258,11 @@ JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
|
|||||||
|
|
||||||
return callback(response_str.c_str(), response_str.size());
|
return callback(response_str.c_str(), response_str.size());
|
||||||
|
|
||||||
} catch (json::exception& e) {
|
} catch (json::parse_error& e) {
|
||||||
// Exception strings may contain invalid UTF-8, hence the ::replace
|
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
|
return callback(err_str.c_str(), err_str.size());
|
||||||
|
} catch (json::type_error& e) {
|
||||||
|
// Type errors may be caused by invalid UTF-8 strings in exception strings, hence the ::replace
|
||||||
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
return callback(err_str.c_str(), err_str.size());
|
return callback(err_str.c_str(), err_str.size());
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -109,8 +109,7 @@ void
|
|||||||
SCgi::activate() {
|
SCgi::activate() {
|
||||||
assert(torrent::this_thread::thread() == scgi_thread::thread());
|
assert(torrent::this_thread::thread() == scgi_thread::thread());
|
||||||
|
|
||||||
torrent::this_thread::poll()->open(this);
|
torrent::this_thread::poll()->open_and_insert_read(this);
|
||||||
torrent::this_thread::poll()->insert_read(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This should close the fd to avoid reuse.
|
// TODO: This should close the fd to avoid reuse.
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ SCgiTask::open(SCgi* parent, int fd) {
|
|||||||
// m_trusted=false into the next reuse, given that the
|
// m_trusted=false into the next reuse, given that the
|
||||||
// UNTRUSTED_CONNECTION=0 parse branch is a no-op.
|
// UNTRUSTED_CONNECTION=0 parse branch is a no-op.
|
||||||
|
|
||||||
torrent::this_thread::poll()->open(this);
|
torrent::this_thread::poll()->open_and_insert_read(this);
|
||||||
torrent::this_thread::poll()->insert_read(this);
|
|
||||||
|
|
||||||
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
||||||
|
|
||||||
@@ -94,12 +93,8 @@ SCgiTask::event_read() {
|
|||||||
if (m_content_length == 0)
|
if (m_content_length == 0)
|
||||||
read_length--;
|
read_length--;
|
||||||
|
|
||||||
if (read_length <= 0) {
|
if (read_length <= 0)
|
||||||
if (m_content_length == 0)
|
|
||||||
return close();
|
|
||||||
|
|
||||||
throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read.");
|
throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read.");
|
||||||
}
|
|
||||||
|
|
||||||
int bytes = ::recv(file_descriptor(), m_buffer.data() + m_position, read_length, 0);
|
int bytes = ::recv(file_descriptor(), m_buffer.data() + m_position, read_length, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -49,23 +49,13 @@ element_access(const tinyxml2::XMLElement* elem, std::initializer_list<std::stri
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char*
|
|
||||||
element_text_value(const tinyxml2::XMLNode* node) {
|
|
||||||
auto text = node->ToText();
|
|
||||||
|
|
||||||
if (text == nullptr)
|
|
||||||
throw rpc_error(XMLRPC_TYPE_ERROR, "expected a text value");
|
|
||||||
|
|
||||||
return text->Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
long long
|
long long
|
||||||
element_to_int(const tinyxml2::XMLNode* elem) {
|
element_to_int(const tinyxml2::XMLNode* elem) {
|
||||||
char* pos;
|
char* pos;
|
||||||
if (elem->FirstChild() == nullptr) {
|
if (elem->FirstChild() == nullptr) {
|
||||||
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer");
|
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer");
|
||||||
}
|
}
|
||||||
auto str = element_text_value(elem->FirstChild());
|
auto str = elem->FirstChild()->ToText()->Value();
|
||||||
auto result = std::strtoll(str, &pos, 10);
|
auto result = std::strtoll(str, &pos, 10);
|
||||||
if (pos == str || *pos != '\0')
|
if (pos == str || *pos != '\0')
|
||||||
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value");
|
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value");
|
||||||
@@ -95,7 +85,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
|||||||
if (child_element == nullptr)
|
if (child_element == nullptr)
|
||||||
return torrent::Object("");
|
return torrent::Object("");
|
||||||
|
|
||||||
return torrent::Object(element_text_value(child_element));
|
return torrent::Object(child_element->ToText()->Value());
|
||||||
|
|
||||||
} else if (std::strncmp(root_type, "int", sizeof("int")) == 0 ||
|
} else if (std::strncmp(root_type, "int", sizeof("int")) == 0 ||
|
||||||
std::strncmp(root_type, "i4", sizeof("i4")) == 0 ||
|
std::strncmp(root_type, "i4", sizeof("i4")) == 0 ||
|
||||||
@@ -108,7 +98,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
|||||||
if (child_element == nullptr)
|
if (child_element == nullptr)
|
||||||
throw rpc_error(XMLRPC_TYPE_ERROR, "empty boolean element");
|
throw rpc_error(XMLRPC_TYPE_ERROR, "empty boolean element");
|
||||||
|
|
||||||
auto boolean_text = std::string(element_text_value(child_element));
|
auto boolean_text = std::string(child_element->ToText()->Value());
|
||||||
|
|
||||||
if (boolean_text == "1")
|
if (boolean_text == "1")
|
||||||
return torrent::Object((int64_t)1);
|
return torrent::Object((int64_t)1);
|
||||||
@@ -139,12 +129,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
|||||||
if (name_element == nullptr)
|
if (name_element == nullptr)
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member missing name element");
|
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member missing name element");
|
||||||
|
|
||||||
auto name_text = name_element->GetText();
|
map[name_element->GetText()] = std::move(xml_value_to_object(child->FirstChildElement("value")));
|
||||||
|
|
||||||
if (name_text == nullptr)
|
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member has an empty name element");
|
|
||||||
|
|
||||||
map[name_text] = std::move(xml_value_to_object(child->FirstChildElement("value")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return map_raw;
|
return map_raw;
|
||||||
@@ -155,7 +140,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
|||||||
if (child_element == nullptr)
|
if (child_element == nullptr)
|
||||||
return torrent::Object("");
|
return torrent::Object("");
|
||||||
|
|
||||||
return torrent::Object(utils::decode_base64(utils::remove_newlines(element_text_value(child_element))));
|
return torrent::Object(utils::decode_base64(utils::remove_newlines(child_element->ToText()->Value())));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
|
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
|
||||||
@@ -315,9 +300,6 @@ process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer
|
|||||||
if (doc->FirstChildElement("methodCall")->FirstChildElement("methodName") == nullptr)
|
if (doc->FirstChildElement("methodCall")->FirstChildElement("methodName") == nullptr)
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element not found");
|
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element not found");
|
||||||
auto method_name = doc->FirstChildElement("methodCall")->FirstChildElement("methodName")->GetText();
|
auto method_name = doc->FirstChildElement("methodCall")->FirstChildElement("methodName")->GetText();
|
||||||
|
|
||||||
if (method_name == nullptr)
|
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element is empty");
|
|
||||||
torrent::Object result;
|
torrent::Object result;
|
||||||
|
|
||||||
// Add a shim here for system.multicall to allow better code reuse, and
|
// Add a shim here for system.multicall to allow better code reuse, and
|
||||||
@@ -328,9 +310,6 @@ process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer
|
|||||||
auto parent_elements = element_access(doc->RootElement(), {"params", "param", "value", "array", "data"});
|
auto parent_elements = element_access(doc->RootElement(), {"params", "param", "value", "array", "data"});
|
||||||
for (auto child = parent_elements->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
|
for (auto child = parent_elements->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
|
||||||
auto sub_method_name = element_access(child, {"struct", "member", "value", "string"})->GetText();
|
auto sub_method_name = element_access(child, {"struct", "member", "value", "string"})->GetText();
|
||||||
|
|
||||||
if (sub_method_name == nullptr)
|
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "multicall methodName element is empty");
|
|
||||||
// If sub_params ends up a nullptr at the end of this if-chian,
|
// If sub_params ends up a nullptr at the end of this if-chian,
|
||||||
// execute_command will turn it into an empty list
|
// execute_command will turn it into an empty list
|
||||||
auto sub_params = element_access(child, {"struct", "member"});
|
auto sub_params = element_access(child, {"struct", "member"});
|
||||||
|
|||||||
+9
-5
@@ -108,18 +108,22 @@ config_comment_log(const std::string& command, const std::string& raw_args) {
|
|||||||
throw torrent::input_error("Unknown log command: " + command);
|
throw torrent::input_error("Unknown log command: " + command);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call special commands in the format "# do:command=args" in the config file.
|
// Call special commands in the format "# do:command=args" or "# do-<category>:command=args" in the config file.
|
||||||
void
|
void
|
||||||
parse_config_file_comments(const std::string& path) {
|
parse_config_file_comments(const std::string& category, const std::string& path) {
|
||||||
|
if (path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
std::fstream file(path, std::ios::in);
|
std::fstream file(path, std::ios::in);
|
||||||
|
|
||||||
if (!file.is_open())
|
if (!file.is_open())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
|
std::string prefix = category.empty() ? "# do:" : "# do-" + category + ":";
|
||||||
|
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(file, line)) {
|
||||||
if (line.size() <= 5 || line.compare(0, 5, "# do:") != 0)
|
if (line.size() <= prefix.size() || line.compare(0, prefix.size(), prefix) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto equal_pos = line.find('=');
|
auto equal_pos = line.find('=');
|
||||||
@@ -127,8 +131,8 @@ parse_config_file_comments(const std::string& path) {
|
|||||||
if (equal_pos == std::string::npos)
|
if (equal_pos == std::string::npos)
|
||||||
throw torrent::input_error("Invalid command in config file comment: " + line);
|
throw torrent::input_error("Invalid command in config file comment: " + line);
|
||||||
|
|
||||||
std::string command = line.substr(5, equal_pos - 5);
|
auto command = line.substr(prefix.size(), equal_pos - prefix.size());
|
||||||
std::string args = line.substr(equal_pos + 1);
|
auto args = line.substr(equal_pos + 1);
|
||||||
|
|
||||||
if (command.empty())
|
if (command.empty())
|
||||||
throw torrent::input_error("Invalid command in config file comment: " + line);
|
throw torrent::input_error("Invalid command in config file comment: " + line);
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
int parse_main_options(int argc, char** argv);
|
int parse_main_options(int argc, char** argv);
|
||||||
void parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn);
|
void parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn);
|
||||||
void parse_config_file_comments(const std::string& path);
|
void parse_config_file_comments(const std::string& category, const std::string& path);
|
||||||
|
|
||||||
void load_session_torrents(const std::string& path);
|
void load_session_torrents(const std::string& path);
|
||||||
void load_arg_torrents(char** first, char** last);
|
void load_arg_torrents(char** first, char** last);
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ DownloadList::unfocus_download(core::Download* d) {
|
|||||||
if (m_state == DISPLAY_DOWNLOAD && d == static_cast<Download*>(m_uiArray[DISPLAY_DOWNLOAD])->download())
|
if (m_state == DISPLAY_DOWNLOAD && d == static_cast<Download*>(m_uiArray[DISPLAY_DOWNLOAD])->download())
|
||||||
activate_display(DISPLAY_DOWNLOAD_LIST);
|
activate_display(DISPLAY_DOWNLOAD_LIST);
|
||||||
|
|
||||||
if (current_view()->focus() < current_view()->end_visible() && *current_view()->focus() == d)
|
if (*current_view()->focus() == d && current_view()->focus() < current_view()->end_visible())
|
||||||
current_view()->next_focus();
|
current_view()->next_focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user