diff --git a/src/Makefile.am b/src/Makefile.am index 43571fad..d97f712a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -168,6 +168,7 @@ libsub_root_a_SOURCES = \ utils/directory.h \ utils/file_status_cache.cc \ utils/file_status_cache.h \ + utils/functional.h \ utils/list_focus.h \ utils/lockfile.cc \ utils/lockfile.h \ diff --git a/src/command_download.cc b/src/command_download.cc index dd8842cb..00eb18b6 100644 --- a/src/command_download.cc +++ b/src/command_download.cc @@ -68,7 +68,7 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type& const std::string& type = (itr++)->as_string(); const std::string& prefix = (itr++)->as_string(); const std::string& postfix = (itr++)->as_string(); - + if (type.empty()) throw torrent::input_error("Invalid arguments."); @@ -401,7 +401,7 @@ t_multicall(core::Download* download, const torrent::Object::list_type& args) { const std::string& cmd = cItr->as_string(); auto t = download->tracker_list()->at(itr); - row.push_back(rpc::parse_command(rpc::make_target(t), cmd.c_str(), cmd.c_str() + cmd.size()).first); + row.push_back(rpc::parse_command(rpc::make_target(&t), cmd.c_str(), cmd.c_str() + cmd.size()).first); } } diff --git a/src/command_network.cc b/src/command_network.cc index f3c9cd33..fba185a9 100644 --- a/src/command_network.cc +++ b/src/command_network.cc @@ -58,40 +58,33 @@ apply_tos(const torrent::Object::string_type& arg) { torrent::Object apply_encoding_list(const std::string& arg) { torrent::encoding_list()->push_back(arg); return torrent::Object(); } -torrent::File* -rpc_find_file(core::Download* download, uint32_t index) { - if (index >= download->file_list()->size_files()) - return NULL; - - return (*download->file_list())[index]; -} - -// Ergh... time to update the Tracker API to allow proper ptrs. -torrent::tracker::Tracker* -rpc_find_tracker(core::Download* download, uint32_t index) { - if (index >= download->tracker_list()->size()) - return NULL; - - return download->tracker_list()->at(index); -} - -torrent::Peer* -rpc_find_peer(core::Download* download, const torrent::HashString& hash) { - torrent::ConnectionList::iterator itr = download->connection_list()->find(hash.c_str()); - - if (itr == download->connection_list()->end()) - return NULL; - - return *itr; -} - void initialize_rpc() { rpc::rpc.initialize(); - rpc::rpc.slot_find_download() = [](const char* hash) { return control->core()->download_list()->find_hex_ptr(hash); }; - rpc::rpc.slot_find_file() = [](core::Download* d, uint32_t index) { return rpc_find_file(d, index); }; - rpc::rpc.slot_find_tracker() = [](core::Download* d, uint32_t index) { return rpc_find_tracker(d, index); }; - rpc::rpc.slot_find_peer() = [](core::Download* d, const torrent::HashString& hash) { return rpc_find_peer(d, hash); }; + + rpc::rpc.slot_find_download() = [](const char* hash) { + return control->core()->download_list()->find_hex_ptr(hash); + }; + rpc::rpc.slot_find_file() = [](core::Download* d, uint32_t index) -> torrent::File* { + if (index >= d->file_list()->size_files()) + throw torrent::input_error("invalid parameters: index not found"); + + return (*d->file_list())[index]; + }; + rpc::rpc.slot_find_tracker() = [](core::Download* d, uint32_t index) -> torrent::tracker::Tracker { + if (index >= d->tracker_list()->size()) + throw torrent::input_error("invalid parameters: index not found"); + + return d->tracker_list()->at(index); + }; + rpc::rpc.slot_find_peer() = [](core::Download* d, const torrent::HashString& hash) -> torrent::Peer* { + auto itr = d->connection_list()->find(hash.c_str()); + + if (itr == d->connection_list()->end()) + throw torrent::input_error("invalid parameters: hash not found"); + + return *itr; + }; unsigned int count = 0; diff --git a/src/command_tracker.cc b/src/command_tracker.cc index f19aea78..add7d050 100644 --- a/src/command_tracker.cc +++ b/src/command_tracker.cc @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -30,7 +30,7 @@ struct call_add_node_t { if (sa == NULL) { lt_log_print(torrent::LOG_DHT_WARN, "Could not resolve host."); } else { - torrent::dht_manager()->add_node(sa, m_port); + torrent::dht_controller()->add_node(sa, m_port); } } @@ -39,7 +39,7 @@ struct call_add_node_t { torrent::Object apply_dht_add_node(const std::string& arg) { - if (!torrent::dht_manager()->is_valid()) + if (!torrent::dht_controller()->is_valid()) throw torrent::input_error("DHT not enabled."); int port, ret; diff --git a/src/core/dht_manager.cc b/src/core/dht_manager.cc index 5628cb2b..d728c8ce 100644 --- a/src/core/dht_manager.cc +++ b/src/core/dht_manager.cc @@ -1,47 +1,11 @@ -// 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 -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #include "config.h" #include #include #include -#include #include #include +#include #include #include "rpc/parse_commands.h" @@ -94,7 +58,7 @@ DhtManager::load_dht_cache() { LT_LOG_THIS("could not open cache file (path:%s)", cache_filename.c_str()); } - torrent::dht_manager()->initialize(cache); + torrent::dht_controller()->initialize(cache); if (m_start == dht_on) start_dht(); @@ -104,31 +68,31 @@ void DhtManager::start_dht() { priority_queue_erase(&taskScheduler, &m_stopTimeout); - if (!torrent::dht_manager()->is_valid()) { + if (!torrent::dht_controller()->is_valid()) { LT_LOG_THIS("server start skipped, manager is uninitialized", 0); return; } - if (torrent::dht_manager()->is_active()) { + if (torrent::dht_controller()->is_active()) { LT_LOG_THIS("server start skipped, already active", 0); return; } torrent::ThrottlePair throttles = control->core()->get_throttle(m_throttleName); - torrent::dht_manager()->set_upload_throttle(throttles.first); - torrent::dht_manager()->set_download_throttle(throttles.second); + torrent::dht_controller()->set_upload_throttle(throttles.first); + torrent::dht_controller()->set_download_throttle(throttles.second); int port = rpc::call_command_value("dht.port"); if (port <= 0) return; - if (!torrent::dht_manager()->start(port)) { + if (!torrent::dht_controller()->start(port)) { m_start = dht_off; return; } - torrent::dht_manager()->reset_statistics(); + torrent::dht_controller()->reset_statistics(); m_updateTimeout.slot() = std::bind(&DhtManager::update, this); priority_queue_insert(&taskScheduler, &m_updateTimeout, (cachedTime + rak::timer::from_seconds(60)).round_seconds()); @@ -146,17 +110,17 @@ DhtManager::stop_dht() { priority_queue_erase(&taskScheduler, &m_updateTimeout); priority_queue_erase(&taskScheduler, &m_stopTimeout); - if (torrent::dht_manager()->is_active()) { + if (torrent::dht_controller()->is_active()) { LT_LOG_THIS("stopping server", 0); log_statistics(true); - torrent::dht_manager()->stop(); + torrent::dht_controller()->stop(); } } void DhtManager::save_dht_cache() { - if (!control->core()->download_store()->is_enabled() || !torrent::dht_manager()->is_valid()) + if (!control->core()->download_store()->is_enabled() || !torrent::dht_controller()->is_valid()) return; std::string filename = control->core()->download_store()->path() + "rtorrent.dht_cache"; @@ -167,7 +131,7 @@ DhtManager::save_dht_cache() { return; torrent::Object cache = torrent::Object::create_map(); - cache_file << *torrent::dht_manager()->store_cache(&cache); + cache_file << *torrent::dht_controller()->store_cache(&cache); if (!cache_file.good()) return; @@ -198,7 +162,7 @@ DhtManager::set_mode(const std::string& arg) { void DhtManager::update() { - if (!torrent::dht_manager()->is_active()) + if (!torrent::dht_controller()->is_active()) throw torrent::internal_error("DhtManager::update called with DHT inactive."); if (m_start == dht_auto && !m_stopTimeout.is_queued()) { @@ -207,7 +171,7 @@ DhtManager::update() { for (itr = control->core()->download_list()->begin(), end = control->core()->download_list()->end(); itr != end; ++itr) if ((*itr)->download()->info()->is_active() && !(*itr)->download()->info()->is_private()) break; - + if (itr == end) { m_stopTimeout.slot() = std::bind(&DhtManager::stop_dht, this); priority_queue_insert(&taskScheduler, &m_stopTimeout, (cachedTime + rak::timer::from_seconds(15 * 60)).round_seconds()); @@ -223,17 +187,17 @@ DhtManager::update() { bool DhtManager::log_statistics(bool force) { - torrent::DhtManager::statistics_type stats = torrent::dht_manager()->get_statistics(); + auto stats = torrent::dht_controller()->get_statistics(); // Check for firewall problems. if (stats.cycle > 2 && stats.queries_sent - m_dhtPrevQueriesSent > 100 && stats.queries_received == m_dhtPrevQueriesReceived) { // We should have had clients ping us at least but have received // nothing, that means the UDP port is probably unreachable. - if (torrent::dht_manager()->can_receive_queries()) + if (torrent::dht_controller()->is_receiving_requests()) LT_LOG_THIS("listening port appears to be unreachable, no queries received", 0); - torrent::dht_manager()->set_can_receive(false); + torrent::dht_controller()->set_receive_requests(false); } if (stats.queries_sent - m_dhtPrevQueriesSent > stats.num_nodes * 2 + 20 && stats.replies_received == m_dhtPrevRepliesReceived) { @@ -248,7 +212,7 @@ DhtManager::log_statistics(bool force) { m_warned = false; if (stats.queries_received > m_dhtPrevQueriesReceived) - torrent::dht_manager()->set_can_receive(true); + torrent::dht_controller()->set_receive_requests(true); // Nothing to log while bootstrapping, but check again every minute. if (stats.cycle <= 1) { @@ -269,7 +233,7 @@ DhtManager::log_statistics(bool force) { // afterwards (i.e. every 2 hours), or when forced. if ((force && stats.cycle != m_dhtPrevCycle) || stats.cycle == 3 || stats.cycle > m_dhtPrevCycle + 7) { char buffer[256]; - snprintf(buffer, sizeof(buffer), + snprintf(buffer, sizeof(buffer), "DHT statistics: %d queries in, %d queries out, %d replies received, %lld bytes read, %lld bytes sent, " "%d known nodes in %d buckets, %d peers (highest: %d) tracked in %d torrents.", stats.queries_received - m_dhtPrevQueriesReceived, @@ -301,11 +265,11 @@ DhtManager::dht_statistics() { torrent::Object dhtStats = torrent::Object::create_map(); dhtStats.insert_key("dht", dht_settings[m_start]); - dhtStats.insert_key("active", torrent::dht_manager()->is_active()); + dhtStats.insert_key("active", torrent::dht_controller()->is_active()); dhtStats.insert_key("throttle", m_throttleName); - if (torrent::dht_manager()->is_active()) { - torrent::DhtManager::statistics_type stats = torrent::dht_manager()->get_statistics(); + if (torrent::dht_controller()->is_active()) { + auto stats = torrent::dht_controller()->get_statistics(); dhtStats.insert_key("cycle", stats.cycle); dhtStats.insert_key("queries_received", stats.queries_received); @@ -327,7 +291,7 @@ DhtManager::dht_statistics() { void DhtManager::set_throttle_name(const std::string& throttleName) { - if (torrent::dht_manager()->is_active()) + if (torrent::dht_controller()->is_active()) throw torrent::input_error("Cannot set DHT throttle while active."); m_throttleName = throttleName; diff --git a/src/core/dht_manager.h b/src/core/dht_manager.h index 0576f18e..6f18eca7 100644 --- a/src/core/dht_manager.h +++ b/src/core/dht_manager.h @@ -1,39 +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 -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #ifndef RTORRENT_CORE_DHT_MANAGER_H #define RTORRENT_CORE_DHT_MANAGER_H diff --git a/src/core/download.cc b/src/core/download.cc index 8ace1c9c..798219ea 100644 --- a/src/core/download.cc +++ b/src/core/download.cc @@ -39,11 +39,11 @@ Download::~Download() { void Download::enable_udp_trackers(bool state) { for (torrent::TrackerList::iterator itr = m_download.tracker_list()->begin(), last = m_download.tracker_list()->end(); itr != last; ++itr) - if ((*itr)->type() == torrent::TRACKER_UDP) { + if (itr->type() == torrent::TRACKER_UDP) { if (state) - (*itr)->enable(); + itr->enable(); else - (*itr)->disable(); + itr->disable(); } } diff --git a/src/display/utils.cc b/src/display/utils.cc index 144db8f9..9b1dc2c4 100644 --- a/src/display/utils.cc +++ b/src/display/utils.cc @@ -164,10 +164,10 @@ print_download_status(char* first, char* last, core::Download* d) { torrent::TrackerList::iterator itr = std::find_if(d->tracker_list()->begin(), d->tracker_list()->end(), std::mem_fn(&torrent::tracker::Tracker::is_busy_not_scrape)); - auto status = (*itr)->status(); + auto status = itr->status(); first = print_buffer(first, last, "Tracker[%i:%i]: Connecting to %s %s", - (*itr)->group(), std::distance(d->tracker_list()->begin(), itr), (*itr)->url().c_str(), status.c_str()); + itr->group(), std::distance(d->tracker_list()->begin(), itr), itr->url().c_str(), status.c_str()); } else if (!d->message().empty()) { first = print_buffer(first, last, "%s", d->message().c_str()); diff --git a/src/display/window_tracker_list.cc b/src/display/window_tracker_list.cc index 40551c92..e2b883c1 100644 --- a/src/display/window_tracker_list.cc +++ b/src/display/window_tracker_list.cc @@ -42,36 +42,36 @@ WindowTrackerList::redraw() { typedef std::pair Range; Range range = rak::advance_bidirectional(0, *m_focus, tl->size(), (m_canvas->height() - 1) / 2); - unsigned int group = tl->at(range.first)->group(); + unsigned int group = tl->at(range.first).group(); while (range.first != range.second) { auto tracker = tl->at(range.first); - if (tracker->group() == group) + if (tracker.group() == group) m_canvas->print(0, pos, "%2i:", group++); m_canvas->print(4, pos++, "%s", - tracker->url().c_str()); + tracker.url().c_str()); if (pos < m_canvas->height()) { const char* state; - if (tracker->is_busy_not_scrape()) + if (tracker.is_busy_not_scrape()) state = "req "; - else if (tracker->is_busy()) + else if (tracker.is_busy()) state = "scr "; else state = " "; - auto tracker_state = tracker->state(); + auto tracker_state = tracker.state(); m_canvas->print(0, pos++, "%s Id: %s Counters: %uf / %us (%u) %s S/L/D: %u/%u/%u (%u/%u)", state, - rak::copy_escape_html(tracker->tracker_id()).c_str(), + rak::copy_escape_html(tracker.tracker_id()).c_str(), tracker_state.failed_counter(), tracker_state.success_counter(), tracker_state.scrape_counter(), - tracker->is_usable() ? " on" : tracker->is_enabled() ? "err" : "off", + tracker.is_usable() ? " on" : tracker.is_enabled() ? "err" : "off", tracker_state.scrape_complete(), tracker_state.scrape_incomplete(), tracker_state.scrape_downloaded(), @@ -84,7 +84,7 @@ WindowTrackerList::redraw() { m_canvas->set_attr(4, pos - 1, m_canvas->width(), is_focused() ? A_REVERSE : A_BOLD, COLOR_PAIR(0)); } - if (tracker->is_busy()) { + if (tracker.is_busy()) { m_canvas->set_attr(0, pos - 2, 4, A_REVERSE, COLOR_PAIR(0)); m_canvas->set_attr(0, pos - 1, 4, A_REVERSE, COLOR_PAIR(0)); } diff --git a/src/rpc/jsonrpc.cc b/src/rpc/jsonrpc.cc index 3e6f2c50..223742c3 100644 --- a/src/rpc/jsonrpc.cc +++ b/src/rpc/jsonrpc.cc @@ -2,21 +2,20 @@ #include "rpc/jsonrpc.h" -#include "rpc/rpc_manager.h" -#include "torrent/exceptions.h" -#include "torrent/object.h" #include #include - #include #include +#include "thread_base.h" +#include "rpc/rpc_manager.h" #include "rpc/command.h" #include "rpc/command_map.h" +#include "rpc/nlohmann/json.h" #include "rpc/parse_commands.h" -#include "thread_base.h" - -#include "nlohmann/json.h" +#include "torrent/exceptions.h" +#include "torrent/object.h" +#include "utils/functional.h" namespace rpc { @@ -123,6 +122,9 @@ jsonrpc_call_command(const std::string& method, const json& params) { auto& params_object_list = params_object.as_list(); rpc::target_type target = rpc::make_target(); + std::function deleter = []() {}; + utils::scope_guard guard([&deleter]() { deleter(); }); + if (!(itr->second.m_flags & CommandMap::flag_no_target)) { // Provide a blank target if none was provided if (params_object_list.empty()) @@ -131,7 +133,8 @@ jsonrpc_call_command(const std::string& method, const json& params) { if (!params_object_list.begin()->is_string()) throw torrent::input_error("invalid parameters: target must be a string"); - RpcManager::object_to_target(params_object_list.begin()->as_string(), itr->second.m_flags, &target); + RpcManager::object_to_target(params_object_list.begin()->as_string(), itr->second.m_flags, &target, &deleter); + params_object_list.erase(params_object_list.begin()); } diff --git a/src/rpc/parse_commands.cc b/src/rpc/parse_commands.cc index ab0d0596..d5fecb80 100644 --- a/src/rpc/parse_commands.cc +++ b/src/rpc/parse_commands.cc @@ -36,7 +36,6 @@ #include "config.h" -#include "rpc/rpc_manager.h" #include #include #include @@ -46,13 +45,10 @@ #include "parse.h" #include "parse_commands.h" +#include "rpc/rpc_manager.h" namespace rpc { -CommandMap commands; -RpcManager rpc; -ExecFile execFile; - inline bool command_map_is_space(char c) { return c == ' ' || c == '\t'; } diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h index a9d9276f..bc3fb12f 100644 --- a/src/rpc/parse_commands.h +++ b/src/rpc/parse_commands.h @@ -40,8 +40,6 @@ #include #include -#include "command_map.h" -#include "exec_file.h" #include "xmlrpc.h" #include "rpc_manager.h" @@ -51,11 +49,6 @@ namespace core { namespace rpc { -// Move to another file? -extern CommandMap commands; -extern RpcManager rpc; -extern ExecFile execFile; - typedef std::pair parse_command_type; // The generic parse command function, used by the rest. At some point diff --git a/src/rpc/rpc_manager.cc b/src/rpc/rpc_manager.cc index 9feeb572..5409a4f3 100644 --- a/src/rpc/rpc_manager.cc +++ b/src/rpc/rpc_manager.cc @@ -9,8 +9,12 @@ namespace rpc { +CommandMap commands; +RpcManager rpc; +ExecFile execFile; + void -RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* target) { +RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* target, std::function* deleter) { if (call_flags & CommandMap::flag_no_target) return; @@ -31,6 +35,7 @@ RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::ta char type = 'd'; std::string hash; std::string index; + const auto& delim_pos = target_string.find_first_of(':', 40); if (delim_pos == target_string.npos || delim_pos + 2 >= target_string.size()) { @@ -49,34 +54,43 @@ RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::ta throw torrent::input_error("invalid parameters: info-hash not found"); try { + torrent::tracker::Tracker* tracker; + switch (type) { case 'd': *target = rpc::make_target(download); break; + case 'f': - *target = rpc::make_target( - command_base::target_file, - rpc.slot_find_file()(download, std::stoi(std::string(index)))); + *target = rpc::make_target(command_base::target_file, + rpc.slot_find_file()(download, std::stoi(std::string(index)))); + break; + case 't': - *target = rpc::make_target( - command_base::target_tracker, - rpc.slot_find_tracker()(download, std::stoi(std::string(index)))); + tracker = new torrent::tracker::Tracker(rpc.slot_find_tracker()(download, std::stoi(std::string(index)))); + + *target = rpc::make_target(command_base::target_tracker, tracker); + *deleter = [tracker]() { delete tracker; }; break; + case 'p': { if (index.size() < 40) { throw torrent::input_error("invalid parameters: not a hash string."); } + torrent::HashString hash; torrent::hash_string_from_hex_c_str(index.c_str(), hash); - *target = rpc::make_target( - command_base::target_peer, - rpc.slot_find_peer()(download, hash)); + + *target = rpc::make_target(command_base::target_peer, + rpc.slot_find_peer()(download, hash)); + break; } default: throw torrent::input_error("invalid parameters: unexpected target type"); } + } catch (const std::logic_error&) { throw torrent::input_error("invalid parameters: invalid index"); } diff --git a/src/rpc/rpc_manager.h b/src/rpc/rpc_manager.h index 4839ace0..a6474315 100644 --- a/src/rpc/rpc_manager.h +++ b/src/rpc/rpc_manager.h @@ -6,6 +6,8 @@ #include #include "rpc/command.h" +#include "rpc/command_map.h" +#include "rpc/exec_file.h" #include "rpc/jsonrpc.h" #include "rpc/xmlrpc.h" @@ -15,6 +17,9 @@ class Download; namespace rpc { +extern ExecFile execFile; +extern CommandMap commands; + class rpc_error : public torrent::base_error { public: rpc_error(int type, std::string msg) : @@ -33,7 +38,7 @@ class RpcManager { public: using slot_download = std::function; using slot_file = std::function; - using slot_tracker = std::function; + using slot_tracker = std::function; using slot_peer = std::function; using slot_response_callback = std::function; @@ -58,13 +63,14 @@ public: bool process(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); - static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target); 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; } + static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target, std::function* deleter); + private: XmlRpc m_xmlrpc; JsonRpc m_jsonrpc; @@ -78,6 +84,9 @@ private: slot_tracker m_slot_find_tracker; slot_peer m_slot_find_peer; }; + +extern RpcManager rpc; + } // namespace rpc #endif diff --git a/src/rpc/xmlrpc.cc b/src/rpc/xmlrpc.cc index 09acb6b7..ca655b3e 100644 --- a/src/rpc/xmlrpc.cc +++ b/src/rpc/xmlrpc.cc @@ -1,39 +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 -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #include "config.h" #include "xmlrpc.h" diff --git a/src/rpc/xmlrpc.h b/src/rpc/xmlrpc.h index 07028d0b..825cc485 100644 --- a/src/rpc/xmlrpc.h +++ b/src/rpc/xmlrpc.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "command.h" #include "scgi_task.h" @@ -18,7 +19,7 @@ class XmlRpc { public: typedef std::function slot_download; typedef std::function slot_file; - typedef std::function slot_tracker; + typedef std::function slot_tracker; typedef std::function slot_peer; typedef std::function slot_write; diff --git a/src/rpc/xmlrpc_c.cc b/src/rpc/xmlrpc_c.cc index f15bc8be..95bace3c 100644 --- a/src/rpc/xmlrpc_c.cc +++ b/src/rpc/xmlrpc_c.cc @@ -1,39 +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 -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #include "config.h" #ifdef HAVE_XMLRPC_C @@ -43,17 +7,16 @@ #include #include - #include -#include - #include #include #include +#include #include "rpc_manager.h" #include "xmlrpc.h" #include "parse_commands.h" +#include "utils/functional.h" namespace rpc { @@ -71,7 +34,7 @@ private: const char* m_msg; }; -torrent::Object xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType = 0, rpc::target_type* target = NULL); +torrent::Object xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int call_type = 0, rpc::target_type* target = NULL, std::function* deleter = NULL); inline torrent::Object xmlrpc_list_entry_to_object(xmlrpc_env* env, xmlrpc_value* src, int index) { @@ -135,59 +98,70 @@ xmlrpc_list_entry_to_value(xmlrpc_env* env, xmlrpc_value* src, int index) { } } -rpc::target_type -xmlrpc_to_target(xmlrpc_env* env, xmlrpc_value* value, int callType) { +std::pair> +xmlrpc_to_target(xmlrpc_env* env, xmlrpc_value* value, int call_type) { rpc::target_type target; + std::function deleter = []() {}; - Rpc::object_to_target(xmlrpc_to_object(env, value, -1, nullptr), callType, &target); - return target; + RpcManager::object_to_target(xmlrpc_to_object(env, value, -1, nullptr), call_type, &target, &deleter); + + return std::make_pair(target, deleter); } -rpc::target_type -xmlrpc_to_index_type(int index, int callType, core::Download* download) { - void* result; +std::pair> +xmlrpc_to_index_type(int index, int call_type, core::Download* download) { + void* result = nullptr; + std::function deleter = []() {}; - switch (callType) { - case XmlRpc::call_file: result = xmlrpc.slot_find_file()(download, index); break; - case XmlRpc::call_tracker: result = xmlrpc.slot_find_tracker()(download, index); break; - default: result = NULL; break; + try { + + switch (call_type) { + case XmlRpc::call_file: + result = rpc.slot_find_file()(download, index); + break; + + case XmlRpc::call_tracker: + result = new torrent::tracker::Tracker(rpc.slot_find_tracker()(download, index)); + deleter = [result]() { delete (torrent::tracker::Tracker*)result; }; + break; + + default: + throw torrent::input_error("invalid parameters: unexpected target type"); + break; + } + + } catch (const torrent::input_error& e) { + throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, e.what()); } - if (result == NULL) - throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid index."); - - return rpc::make_target(callType, result); + return std::make_pair(rpc::make_target(call_type, result), deleter); } torrent::Object -xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target_type* target) { +xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int call_type, rpc::target_type* target, std::function* deleter) { switch (xmlrpc_value_type(value)) { case XMLRPC_TYPE_INT: int v; xmlrpc_read_int(env, value, &v); - + return torrent::Object((int64_t)v); #ifdef XMLRPC_HAVE_I8 case XMLRPC_TYPE_I8: xmlrpc_int64 v2; xmlrpc_read_i8(env, value, &v2); - + return torrent::Object((int64_t)v2); #endif - // case XMLRPC_TYPE_BOOL: - // case XMLRPC_TYPE_DOUBLE: - // case XMLRPC_TYPE_DATETIME: - case XMLRPC_TYPE_STRING: - if (callType != XmlRpc::call_generic && target != nullptr) { + if (call_type != XmlRpc::call_generic && target != nullptr) { // When the call type is not supposed to be void, we'll try to // convert it to a command target. It's not that important that // it is converted to the right type here, as an mismatch will // be caught when executing the command. - *target = xmlrpc_to_target(env, value, callType); + std::tie(*target, *deleter) = xmlrpc_to_target(env, value, call_type); return torrent::Object(); } else { @@ -199,7 +173,6 @@ xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target torrent::Object result = torrent::Object(std::string(valueString)); - // Urgh, seriously? ::free((void*)valueString); return result; } @@ -216,7 +189,6 @@ xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target torrent::Object result = torrent::Object(std::string(valueString, valueSize)); - // Urgh, seriously? ::free((void*)valueString); return result; } @@ -229,7 +201,7 @@ xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target if (env->fault_occurred) throw xmlrpc_error_c(env); - if (callType != XmlRpc::call_generic && last != 0) { + if (call_type != XmlRpc::call_generic && last != 0) { if (last < 1) throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Too few arguments."); @@ -240,14 +212,15 @@ xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target throw xmlrpc_error_c(env); if (target != nullptr) - *target = xmlrpc_to_target(env, tmp, callType); + std::tie(*target, *deleter) = xmlrpc_to_target(env, tmp, call_type); + xmlrpc_DECREF(tmp); if (env->fault_occurred) throw xmlrpc_error_c(env); if (target->first == XmlRpc::call_download && - (callType == XmlRpc::call_file || callType == XmlRpc::call_tracker)) { + (call_type == XmlRpc::call_file || call_type == XmlRpc::call_tracker)) { // If we have a download target and the call type requires // another contained type, then we try to use the next // parameter as the index to support old-style calls. @@ -255,7 +228,17 @@ xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target if (current == last) throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Too few arguments, missing index."); - *target = xmlrpc_to_index_type(xmlrpc_list_entry_to_value(env, value, current++), callType, (core::Download*)target->second); + { + std::function old_deleter, tmp_deleter; + std::tie(*target, tmp_deleter) = xmlrpc_to_index_type(xmlrpc_list_entry_to_value(env, value, current++), + call_type, + (core::Download*)target->second); + old_deleter.swap(*deleter); + *deleter = [old_deleter, tmp_deleter]() { + tmp_deleter(); + old_deleter(); + }; + } } } @@ -276,10 +259,6 @@ xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target } } - // case XMLRPC_TYPE_STRUCT: - // case XMLRPC_TYPE_C_PTR: - // case XMLRPC_TYPE_NIL: - // case XMLRPC_TYPE_DEAD: default: throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Unsupported type found."); } @@ -291,7 +270,7 @@ object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) { case torrent::Object::TYPE_VALUE: #ifdef XMLRPC_HAVE_I8 - if (xmlrpc.dialect() != XmlRpc::dialect_generic) + if (rpc.dialect() != XmlRpc::dialect_generic) return xmlrpc_i8_new(env, object.as_value()); #else return xmlrpc_int_new(env, object.as_value()); @@ -329,7 +308,7 @@ object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) { case torrent::Object::TYPE_LIST: { xmlrpc_value* result = xmlrpc_array_new(env); - + for (torrent::Object::list_const_iterator itr = object.as_list().begin(), last = object.as_list().end(); itr != last; itr++) { xmlrpc_value* item = object_to_xmlrpc(env, *itr); xmlrpc_array_append_item(env, result, item); @@ -342,7 +321,7 @@ object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) { case torrent::Object::TYPE_MAP: { xmlrpc_value* result = xmlrpc_struct_new(env); - + for (torrent::Object::map_const_iterator itr = object.as_map().begin(), last = object.as_map().end(); itr != last; itr++) { xmlrpc_value* item = object_to_xmlrpc(env, itr->second); xmlrpc_struct_set_value(env, result, itr->first.c_str(), item); @@ -355,11 +334,11 @@ object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) { case torrent::Object::TYPE_DICT_KEY: { xmlrpc_value* result = xmlrpc_array_new(env); - + xmlrpc_value* key_item = object_to_xmlrpc(env, object.as_dict_key()); xmlrpc_array_append_item(env, result, key_item); xmlrpc_DECREF(key_item); - + if (object.as_dict_obj().is_list()) { for (torrent::Object::list_const_iterator itr = object.as_dict_obj().as_list().begin(), @@ -392,18 +371,21 @@ xmlrpc_call_command(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) { return NULL; } + std::function deleter = []() {}; + utils::scope_guard guard([&deleter]() { deleter(); }); + try { torrent::Object object; rpc::target_type target = rpc::make_target(); if (itr->second.m_flags & CommandMap::flag_no_target) - xmlrpc_to_object(env, args, XmlRpc::call_generic, &target).swap(object); + xmlrpc_to_object(env, args, XmlRpc::call_generic, &target, &deleter).swap(object); else if (itr->second.m_flags & CommandMap::flag_file_target) - xmlrpc_to_object(env, args, XmlRpc::call_file, &target).swap(object); + xmlrpc_to_object(env, args, XmlRpc::call_file, &target, &deleter).swap(object); else if (itr->second.m_flags & CommandMap::flag_tracker_target) - xmlrpc_to_object(env, args, XmlRpc::call_tracker, &target).swap(object); + xmlrpc_to_object(env, args, XmlRpc::call_tracker, &target, &deleter).swap(object); else - xmlrpc_to_object(env, args, XmlRpc::call_any, &target).swap(object); + xmlrpc_to_object(env, args, XmlRpc::call_any, &target, &deleter).swap(object); if (env->fault_occurred) return NULL; @@ -425,7 +407,7 @@ XmlRpc::initialize() { #ifndef XMLRPC_HAVE_I8 m_dialect = dialect_generic; #endif - + m_env = new xmlrpc_env; xmlrpc_env_init((xmlrpc_env*)m_env); diff --git a/src/ui/element_tracker_list.cc b/src/ui/element_tracker_list.cc index e3e02719..d7b274ed 100644 --- a/src/ui/element_tracker_list.cc +++ b/src/ui/element_tracker_list.cc @@ -69,10 +69,10 @@ ElementTrackerList::receive_disable() { auto t = m_download->download()->tracker_list()->at(m_focus); - if (t->is_enabled()) - t->disable(); + if (t.is_enabled()) + t.disable(); else - t->enable(); + t.enable(); m_window->mark_dirty(); } @@ -114,7 +114,7 @@ ElementTrackerList::receive_cycle_group() { if (m_focus >= tl->size()) throw torrent::internal_error("ui::ElementTrackerList::receive_group_cycle(...) called with an invalid focus"); - tl->cycle_group(tl->at(m_focus)->group()); + tl->cycle_group(tl->at(m_focus).group()); m_window->mark_dirty(); } diff --git a/src/utils/functional.h b/src/utils/functional.h new file mode 100644 index 00000000..3a6c1c10 --- /dev/null +++ b/src/utils/functional.h @@ -0,0 +1,21 @@ +#ifndef RTORRENT_UTILS_FUNCTIONAL_H +#define RTORRENT_UTILS_FUNCTIONAL_H + +#include + +namespace utils { + +// Create a class that calls a std::function when returning or exiting a scope. +class scope_guard { +public: + scope_guard(std::function on_exit_scope) : m_on_exit_scope(on_exit_scope) {} + ~scope_guard() { m_on_exit_scope(); } + +private: + std::function m_on_exit_scope; +}; + +} // namespace utils + +#endif +