mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-09 15:49:20 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5aa45bbaf4 | |||
| 728790a2b4 | |||
| e652a3cbab | |||
| 15ccae27de | |||
| 4c0535b1fa | |||
| d59765fda4 | |||
| 66c49d24e5 |
+3
-3
@@ -1,6 +1,6 @@
|
||||
m4_pattern_allow([PKG_CHECK_EXISTS])
|
||||
|
||||
AC_INIT([rtorrent],[0.16.24],[sundell.software@gmail.com])
|
||||
AC_INIT([rtorrent],[0.16.25],[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], [28], [api version])
|
||||
AC_DEFINE([API_VERSION], [29], [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.24])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.25])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
TORRENT_WITH_XMLRPC_C
|
||||
|
||||
+31
-17
@@ -107,7 +107,7 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
|
||||
switch (changeType) {
|
||||
case 0:
|
||||
if (symlink(target.c_str(), link.c_str()) == -1)
|
||||
lt_log_print(torrent::LOG_TORRENT_WARN, "create_link failed: %s", std::strerror(errno));
|
||||
lt_log_print(torrent::LOG_TORRENT_DEBUG, "create_link failed: %s", std::strerror(errno));
|
||||
|
||||
break;
|
||||
|
||||
@@ -117,7 +117,7 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
|
||||
errno = 0;
|
||||
|
||||
if (!fileStat.update_link(link) || !fileStat.is_link() || unlink(link.c_str()) == -1)
|
||||
lt_log_print(torrent::LOG_TORRENT_WARN, "delete_link failed: %s", std::strerror(errno));
|
||||
lt_log_print(torrent::LOG_TORRENT_DEBUG, "delete_link failed: %s", std::strerror(errno));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ apply_d_delete_tied(core::Download* download) {
|
||||
if (tie.empty())
|
||||
return torrent::Object();
|
||||
|
||||
if (::unlink(expand_path(tie).c_str()) == -1)
|
||||
if (unlink(expand_path(tie).c_str()) == -1)
|
||||
control->core()->push_log_std("Could not unlink tied file: " + std::string(std::strerror(errno)));
|
||||
|
||||
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(download));
|
||||
@@ -293,7 +293,7 @@ apply_d_add_peer(core::Download* download, const std::string& arg) {
|
||||
// Currently discarding SOCK_STREAM.
|
||||
torrent::this_thread::resolver()->resolve_preferred(callback_id, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) {
|
||||
if (sa == nullptr) {
|
||||
lt_log_print(torrent::LOG_TORRENT_WARN, "could not resolve hostname for added peer: %s", gai_strerror(err));
|
||||
lt_log_print(torrent::LOG_TORRENT_DEBUG, "could not resolve hostname for added peer: %s", gai_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -334,6 +334,11 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
|
||||
bool use_regex = true;
|
||||
|
||||
rpc::preparsed_commands commands([&args](auto& cmds) {
|
||||
for (auto cItr = ++args.begin(); cItr != args.end(); ++cItr)
|
||||
cmds.push_back(rpc::parse_command_object(cItr->as_string()));
|
||||
});
|
||||
|
||||
if (args.front().is_list())
|
||||
for (const auto& o : args.front().as_list())
|
||||
regex_list.push_back(o.as_string_c());
|
||||
@@ -349,10 +354,10 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
|
||||
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++) {
|
||||
const std::string& cmd = cItr->as_string();
|
||||
row.push_back(rpc::parse_command(rpc::make_target(file.get()), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||
}
|
||||
// Defer parsing until a file actually matches the multicall selection.
|
||||
commands.prepare_if_needed();
|
||||
for (auto& itr : commands)
|
||||
row.push_back(rpc::call_object(itr, rpc::make_target(file.get())));
|
||||
}
|
||||
|
||||
return resultRaw;
|
||||
@@ -372,6 +377,11 @@ t_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
auto result_raw = torrent::Object::create_list();
|
||||
auto& result = result_raw.as_list();
|
||||
|
||||
rpc::preparsed_commands commands([&args](auto& cmds) {
|
||||
for (auto cItr = ++args.begin(); cItr != args.end(); ++cItr)
|
||||
cmds.push_back(rpc::parse_command_object(cItr->as_string()));
|
||||
});
|
||||
|
||||
for (uint32_t idx = 0, last = download->tracker_list_size(); idx < last; idx++) {
|
||||
auto& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
auto tracker = download->tracker_controller().at(idx);
|
||||
@@ -379,11 +389,10 @@ t_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
if (!tracker.is_valid())
|
||||
continue;
|
||||
|
||||
for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||
auto& cmd = cItr->as_string();
|
||||
|
||||
row.push_back(rpc::parse_command(rpc::make_target(&tracker), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||
}
|
||||
// Do not parse columns when there are no valid tracker targets.
|
||||
commands.prepare_if_needed();
|
||||
for (auto& itr : commands)
|
||||
row.push_back(rpc::call_object(itr, rpc::make_target(&tracker)));
|
||||
}
|
||||
|
||||
return result_raw;
|
||||
@@ -405,13 +414,18 @@ p_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
auto* connection_list = download->connection_list();
|
||||
const auto change_counter = connection_list->change_counter();
|
||||
|
||||
rpc::preparsed_commands commands([&args](auto& cmds) {
|
||||
for (auto cItr = ++args.begin(); cItr != args.end(); ++cItr)
|
||||
cmds.push_back(rpc::parse_command_object(cItr->as_string()));
|
||||
});
|
||||
|
||||
for (const auto& connection : *connection_list) {
|
||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
|
||||
for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||
const std::string& cmd = cItr->as_string();
|
||||
|
||||
row.push_back(rpc::parse_command(rpc::make_target(connection), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||
// Prepare only after a peer exists, preserving empty-list laziness.
|
||||
commands.prepare_if_needed();
|
||||
for (auto& itr : commands) {
|
||||
row.push_back(rpc::call_object(itr, rpc::make_target(connection)));
|
||||
|
||||
// Erasing a peer frees it and swaps the last element into its place, so
|
||||
// neither this peer nor the iteration survives a change to the list.
|
||||
|
||||
+13
-7
@@ -169,7 +169,7 @@ void apply_try_import(const std::string& path) { if (!rpc::parse_command_file(pa
|
||||
|
||||
torrent::Object
|
||||
apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
|
||||
bool closed = false;
|
||||
int closed{};
|
||||
|
||||
torrent::FileList::cache_list cache;
|
||||
|
||||
@@ -186,11 +186,11 @@ apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
|
||||
download->set_hash_failed(true);
|
||||
download->set_message(std::string("Low diskspace."));
|
||||
|
||||
closed = true;
|
||||
closed++;
|
||||
}
|
||||
|
||||
if (closed)
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Closed torrents due to low diskspace.");
|
||||
if (closed != 0)
|
||||
lt_log_print(torrent::LOG_TORRENT_EVENTS, "closed torrents due to low diskspace : count:%i", closed);
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
@@ -247,20 +247,26 @@ d_multicall(const torrent::Object::list_type& args) {
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
|
||||
rpc::preparsed_commands commands([&args](auto& cmds) {
|
||||
for (auto cItr = ++args.begin(); cItr != args.end(); ++cItr)
|
||||
cmds.push_back(rpc::parse_command_object(cItr->as_string()));
|
||||
});
|
||||
|
||||
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++) {
|
||||
// Skip parsing if there are no usable download targets in the view.
|
||||
commands.prepare_if_needed();
|
||||
for (auto& itr : commands) {
|
||||
// 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);
|
||||
row.push_back(rpc::call_object(itr, rpc::make_target(download)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
// For cg_d_group.
|
||||
#include "core/download.h"
|
||||
|
||||
#define LT_LOG_SUBSYSTEM(log_fmt, ...) \
|
||||
lt_log_print_subsystem(torrent::LOG_TORRENT_INFO, "choke_queue", log_fmt, __VA_ARGS__);
|
||||
|
||||
// A hack to allow testing of the new choke_group API without the
|
||||
// working parts present.
|
||||
#define USE_CHOKE_GROUP 0
|
||||
@@ -207,7 +204,7 @@ apply_cg_index_of(const std::string& arg) {
|
||||
|
||||
torrent::Object
|
||||
apply_cg_all_update_balance(bool is_up) {
|
||||
LT_LOG_SUBSYSTEM("apply update balance: hack is_up:%i", (int)is_up);
|
||||
lt_log_print(torrent::LOG_TORRENT_DEBUG, "choke_queue : applying update balance : is_up:%i", (int)is_up);
|
||||
|
||||
for (const auto& itr : cg_list_hack) {
|
||||
if (is_up)
|
||||
|
||||
@@ -88,6 +88,8 @@ initialize_command_system() {
|
||||
|
||||
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_name.allow_legacy_utf8", [](auto, auto) { return torrent::runtime::client_config()->file_name_allow_legacy_utf8(); });
|
||||
CMD_ANY_VALUE_V ("system.file_name.allow_legacy_utf8.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_file_name_allow_legacy_utf8(value); });
|
||||
|
||||
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(); });
|
||||
@@ -167,10 +169,13 @@ initialize_command_system() {
|
||||
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.torrent_name.use_sanitized");
|
||||
rpc::rpc.mark_safe("system.file.allocate");
|
||||
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.file_name.replace_slash");
|
||||
rpc::rpc.mark_safe("system.file_name.allow_legacy_utf8");
|
||||
|
||||
rpc::rpc.mark_safe("system.sockets.size");
|
||||
rpc::rpc.mark_safe("system.sockets.max_size");
|
||||
|
||||
+36
-26
@@ -29,6 +29,11 @@
|
||||
#include "session/session_manager.h"
|
||||
#include "ui/root.h"
|
||||
|
||||
#define LT_LOG_DOWNLOAD_DEBUG(log_fmt, ...) \
|
||||
lt_log_print_hash_only(torrent::LOG_TORRENT_DEBUG, download->info()->hash(), "download_list : " log_fmt, __VA_ARGS__);
|
||||
#define LT_LOG_DOWNLOAD_EVENTS(log_fmt, ...) \
|
||||
lt_log_print_info(torrent::LOG_TORRENT_EVENTS, download->info(), "download_list", log_fmt, __VA_ARGS__);
|
||||
|
||||
#define DL_TRIGGER_EVENT(download, event_name) \
|
||||
rpc::commands.call_catch(event_name, rpc::make_target(download), torrent::Object(), "Event '" event_name "' failed: ");
|
||||
|
||||
@@ -109,7 +114,7 @@ DownloadList::create(torrent::Object* obj, uint32_t tracker_key, bool printLog)
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
if (printLog)
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download: %s", e.what());
|
||||
lt_log_print(torrent::LOG_TORRENT_EVENTS, "download_list : could not create download : %s", e.what());
|
||||
|
||||
delete obj;
|
||||
|
||||
@@ -135,7 +140,7 @@ DownloadList::create(std::istream* str, uint32_t tracker_key, bool printLog) {
|
||||
delete object;
|
||||
|
||||
if (printLog)
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
|
||||
lt_log_print(torrent::LOG_TORRENT_EVENTS, "download_list : could not create download : invalid or incomplete bencode data");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -146,7 +151,7 @@ DownloadList::create(std::istream* str, uint32_t tracker_key, bool printLog) {
|
||||
delete object;
|
||||
|
||||
if (printLog)
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download: %s", e.what());
|
||||
lt_log_print(torrent::LOG_TORRENT_EVENTS, "download_list : could not create download : %s", e.what());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -160,7 +165,7 @@ DownloadList::iterator
|
||||
DownloadList::insert(Download* 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.");
|
||||
LT_LOG_DOWNLOAD_DEBUG("inserting download", 0);
|
||||
|
||||
try {
|
||||
(*itr)->data()->slot_initial_hash() = std::bind(&DownloadList::hash_done, this, download);
|
||||
@@ -200,7 +205,7 @@ DownloadList::erase(iterator itr) {
|
||||
|
||||
(*itr)->set_erasing();
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, (*itr)->info(), "download_list", "Erasing download.");
|
||||
lt_log_print_hash_only(torrent::LOG_TORRENT_DEBUG, (*itr)->info()->hash(), "download_list : removing download");
|
||||
|
||||
// Makes sure close doesn't restart hashing of this download.
|
||||
(*itr)->set_hash_failed(true);
|
||||
@@ -228,7 +233,7 @@ DownloadList::open(Download* download) {
|
||||
return true;
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not open download: %s", e.what());
|
||||
lt_log_print(torrent::LOG_TORRENT_EVENTS, "download_list : could not open download : %s", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -237,7 +242,7 @@ void
|
||||
DownloadList::open_throw(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Opening download.");
|
||||
lt_log_print_hash_only(torrent::LOG_TORRENT_DEBUG, download->info()->hash(), "download_list : opening download");
|
||||
|
||||
if (download->download()->info()->is_open())
|
||||
return;
|
||||
@@ -248,6 +253,7 @@ DownloadList::open_throw(Download* download) {
|
||||
openFlags |= torrent::Download::open_enable_fallocate;
|
||||
|
||||
download->download()->open(openFlags);
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.opened");
|
||||
}
|
||||
|
||||
@@ -257,7 +263,7 @@ DownloadList::close(Download* download) {
|
||||
close_throw(download);
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not close download: %s", e.what());
|
||||
lt_log_print(torrent::LOG_TORRENT_EVENTS, "download_list : could not close download : %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +271,7 @@ DownloadList::close(Download* download) {
|
||||
// need the files closed and will keep using the download.
|
||||
void
|
||||
DownloadList::close_files(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download files.");
|
||||
lt_log_print_hash_only(torrent::LOG_TORRENT_DEBUG, download->info()->hash(), "download_list : closing download files");
|
||||
|
||||
if (download->download()->info()->is_active()) {
|
||||
download->download()->stop(torrent::Download::stop_skip_tracker);
|
||||
@@ -280,7 +286,7 @@ DownloadList::close_files(Download* download) {
|
||||
|
||||
void
|
||||
DownloadList::close_directly(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
|
||||
lt_log_print_hash_only(torrent::LOG_TORRENT_DEBUG, download->info()->hash(), "download_list : closing download directly");
|
||||
|
||||
auto lifetime = download->lifetime();
|
||||
bool was_active = download->download()->info()->is_active();
|
||||
@@ -327,7 +333,8 @@ DownloadList::update_paused_state(Download* download) {
|
||||
|
||||
void
|
||||
DownloadList::close_quick(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download quickly.");
|
||||
lt_log_print_hash_only(torrent::LOG_TORRENT_DEBUG, download->info()->hash(), "download_list : closing download quickly.");
|
||||
|
||||
close(download);
|
||||
|
||||
// Make sure we cancel any tracker requests. This should rather be
|
||||
@@ -341,7 +348,7 @@ void
|
||||
DownloadList::close_throw(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download with throw.");
|
||||
LT_LOG_DOWNLOAD_DEBUG("closing download with throw", 0);
|
||||
|
||||
// When pause gets called it will clear the initial hash check state
|
||||
// and set hash failed. This should ensure hashing doesn't restart
|
||||
@@ -383,7 +390,7 @@ void
|
||||
DownloadList::resume(Download* download, int flags) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Resuming download: flags:%0x.", flags);
|
||||
LT_LOG_DOWNLOAD_DEBUG("resuming download : flags:%0x.", flags);
|
||||
|
||||
try {
|
||||
|
||||
@@ -472,7 +479,7 @@ DownloadList::resume(Download* download, int flags) {
|
||||
DL_TRIGGER_EVENT(download, "event.download.resumed");
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not resume download: %s", e.what());
|
||||
LT_LOG_DOWNLOAD_EVENTS("could not resume download : %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,7 +487,7 @@ void
|
||||
DownloadList::pause(Download* download, int flags) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Pausing download: flags:%0x.", flags);
|
||||
LT_LOG_DOWNLOAD_DEBUG("pausing download : flags:%0x.", flags);
|
||||
|
||||
auto lifetime = download->lifetime();
|
||||
|
||||
@@ -523,7 +530,7 @@ DownloadList::pause(Download* download, int flags) {
|
||||
//control->core()->download_store()->save(download);
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not pause download: %s", e.what());
|
||||
LT_LOG_DOWNLOAD_EVENTS("could not pause download : %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,7 +538,7 @@ void
|
||||
DownloadList::check_hash(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Checking hash.");
|
||||
LT_LOG_DOWNLOAD_DEBUG("checking hash", 0);
|
||||
|
||||
try {
|
||||
if (rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped)
|
||||
@@ -540,7 +547,7 @@ DownloadList::check_hash(Download* download) {
|
||||
hash_queue(download, Download::variable_hashing_rehash);
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not check hash: %s", e.what());
|
||||
LT_LOG_DOWNLOAD_EVENTS("could not check hash : %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,7 +555,7 @@ void
|
||||
DownloadList::hash_done(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Hash done.");
|
||||
LT_LOG_DOWNLOAD_DEBUG("hash done", 0);
|
||||
|
||||
if (download->is_hash_checking() || download->is_active())
|
||||
throw torrent::internal_error("DownloadList::hash_done(...) download in invalid state.");
|
||||
@@ -613,7 +620,8 @@ DownloadList::hash_done(Download* download) {
|
||||
confirm_finished(download);
|
||||
} else {
|
||||
download->set_message("Hash check on download completion found bad chunks.");
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Hash check on download completion found bad chunks.");
|
||||
LT_LOG_DOWNLOAD_EVENTS("hash check on download completion found bad chunks", 0);
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.hash_final_failed");
|
||||
}
|
||||
|
||||
@@ -634,7 +642,7 @@ void
|
||||
DownloadList::hash_queue(Download* download, int type) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Hash queue.");
|
||||
LT_LOG_DOWNLOAD_DEBUG("hash queue : type:%d", type);
|
||||
|
||||
if (rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped)
|
||||
throw torrent::internal_error("DownloadList::hash_queue(...) hashing already queued.");
|
||||
@@ -678,7 +686,7 @@ void
|
||||
DownloadList::received_finished(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Received finished.");
|
||||
LT_LOG_DOWNLOAD_DEBUG("received finished", 0);
|
||||
|
||||
if (rpc::call_command_value("pieces.hash.on_completion"))
|
||||
// Set some 'checking_finished_thingie' variable to make hash_done
|
||||
@@ -693,7 +701,7 @@ void
|
||||
DownloadList::confirm_finished(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Confirming finished.");
|
||||
LT_LOG_DOWNLOAD_DEBUG("confirming finished", 0);
|
||||
|
||||
if (download->download()->info()->is_meta_download())
|
||||
return process_meta_download(download);
|
||||
@@ -768,7 +776,7 @@ DownloadList::confirm_finished(Download* download) {
|
||||
|
||||
void
|
||||
DownloadList::process_meta_download(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Processing meta download.");
|
||||
LT_LOG_DOWNLOAD_DEBUG("processing meta download", 0);
|
||||
|
||||
rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download));
|
||||
rpc::call_command("d.close", torrent::Object(), rpc::make_target(download));
|
||||
@@ -778,7 +786,7 @@ DownloadList::process_meta_download(Download* download) {
|
||||
std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary);
|
||||
|
||||
if (!file.is_open()) {
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata.");
|
||||
LT_LOG_DOWNLOAD_EVENTS("could not read download metadata : %s", metafile.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -786,7 +794,7 @@ DownloadList::process_meta_download(Download* download) {
|
||||
file >> bencode->insert_key("info", torrent::Object());
|
||||
|
||||
if (file.fail()) {
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
|
||||
LT_LOG_DOWNLOAD_EVENTS("could not create download, the input is not a valid torrent : %s", metafile.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -794,8 +802,10 @@ DownloadList::process_meta_download(Download* download) {
|
||||
|
||||
// Steal the keys we still need. The old download has no use for them.
|
||||
bencode->insert_key("rtorrent_meta_download", torrent::Object()).swap(download->bencode()->get_key("rtorrent_meta_download"));
|
||||
|
||||
if (download->bencode()->has_key("announce"))
|
||||
bencode->insert_key("announce", torrent::Object()).swap(download->bencode()->get_key("announce"));
|
||||
|
||||
if (download->bencode()->has_key("announce-list"))
|
||||
bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list"));
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef RTORRENT_CORE_DOWNLOAD_LIST_H
|
||||
#define RTORRENT_CORE_DOWNLOAD_LIST_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
+2
-1
@@ -440,7 +440,8 @@ Manager::receive_hashing_changed() {
|
||||
} else {
|
||||
(*itr)->set_hash_failed(true);
|
||||
(*itr)->set_message("Hashing failed: " + std::string(e.what()));
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Hashing failed: %s", e.what());
|
||||
|
||||
lt_log_print_hash_only(torrent::LOG_TORRENT_EVENTS, (*itr)->info()->hash(), "hashing failed : %s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ public:
|
||||
|
||||
static stack_type* from_data(char* data) { return reinterpret_cast<stack_type*>(data); }
|
||||
|
||||
char buffer[sizeof(torrent::Object) * max_arguments];
|
||||
alignas(optimal_alignment) char buffer[sizeof(torrent::Object) * max_arguments];
|
||||
};
|
||||
|
||||
command_base() : m_copy_helper(nullptr), m_dest_helper(nullptr) {}
|
||||
|
||||
@@ -123,6 +123,39 @@ parse_command(target_type target, const char* first, const char* last) {
|
||||
return std::make_pair(commands.call_command(key, args, target), first);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
parse_command_object(const char* first, const char* last) {
|
||||
first = std::find_if(first, last, [&](char c) { return !command_map_is_space(c); });
|
||||
|
||||
if (first == last || *first == '#')
|
||||
return torrent::Object();
|
||||
|
||||
char key[128];
|
||||
|
||||
first = parse_command_name(first, last, key, key + 128);
|
||||
first = std::find_if(first, last, [&](char c) { return !command_map_is_space(c); });
|
||||
|
||||
if (first == last || *first != '=')
|
||||
throw torrent::input_error("Could not find '=' in command '" + std::string(key) + "'.");
|
||||
|
||||
torrent::Object result = torrent::Object::create_dict_key();
|
||||
|
||||
result.as_dict_key() = key;
|
||||
|
||||
first = parse_whole_list(first + 1, last, &result.as_dict_obj(), &parse_is_delim_command);
|
||||
|
||||
// Find the last character that is part of this command, skipping
|
||||
// the whitespace at the end.
|
||||
first = std::find_if(first, last, [&](char c) { return !command_map_is_space(c); });
|
||||
|
||||
// This helper accepts exactly one command and cannot return where a next
|
||||
// command begins, so reject every non-whitespace suffix, including ';'.
|
||||
if (first != last && *first != '\0')
|
||||
throw torrent::input_error("Junk at end of input.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
parse_command_multiple(target_type target, const char* first, const char* last) {
|
||||
parse_command_type result;
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "xmlrpc.h"
|
||||
#include "rpc_manager.h"
|
||||
@@ -67,6 +70,34 @@ parse_command_single(target_type target, const std::string& cmd) {
|
||||
return parse_command(target, cmd.c_str(), cmd.c_str() + cmd.size()).first;
|
||||
}
|
||||
|
||||
// Parse one RPC command without executing it. Repeated evaluations can use
|
||||
// call_object on the result, which handles per-target argument expansion.
|
||||
torrent::Object parse_command_object(const char* first, const char* last);
|
||||
|
||||
inline torrent::Object parse_command_object(const std::string& cmd) {
|
||||
return parse_command_object(cmd.c_str(), cmd.c_str() + cmd.size());
|
||||
}
|
||||
|
||||
// Prepare a multicall's commands once, on the first target that uses them. This
|
||||
// keeps empty target lists from parsing commands that would never be evaluated.
|
||||
struct preparsed_commands : public std::vector<torrent::Object> {
|
||||
explicit preparsed_commands(std::function<void(preparsed_commands&)> prepare)
|
||||
: m_prepare(std::move(prepare)) {}
|
||||
|
||||
void prepare_if_needed() {
|
||||
if (m_prepare) {
|
||||
// Clear before invoking: the callback may inspect this vector, and a
|
||||
// throwing callback must not be run again against partially added items.
|
||||
auto prepare = std::move(m_prepare);
|
||||
m_prepare = {};
|
||||
prepare(*this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(preparsed_commands&)> m_prepare;
|
||||
};
|
||||
|
||||
inline torrent::Object
|
||||
parse_command_multiple_std(const std::string& cmd, target_type target = rpc::make_target()) {
|
||||
return parse_command_multiple(target, cmd.c_str(), cmd.c_str() + cmd.size());
|
||||
|
||||
@@ -197,8 +197,9 @@ ElementDownloadList::receive_cycle_throttle() {
|
||||
return;
|
||||
|
||||
core::Download* download = m_view->focus()->get();
|
||||
|
||||
if (download->is_active()) {
|
||||
lt_log_print(torrent::LOG_TORRENT_WARN, "Cannot change throttle on active download.");
|
||||
lt_log_print_hash_only(torrent::LOG_TORRENT_DEBUG, download->info()->hash(), "cannot change throttle on active download");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
#include "test/rpc/test_command.h"
|
||||
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "rpc/command.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommand);
|
||||
|
||||
@@ -83,3 +86,50 @@ TestCommand::test_stack_double() {
|
||||
rpc::command_base::pop_stack(&stack_first, last_stack_first);
|
||||
CPPUNIT_ASSERT(command_stack_all_empty());
|
||||
}
|
||||
|
||||
void
|
||||
TestCommand::test_preparsed_commands() {
|
||||
unsigned int prepare_count = 0;
|
||||
rpc::preparsed_commands commands([&prepare_count](auto& prepared) {
|
||||
++prepare_count;
|
||||
// Reentrant access must not invoke the same callback recursively.
|
||||
prepared.prepare_if_needed();
|
||||
prepared.push_back(rpc::parse_command_object("string.length=abc"));
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(0u, prepare_count);
|
||||
CPPUNIT_ASSERT(commands.empty());
|
||||
|
||||
commands.prepare_if_needed();
|
||||
CPPUNIT_ASSERT_EQUAL(1u, prepare_count);
|
||||
|
||||
size_t count = 0;
|
||||
for (auto& itr : commands) {
|
||||
CPPUNIT_ASSERT(itr.is_dict_key());
|
||||
++count;
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(size_t(1), count);
|
||||
CPPUNIT_ASSERT_EQUAL(1u, prepare_count);
|
||||
|
||||
for (auto& itr : commands)
|
||||
CPPUNIT_ASSERT(itr.is_dict_key());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(1u, prepare_count);
|
||||
}
|
||||
|
||||
void
|
||||
TestCommand::test_parse_command_object() {
|
||||
auto command = rpc::parse_command_object("\tstring.length=abc ");
|
||||
CPPUNIT_ASSERT(command.is_dict_key());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("string.length"), command.as_dict_key());
|
||||
|
||||
// This helper has no way to return the next-command pointer, so it must not
|
||||
// silently accept a multipart command separated by ';'.
|
||||
CPPUNIT_ASSERT_THROW(rpc::parse_command_object("string.length=abc;string.length=def"), torrent::input_error);
|
||||
|
||||
// Unlike parse_command (which parses command files), this helper handles one
|
||||
// multicall command and must reject newline boundaries, including CRLF.
|
||||
CPPUNIT_ASSERT_THROW(rpc::parse_command_object("string.length=abc\nstring.length=def"), torrent::input_error);
|
||||
CPPUNIT_ASSERT_THROW(rpc::parse_command_object("string.length=abc\r\nstring.length=def"), torrent::input_error);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,14 @@ class TestCommand : public test_fixture {
|
||||
|
||||
CPPUNIT_TEST(test_stack);
|
||||
CPPUNIT_TEST(test_stack_double);
|
||||
CPPUNIT_TEST(test_preparsed_commands);
|
||||
CPPUNIT_TEST(test_parse_command_object);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
void test_stack();
|
||||
void test_stack_double();
|
||||
void test_preparsed_commands();
|
||||
void test_parse_command_object();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user