diff --git a/scripts/checks.m4 b/scripts/checks.m4 index 38a490e3..ccc08e47 100644 --- a/scripts/checks.m4 +++ b/scripts/checks.m4 @@ -281,18 +281,34 @@ AC_DEFUN([TORRENT_WITH_LUA], [ AC_MSG_RESULT(no) else AX_PROG_LUA - AX_LUA_LIBS - AX_LUA_HEADERS - AC_DEFINE(HAVE_LUA, 1, Use LUA.) - AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory]) - LIBS="$LIBS $LUA_LIB" - CXXFLAGS="$CXXFLAGS $LUA_INCLUDE" + + # 1. Override AX_LUA_LIBS default crash behavior + AX_LUA_LIBS([have_lua_libs=yes], [have_lua_libs=no]) + + # 2. Override AX_LUA_HEADERS default crash behavior + AX_LUA_HEADERS([have_lua_headers=yes], [have_lua_headers=no]) + + # 3. Only inject if both checks completely pass + if test "x$have_lua_libs" = "xyes" && test "x$have_lua_headers" = "xyes"; then + AC_DEFINE(HAVE_LUA, 1, Use LUA.) + AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory]) + LIBS="$LIBS $LUA_LIB" + CXXFLAGS="$CXXFLAGS $LUA_INCLUDE" + else + # Throw fatal error ONLY if user strictly ran --with-lua=yes + if test "$withval" = "yes"; then + AC_MSG_ERROR([Lua support explicitly requested, but compatible Lua 5.3 libs/headers were not found.]) + else + AC_MSG_WARN([Lua 5.3 libs or headers missing. Proceeding without Lua support.]) + fi + fi fi ],[ AC_MSG_RESULT(ignored) ]) ]) + AC_DEFUN([TORRENT_WITH_INOTIFY], [ AC_LANG_PUSH(C++) diff --git a/src/command_download.cc b/src/command_download.cc index a8bc128f..a90a6873 100644 --- a/src/command_download.cc +++ b/src/command_download.cc @@ -33,31 +33,34 @@ #include "control.h" #include "command_helpers.h" -std::string +torrent::string_utf8 retrieve_d_base_path(core::Download* download) { if (download->file_list()->is_multi_file()) return download->file_list()->frozen_root_dir(); - else - return download->file_list()->empty() ? std::string() : download->file_list()->at(0)->frozen_path(); + + if (download->file_list()->empty()) + return {}; + + return download->file_list()->at(0)->frozen_path(); } -std::string +torrent::string_utf8 retrieve_d_base_filename(core::Download* download) { - const std::string* base; + torrent::string_utf8 base_path; if (download->file_list()->is_multi_file()) - base = &download->file_list()->frozen_root_dir(); - else if (download->file_list()->empty()) - return std::string(); + base_path = download->file_list()->frozen_root_dir(); + else if (!download->file_list()->empty()) + base_path = download->file_list()->at(0)->frozen_path(); else - base = &download->file_list()->at(0)->frozen_path(); + return {}; - std::string::size_type split = base->rfind('/'); + auto split = base_path.str().rfind('/'); if (split == std::string::npos) - return *base; - else - return base->substr(split + 1); + return base_path; + + return torrent::string_utf8::from_string(base_path.str().substr(split + 1)); } torrent::Object @@ -145,9 +148,9 @@ apply_d_directory(core::Download* download, const std::string& name) { if (!download->file_list()->is_multi_file()) download->set_root_directory(name); else if (name.empty() || *name.rbegin() == '/') - download->set_root_directory(name + download->info()->name()); + download->set_root_directory(name + download->info()->name().str()); else - download->set_root_directory(name + "/" + download->info()->name()); + download->set_root_directory(name + "/" + download->info()->name().str()); } torrent::Object @@ -272,16 +275,6 @@ retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::O return result; } -torrent::Object -retrieve_d_bitfield(core::Download* download) { - const torrent::Bitfield* bitField = download->download()->file_list()->bitfield(); - - if (bitField->empty()) - return torrent::Object(""); - - return torrent::Object(torrent::utils::transform_to_hex_str(*bitField)); -} - void apply_d_add_peer(core::Download* download, const std::string& arg) { int port, ret; @@ -651,30 +644,36 @@ void cg_d_group_set(core::Download* download, const torrent::Objec void initialize_command_download() { - CMD2_DL("d.hash", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->hash()); }); - CMD2_DL("d.local_id", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->local_id()); }); - CMD2_DL("d.local_id_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); }); - CMD2_DL("d.bitfield", std::bind(&retrieve_d_bitfield, std::placeholders::_1)); - CMD2_DL("d.base_path", std::bind(&retrieve_d_base_path, std::placeholders::_1)); - CMD2_DL("d.base_filename", std::bind(&retrieve_d_base_filename, std::placeholders::_1)); + CMD2_DL("d.hash", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->hash()); }); + CMD2_DL("d.local_id", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->local_id()); }); + CMD2_DL("d.local_id_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); }); + CMD2_DL("d.bitfield", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(*download->download()->file_list()->bitfield()); }); + CMD2_DL("d.base_path", [](auto* download, auto) { return retrieve_d_base_path(download).str(); }); + CMD2_DL("d.base_path.base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64(); }); + CMD2_DL("d.base_path.or_base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_base64(); }); + CMD2_DL("d.base_filename", [](auto* download, auto) { return retrieve_d_base_filename(download).str(); }); + CMD2_DL("d.base_filename.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); }); + CMD2_DL("d.base_filename.or_base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_base64(); }); - CMD2_DL("d.name", CMD2_ON_INFO(name)); - CMD2_DL("d.creation_date", CMD2_ON_INFO(creation_date)); - CMD2_DL("d.load_date", CMD2_ON_INFO(load_date)); + CMD2_DL("d.name", [](auto* download, auto) { return download->info()->name().str(); }); + CMD2_DL("d.name.base64", [](auto* download, auto) { return download->info()->name().object_base64(); }); + CMD2_DL("d.name.or_base64", [](auto* download, auto) { return download->info()->name().object_utf8_or_base64(); }); + CMD2_DL("d.creation_date", [](auto* download, auto) { return download->info()->creation_date(); }); + CMD2_DL("d.load_date", [](auto* download, auto) { return download->info()->load_date(); }); // // Network related: // - CMD2_DL ("d.up.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(up_rate))); - CMD2_DL ("d.up.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(up_rate))); - CMD2_DL ("d.down.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(down_rate))); - CMD2_DL ("d.down.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(down_rate))); - CMD2_DL ("d.skip.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(skip_rate))); - CMD2_DL ("d.skip.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(skip_rate))); + CMD2_DL ("d.up.rate", [](auto* download, auto) { return download->info()->up_rate()->rate(); }); + CMD2_DL ("d.up.total", [](auto* download, auto) { return download->info()->up_rate()->total(); }); + CMD2_DL ("d.down.rate", [](auto* download, auto) { return download->info()->down_rate()->rate(); }); + CMD2_DL ("d.down.total", [](auto* download, auto) { return download->info()->down_rate()->total(); }); + CMD2_DL ("d.skip.rate", [](auto* download, auto) { return download->info()->skip_rate()->rate(); }); + CMD2_DL ("d.skip.total", [](auto* download, auto) { return download->info()->skip_rate()->total(); }); - CMD2_DL ("d.peer_exchange", CMD2_ON_INFO(is_pex_enabled)); - CMD2_DL_VALUE_V ("d.peer_exchange.set", std::bind(&torrent::Download::set_pex_enabled, CMD2_BIND_DL, std::placeholders::_2)); + CMD2_DL ("d.peer_exchange", [](auto* download, auto) { return download->info()->is_pex_enabled(); }); + CMD2_DL_VALUE_V ("d.peer_exchange.set", [](auto* download, auto value) { download->download()->set_pex_enabled(value); }); CMD2_DL_LIST ("d.create_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 0)); CMD2_DL_LIST ("d.delete_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 1)); @@ -690,16 +689,16 @@ initialize_command_download() { // Control functinos: // - CMD2_DL ("d.is_open", CMD2_ON_INFO(is_open)); - CMD2_DL ("d.is_active", CMD2_ON_INFO(is_active)); + CMD2_DL ("d.is_open", [](auto* download, auto) { return download->info()->is_open(); }); + CMD2_DL ("d.is_active", [](auto* download, auto) { return download->info()->is_active(); }); CMD2_DL ("d.is_hash_checked", std::bind(&torrent::Download::is_hash_checked, CMD2_BIND_DL)); CMD2_DL ("d.is_hash_checking", std::bind(&torrent::Download::is_hash_checking, CMD2_BIND_DL)); CMD2_DL ("d.is_multi_file", std::bind(&torrent::FileList::is_multi_file, CMD2_BIND_FL)); - CMD2_DL ("d.is_private", CMD2_ON_INFO(is_private)); - CMD2_DL ("d.is_pex_active", CMD2_ON_INFO(is_pex_active)); + CMD2_DL ("d.is_private", [](auto* download, auto) { return download->info()->is_private(); }); + CMD2_DL ("d.is_pex_active", [](auto* download, auto) { return download->info()->is_pex_active(); }); CMD2_DL ("d.is_partially_done", CMD2_ON_DATA(is_partially_done)); CMD2_DL ("d.is_not_partially_done", CMD2_ON_DATA(is_not_partially_done)); - CMD2_DL ("d.is_meta", CMD2_ON_INFO(is_meta_download)); + CMD2_DL ("d.is_meta", [](auto* download, auto) { return download->info()->is_meta_download(); }); CMD2_DL_V ("d.resume", std::bind(&core::DownloadList::resume_default, control->core()->download_list(), std::placeholders::_1)); CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1)); @@ -901,8 +900,14 @@ initialize_command_download() { 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_path.base64"); + rpc::rpc.mark_safe("d.base_path.or_base64"); rpc::rpc.mark_safe("d.base_filename"); + rpc::rpc.mark_safe("d.base_filename.base64"); + rpc::rpc.mark_safe("d.base_filename.or_base64"); rpc::rpc.mark_safe("d.name"); + rpc::rpc.mark_safe("d.name.base64"); + rpc::rpc.mark_safe("d.name.or_base64"); rpc::rpc.mark_safe("d.directory"); rpc::rpc.mark_safe("d.directory_base"); rpc::rpc.mark_safe("d.creation_date"); diff --git a/src/command_file.cc b/src/command_file.cc index 581e696b..878c97e6 100644 --- a/src/command_file.cc +++ b/src/command_file.cc @@ -24,24 +24,46 @@ apply_f_path(torrent::File* file) { if (file->path()->empty()) return std::string(); - torrent::Object resultRaw(*file->path()->begin()); - torrent::Object::string_type& result = resultRaw.as_string(); + torrent::Object result_raw(file->path()->begin()->str()); + auto& result = result_raw.as_string(); for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++) - result += '/' + *itr; + result += '/' + itr->str(); - return resultRaw; + return result_raw; } torrent::Object apply_f_path_components(torrent::File* file) { - torrent::Object resultRaw = torrent::Object::create_list(); - torrent::Object::list_type& result = resultRaw.as_list(); + auto result_raw = torrent::Object::create_list(); + auto& result = result_raw.as_list(); for (const auto& itr : *file->path()) - result.push_back(itr); + result.push_back(itr.str()); - return resultRaw; + return result_raw; +} + +torrent::Object +apply_f_path_components_base64(torrent::File* file) { + auto result_raw = torrent::Object::create_list(); + auto& result = result_raw.as_list(); + + for (const auto& itr : *file->path()) + result.push_back(itr.object_base64()); + + return result_raw; +} + +torrent::Object +apply_f_path_components_or_base64(torrent::File* file) { + auto result_raw = torrent::Object::create_list(); + auto& result = result_raw.as_list(); + + for (const auto& itr : *file->path()) + result.push_back(itr.object_utf8_or_base64()); + + return result_raw; } torrent::Object @@ -57,7 +79,7 @@ apply_fi_filename_last(torrent::FileListIterator* itr) { if (itr->depth() >= itr->file()->path()->size()) return "ERROR"; - return itr->file()->path()->at(itr->depth()); + return itr->file()->path()->at(itr->depth()).str(); } void @@ -91,10 +113,14 @@ initialize_command_file() { CMD2_FILE("f.priority", std::bind(&torrent::File::priority, std::placeholders::_1)); CMD2_FILE_VALUE_V("f.priority.set", std::bind(&apply_f_set_priority, std::placeholders::_1, std::placeholders::_2)); - CMD2_FILE("f.path", std::bind(&apply_f_path, std::placeholders::_1)); - CMD2_FILE("f.path_components", std::bind(&apply_f_path_components, std::placeholders::_1)); - CMD2_FILE("f.path_depth", std::bind(&apply_f_path_depth, std::placeholders::_1)); - CMD2_FILE("f.frozen_path", std::bind(&torrent::File::frozen_path, std::placeholders::_1)); + CMD2_FILE("f.path", [](auto* file, auto) { return apply_f_path(file); }); + CMD2_FILE("f.path_components", [](auto* file, auto) { return apply_f_path_components(file); }); + CMD2_FILE("f.path_components.base64", [](auto* file, auto) { return apply_f_path_components_base64(file); }); + CMD2_FILE("f.path_components.or_base64", [](auto* file, auto) { return apply_f_path_components_or_base64(file); }); + CMD2_FILE("f.path_depth", [](auto* file, auto) { return apply_f_path_depth(file); }); + CMD2_FILE("f.frozen_path", [](auto* file, auto) { return file->frozen_path().str(); }); + CMD2_FILE("f.frozen_path.base64", [](auto* file, auto) { return file->frozen_path().object_base64(); }); + CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); }); CMD2_FILE("f.match_depth_prev", std::bind(&torrent::File::match_depth_prev, std::placeholders::_1)); CMD2_FILE("f.match_depth_next", std::bind(&torrent::File::match_depth_next, std::placeholders::_1)); @@ -108,6 +134,8 @@ initialize_command_file() { 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.frozen_path.base64"); + rpc::rpc.mark_safe("f.frozen_path.or_base64"); rpc::rpc.mark_safe("f.offset"); rpc::rpc.mark_safe("f.size_bytes"); rpc::rpc.mark_safe("f.size_chunks"); diff --git a/src/core/download.cc b/src/core/download.cc index ff166764..be53b3f2 100644 --- a/src/core/download.cc +++ b/src/core/download.cc @@ -116,7 +116,7 @@ Download::set_root_directory(const std::string& path) { (file_list->is_multi_file() ? !file_list->is_root_dir_created() : - !file_stat.update(file_list->front()->frozen_path()))) { + !file_stat.update(file_list->front()->frozen_path().str()))) { set_message("Cannot change the directory of an open download after the files have been moved."); rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this)); diff --git a/src/core/download_list.cc b/src/core/download_list.cc index fa5395e3..76dbdb65 100644 --- a/src/core/download_list.cc +++ b/src/core/download_list.cc @@ -683,8 +683,10 @@ DownloadList::process_meta_download(Download* download) { rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download)); rpc::call_command("d.close", torrent::Object(), rpc::make_target(download)); - std::string metafile = (*download->file_list()->begin())->frozen_path(); + std::string metafile = (*download->file_list()->begin())->frozen_path().str(); + 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."); return; @@ -692,11 +694,13 @@ DownloadList::process_meta_download(Download* download) { torrent::Object* bencode = new torrent::Object(torrent::Object::create_map()); file >> bencode->insert_key("info", torrent::Object()); + if (file.fail()) { delete bencode; lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent."); return; } + file.close(); // Steal the keys we still need. The old download has no use for them. @@ -707,6 +711,7 @@ DownloadList::process_meta_download(Download* download) { bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list")); erase_ptr(download); + control->core()->try_create_download_from_meta_download(bencode, metafile); } diff --git a/src/display/window_file_list.cc b/src/display/window_file_list.cc index bb06dd76..c15207c7 100644 --- a/src/display/window_file_list.cc +++ b/src/display/window_file_list.cc @@ -135,8 +135,11 @@ WindowFileList::redraw() { m_canvas->print(0, pos, "%*c%-*s", 16, ' ', filenameWidth, "EMPTY"); } else if (itr.is_entering()) { - m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', - itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); + if (itr.depth() < itr.file()->path()->size()) + m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', + wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str()); + else + m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', L"UNKNOWN"); } else if (itr.is_leaving()) { m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), ""); @@ -166,8 +169,12 @@ WindowFileList::redraw() { else m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40)); - m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', - itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); + if (itr.depth() < itr.file()->path()->size()) + m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', + wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str()); + else + m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', L"UNKNOWN"); + } else { m_canvas->print(0, pos, "BORK BORK"); diff --git a/src/rpc/jsonrpc.cc b/src/rpc/jsonrpc.cc index 56c01855..a18ffaf7 100644 --- a/src/rpc/jsonrpc.cc +++ b/src/rpc/jsonrpc.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include "rpc/rpc_manager.h" #include "rpc/command.h" @@ -66,12 +67,23 @@ object_to_json(const torrent::Object& object) noexcept { switch (object.type()) { case torrent::Object::TYPE_VALUE: return object.as_value(); + case torrent::Object::TYPE_STRING: + if (object.flags() & torrent::Object::flag_base64) { + std::vector binary_data; + torrent::utils::transform_from_hex(object.as_string(), binary_data); + + return json::binary(binary_data); + } + return object.as_string(); + case torrent::Object::TYPE_LIST: { json result = json::array(); + for (const auto& obj : object.as_list()) result.push_back(object_to_json(obj)); + return result; } case torrent::Object::TYPE_MAP: { @@ -135,7 +147,9 @@ jsonrpc_call_command(const std::string& method, const json& params) { 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()); } diff --git a/src/rpc/xmlrpc_c.cc b/src/rpc/xmlrpc_c.cc index 65679ac6..8253347c 100644 --- a/src/rpc/xmlrpc_c.cc +++ b/src/rpc/xmlrpc_c.cc @@ -278,6 +278,9 @@ object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) { case torrent::Object::TYPE_STRING: { + if (object.flags() & torrent::Object::flag_base64) + return xmlrpc_base64_new(env, object.as_string().c_str(), object.as_string().size()); + #ifdef XMLRPC_HAVE_I8 // The versions that support I8 do implicit utf-8 validation. xmlrpc_value* result = xmlrpc_string_new(env, object.as_string().c_str()); diff --git a/src/rpc/xmlrpc_tinyxml2.cc b/src/rpc/xmlrpc_tinyxml2.cc index f7bb51a5..52f575a5 100644 --- a/src/rpc/xmlrpc_tinyxml2.cc +++ b/src/rpc/xmlrpc_tinyxml2.cc @@ -152,10 +152,18 @@ void print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) { switch (obj.type()) { case torrent::Object::TYPE_STRING: + if (obj.flags() & torrent::Object::flag_base64) { + printer->OpenElement("base64", true); + printer->PushText(obj.as_string().c_str()); + printer->CloseElement(true); + break; + } + printer->OpenElement("string", true); printer->PushText(obj.as_string().c_str()); printer->CloseElement(true); break; + case torrent::Object::TYPE_VALUE: printer->OpenElement("i8", true); printer->PushText(std::to_string(obj.as_value()).c_str()); diff --git a/src/ui/download.cc b/src/ui/download.cc index 4bc42bc4..71eb99a5 100644 --- a/src/ui/download.cc +++ b/src/ui/download.cc @@ -250,7 +250,7 @@ Download::activate_display(Display displayType, bool focusDisplay) { // Set title. switch (displayType) { case DISPLAY_MAX_SIZE: break; - default: control->ui()->window_title()->set_title(m_download->info()->name()); break; + default: control->ui()->window_title()->set_title(m_download->info()->name().str()); break; } control->display()->adjust_layout();