diff --git a/src/command_dynamic.cc b/src/command_dynamic.cc index ff2b482c..7e7a96a0 100644 --- a/src/command_dynamic.cc +++ b/src/command_dynamic.cc @@ -42,39 +42,74 @@ #include "control.h" #include "command_helpers.h" +std::string +system_method_generate_command(torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last) { + std::string command; + + while (first != last) { + if (!command.empty()) + command += " ;"; + + command += (first++)->as_string(); + } + + return command; +} + +// system.method.insert {name, "simple|private|const", ...} +// system.method.insert {name, "list|private|const"} +// +// Add a new user-defined method called 'name' and any number of +// lines. torrent::Object system_method_insert(__UNUSED rpc::target_type target, const torrent::Object& rawArgs) { const torrent::Object::list_type& args = rawArgs.as_list(); - // Allow arbritary number of args, just use what ever necessary in - // order to create a function blob out of them. + if (args.empty() || ++args.begin() == args.end()) + throw torrent::input_error("Invalid argument count."); - if (args.empty()) - return torrent::Object(); - - const std::string& rawKey = args.front().as_string(); + torrent::Object::list_const_iterator itrArgs = args.begin(); + const std::string& rawKey = (itrArgs++)->as_string(); if (rawKey.empty() || rpc::commands.has(rawKey)) throw torrent::input_error("Invalid key."); - std::string command; + rpc::Command* command = NULL; + rpc::Command::any_slot slot = NULL; + int flags = rpc::CommandMap::flag_delete_key | rpc::CommandMap::flag_modifiable | rpc::CommandMap::flag_public_xmlrpc; - for (torrent::Object::list_const_iterator itr = ++args.begin(), last = args.end(); itr != last; itr++) { - if (!command.empty()) - command += " ;"; + const std::string& options = itrArgs->as_string(); - command += itr->as_string(); + // Use a | separated list of options here, for non-modifable, + // non-public methods, etc. + if (options.find("list") != std::string::npos) { + // Later, make it possible to add functions here. + slot = &rpc::CommandFunctionList::call; + command = new rpc::CommandFunctionList(); + + } else if (options.find("simple") != std::string::npos) { + slot = &rpc::CommandFunction::call; + command = new rpc::CommandFunction(system_method_generate_command(++itrArgs, args.end())); } + if (options.find("private") != std::string::npos) + flags &= ~rpc::CommandMap::flag_public_xmlrpc; + + if (options.find("const") != std::string::npos) + flags &= ~rpc::CommandMap::flag_modifiable; + char* key = new char[rawKey.size() + 1]; std::memcpy(key, rawKey.c_str(), rawKey.size() + 1); - rpc::commands.insert_type(key, new rpc::CommandFunction(command), &rpc::CommandFunction::call, - rpc::CommandMap::flag_delete_key | rpc::CommandMap::flag_modifiable | rpc::CommandMap::flag_public_xmlrpc, NULL, NULL); - + rpc::commands.insert_type(key, command, slot, flags, NULL, NULL); return torrent::Object(); } +// system.method.erase <> {name} +// +// Erase a modifiable method called 'name. Trying to remove methods +// that aren't modifiable, e.g. defined by rtorrent or set to +// read-only by the user, will result in an error. torrent::Object system_method_erase(__UNUSED rpc::target_type target, const torrent::Object& rawArgs) { rpc::CommandMap::iterator itr = rpc::commands.find(rawArgs.as_string().c_str()); @@ -90,8 +125,71 @@ system_method_erase(__UNUSED rpc::target_type target, const torrent::Object& raw return torrent::Object(); } +torrent::Object +system_method_set(__UNUSED rpc::target_type target, const torrent::Object& rawArgs) { + const torrent::Object::list_type& args = rawArgs.as_list(); + + if (args.empty()) + throw torrent::input_error("Invalid argument count."); + + rpc::CommandFunction* function; + rpc::CommandMap::iterator itr = rpc::commands.find(args.front().as_string().c_str()); + + if (itr == rpc::commands.end() || !(itr->second.m_flags & rpc::CommandMap::flag_modifiable) || + (function = dynamic_cast(itr->second.m_variable)) == NULL) + throw torrent::input_error("Command not modifiable or wrong type."); + + function->set_command(system_method_generate_command(++args.begin(), args.end())); + return torrent::Object(); +} + +torrent::Object +system_method_set_key(__UNUSED rpc::target_type target, const torrent::Object& rawArgs) { + const torrent::Object::list_type& args = rawArgs.as_list(); + + if (args.empty() || ++args.begin() == args.end()) + throw torrent::input_error("Invalid argument count."); + + rpc::CommandFunctionList* function; + rpc::CommandMap::iterator itr = rpc::commands.find(args.front().as_string().c_str()); + + if (itr == rpc::commands.end() || !(itr->second.m_flags & rpc::CommandMap::flag_modifiable) || + (function = dynamic_cast(itr->second.m_variable)) == NULL) + throw torrent::input_error("Command not modifiable or wrong type."); + + torrent::Object::list_const_iterator itrArgs = ++args.begin(); + const std::string& key = (itrArgs++)->as_string(); + + if (itrArgs != args.end()) + function->insert(key, system_method_generate_command(itrArgs, args.end())); + else + function->erase(key); + + return torrent::Object(); +} + +torrent::Object +system_method_has_key(__UNUSED rpc::target_type target, const torrent::Object& rawArgs) { + const torrent::Object::list_type& args = rawArgs.as_list(); + + if (args.size() != 2) + throw torrent::input_error("Invalid argument count."); + + rpc::CommandFunctionList* function; + rpc::CommandMap::iterator itr = rpc::commands.find(args.front().as_string().c_str()); + + if (itr == rpc::commands.end() || + (function = dynamic_cast(itr->second.m_variable)) == NULL) + throw torrent::input_error("Command is the wrong type."); + + return torrent::Object((int64_t)(function->find(args.back().as_string().c_str()) != function->end())); +} + void initialize_command_dynamic() { - CMD_G("system.method.insert", rak::ptr_fn(&system_method_insert)); + CMD_G("system.method.insert", rak::ptr_fn(&system_method_insert)); CMD_G_STRING("system.method.erase", rak::ptr_fn(&system_method_erase)); + CMD_G("system.method.set", rak::ptr_fn(&system_method_set)); + CMD_G("system.method.set_key", rak::ptr_fn(&system_method_set_key)); + CMD_G("system.method.has_key", rak::ptr_fn(&system_method_has_key)); } diff --git a/src/command_events.cc b/src/command_events.cc index 6dc78d35..937b336c 100644 --- a/src/command_events.cc +++ b/src/command_events.cc @@ -59,7 +59,7 @@ #include "command_helpers.h" torrent::Object -apply_on_state_change(core::DownloadList::slot_map* slotMap, const torrent::Object& rawArgs) { +apply_on_state_change(const char* name, const torrent::Object& rawArgs) { const torrent::Object::list_type& args = rawArgs.as_list(); if (args.size() == 0 || args.size() > 2) @@ -76,9 +76,18 @@ apply_on_state_change(core::DownloadList::slot_map* slotMap, const torrent::Obje std::string key = rawKey[0] != '_' ? ("1_state_" + rawKey) : rawKey.substr(1); if (args.size() == 1) - slotMap->erase(key); + rpc::commands.call("system.method.set_key", rpc::make_target(), rpc::create_object_list(name, key)); else - (*slotMap)[key] = args.back().as_string(); + rpc::commands.call("system.method.set_key", rpc::make_target(), rpc::create_object_list(name, key, args.back())); + + // Deprecated notice, remove this function in the next minor + // version. + static bool notify = true; + + if (notify) { + control->core()->push_log("Deprecated on_* commands, use 'system.method.set_key = event.download.{inserted, erased, ...}, , ' instead."); + notify = false; + } return torrent::Object(); } @@ -117,8 +126,8 @@ apply_on_ratio(int action, const torrent::Object& rawArgs) { switch (action) { // case core::DownloadList::SLOTS_CLOSE: success = downloadList->close_try(current); break; // case core::DownloadList::SLOTS_STOP: success = downloadList->stop_try(current); break; - case core::DownloadList::SLOTS_CLOSE: rpc::parse_command_single(rpc::make_target(current), "d.try_close="); break; - case core::DownloadList::SLOTS_STOP: rpc::parse_command_single(rpc::make_target(current), "d.try_stop="); break; + case core::DownloadList::D_SLOTS_CLOSE: rpc::parse_command_single(rpc::make_target(current), "d.try_close="); break; + case core::DownloadList::D_SLOTS_STOP: rpc::parse_command_single(rpc::make_target(current), "d.try_stop="); break; default: success = false; break; } @@ -341,26 +350,24 @@ d_multicall(const torrent::Object& rawArgs) { void initialize_command_events() { - core::DownloadList* downloadList = control->core()->download_list(); - ADD_VARIABLE_BOOL("check_hash", true); ADD_VARIABLE_BOOL("session_lock", true); ADD_VARIABLE_BOOL("session_on_completion", true); - ADD_COMMAND_LIST("on_insert", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_insert())); - ADD_COMMAND_LIST("on_erase", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_erase())); - ADD_COMMAND_LIST("on_open", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_open())); - ADD_COMMAND_LIST("on_close", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_close())); - ADD_COMMAND_LIST("on_start", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_start())); - ADD_COMMAND_LIST("on_stop", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_stop())); - ADD_COMMAND_LIST("on_hash_queued", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_hash_queued())); - ADD_COMMAND_LIST("on_hash_removed", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_hash_removed())); - ADD_COMMAND_LIST("on_hash_done", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_hash_done())); - ADD_COMMAND_LIST("on_finished", rak::bind_ptr_fn(&apply_on_state_change, &downloadList->slot_map_finished())); + // Deprecated. + ADD_COMMAND_LIST("on_insert", rak::bind_ptr_fn(&apply_on_state_change, "event.download.inserted")); + ADD_COMMAND_LIST("on_erase" , rak::bind_ptr_fn(&apply_on_state_change, "event.download.erased")); + ADD_COMMAND_LIST("on_open", rak::bind_ptr_fn(&apply_on_state_change, "event.download.opened")); + ADD_COMMAND_LIST("on_close", rak::bind_ptr_fn(&apply_on_state_change, "event.download.closed")); + ADD_COMMAND_LIST("on_start", rak::bind_ptr_fn(&apply_on_state_change, "event.download.resumed")); + ADD_COMMAND_LIST("on_stop", rak::bind_ptr_fn(&apply_on_state_change, "event.download.paused")); + ADD_COMMAND_LIST("on_hash_queued", rak::bind_ptr_fn(&apply_on_state_change, "event.download.hash_queued")); + ADD_COMMAND_LIST("on_hash_removed", rak::bind_ptr_fn(&apply_on_state_change, "event.download.hash_removed")); + ADD_COMMAND_LIST("on_finished", rak::bind_ptr_fn(&apply_on_state_change, "event.download.finished")); - ADD_COMMAND_LIST("stop_on_ratio", rak::bind_ptr_fn(&apply_on_ratio, (int)core::DownloadList::SLOTS_STOP)); - ADD_COMMAND_LIST("close_on_ratio", rak::bind_ptr_fn(&apply_on_ratio, (int)core::DownloadList::SLOTS_CLOSE)); + ADD_COMMAND_LIST("stop_on_ratio", rak::bind_ptr_fn(&apply_on_ratio, (int)core::DownloadList::D_SLOTS_STOP)); + ADD_COMMAND_LIST("close_on_ratio", rak::bind_ptr_fn(&apply_on_ratio, (int)core::DownloadList::D_SLOTS_CLOSE)); ADD_COMMAND_VOID("start_tied", &apply_start_tied); ADD_COMMAND_VOID("stop_untied", &apply_stop_untied); diff --git a/src/core/download_list.cc b/src/core/download_list.cc index 337e57c8..236cb948 100644 --- a/src/core/download_list.cc +++ b/src/core/download_list.cc @@ -72,19 +72,19 @@ DownloadList::check_contains(Download* d) { #endif } -struct download_list_call { - download_list_call(Download* d) : m_download(d) {} +// struct download_list_call { +// download_list_call(Download* d) : m_download(d) {} - void operator () (const DownloadList::slot_map::value_type& s) { - try { - rpc::parse_command_d_multiple_std(m_download, s.second); - } catch (torrent::input_error& e) { - control->core()->push_log((std::string("Download event action failed: ") + e.what()).c_str()); - } - } +// void operator () (const DownloadList::slot_map::value_type& s) { +// try { +// rpc::parse_command_d_multiple_std(m_download, s.second); +// } catch (torrent::input_error& e) { +// control->core()->push_log((std::string("Download event action failed: ") + e.what()).c_str()); +// } +// } - Download* m_download; -}; +// Download* m_download; +// }; void DownloadList::clear() { @@ -171,7 +171,7 @@ DownloadList::insert(Download* download) { std::for_each(control->view_manager()->begin(), control->view_manager()->end(), std::bind2nd(std::mem_fun(&View::insert), download)); std::for_each(control->view_manager()->begin(), control->view_manager()->end(), std::bind2nd(std::mem_fun(&View::filter_download), download)); - std::for_each(slot_map_insert().begin(), slot_map_insert().end(), download_list_call(*itr)); + rpc::commands.call_catch("event.download.inserted", rpc::make_target(*itr), torrent::Object(), "Download event action failed: "); } catch (torrent::local_error& e) { // Should perhaps relax this, just print an error and remove the @@ -199,7 +199,7 @@ DownloadList::erase(iterator itr) { control->core()->download_store()->remove(*itr); - std::for_each(slot_map_erase().begin(), slot_map_erase().end(), download_list_call(*itr)); + rpc::commands.call_catch("event.download.erased", rpc::make_target(*itr), torrent::Object(), "Download event action failed: "); std::for_each(control->view_manager()->begin(), control->view_manager()->end(), std::bind2nd(std::mem_fun(&View::erase), *itr)); torrent::download_remove(*(*itr)->download()); @@ -230,8 +230,7 @@ DownloadList::open_throw(Download* download) { return; download->download()->open(download->resume_flags()); - - std::for_each(slot_map_open().begin(), slot_map_open().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.opened", rpc::make_target(download), torrent::Object(), "Download event action failed: "); } void @@ -294,8 +293,8 @@ DownloadList::close_throw(Download* download) { if (!download->is_hash_failed() && rpc::call_command_value("d.get_hashing", rpc::make_target(download)) != Download::variable_hashing_stopped) throw torrent::internal_error("DownloadList::close_throw(...) called but we're going into a hashing loop."); - std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download)); - std::for_each(slot_map_close().begin(), slot_map_close().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.hash_removed", rpc::make_target(download), torrent::Object(), "Download event action failed: "); + rpc::commands.call_catch("event.download.closed", rpc::make_target(download), torrent::Object(), "Download event action failed: "); } void @@ -328,7 +327,7 @@ DownloadList::resume(Download* download, int flags) { if (rpc::call_command_value("d.get_hashing", rpc::make_target(download)) == Download::variable_hashing_stopped) rpc::call_command("d.set_hashing", Download::variable_hashing_initial, rpc::make_target(download)); - std::for_each(slot_map_hash_queued().begin(), slot_map_hash_queued().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.hash_queued", rpc::make_target(download), torrent::Object(), "Download event action failed: "); return; } @@ -361,7 +360,7 @@ DownloadList::resume(Download* download, int flags) { download->set_resume_flags(~uint32_t()); - std::for_each(slot_map_start().begin(), slot_map_start().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.resumed", rpc::make_target(download), torrent::Object(), "Download event action failed: "); } catch (torrent::local_error& e) { control->core()->push_log(e.what()); @@ -384,7 +383,7 @@ DownloadList::pause(Download* download, int flags) { download->download()->hash_stop(); rpc::call_command_set_value("d.set_hashing", Download::variable_hashing_stopped, rpc::make_target(download)); - std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.hash_removed", rpc::make_target(download), torrent::Object(), "Download event action failed: "); } if (!download->download()->is_active()) @@ -396,7 +395,7 @@ DownloadList::pause(Download* download, int flags) { // TODO: This is actually for pause, not stop... And doesn't get // called when the download isn't active, but was in the 'started' // view. - std::for_each(slot_map_stop().begin(), slot_map_stop().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.paused", rpc::make_target(download), torrent::Object(), "Download event action failed: "); rpc::call_command("d.set_state_changed", cachedTime.seconds(), rpc::make_target(download)); rpc::call_command("d.set_state_counter", rpc::call_command_value("d.get_state_counter", rpc::make_target(download)), rpc::make_target(download)); @@ -441,7 +440,7 @@ DownloadList::hash_done(Download* download) { if (!download->is_hash_checked()) { download->set_hash_failed(true); - std::for_each(slot_map_hash_done().begin(), slot_map_hash_done().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.hash_done", rpc::make_target(download), torrent::Object(), "Download event action failed: "); return; } @@ -497,7 +496,7 @@ DownloadList::hash_done(Download* download) { return; } - std::for_each(slot_map_hash_done().begin(), slot_map_hash_done().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.hash_done", rpc::make_target(download), torrent::Object(), "Download event action failed: "); } void @@ -512,8 +511,9 @@ DownloadList::hash_queue(Download* download, int type) { if (download->is_open()) { pause(download, torrent::Download::stop_skip_tracker); download->download()->close(); - std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download)); - std::for_each(slot_map_close().begin(), slot_map_close().end(), download_list_call(download)); + + rpc::commands.call_catch("event.download.hash_removed", rpc::make_target(download), torrent::Object(), "Download event action failed: "); + rpc::commands.call_catch("event.download.closed", rpc::make_target(download), torrent::Object(), "Download event action failed: "); } torrent::resume_clear_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume")); @@ -526,7 +526,7 @@ DownloadList::hash_queue(Download* download, int type) { // If any more stuff is added here, make sure resume etc are still // correct. - std::for_each(slot_map_hash_queued().begin(), slot_map_hash_queued().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.hash_queued", rpc::make_target(download), torrent::Object(), "Download event action failed: "); } void @@ -570,7 +570,7 @@ DownloadList::confirm_finished(Download* download) { // up/downloaded baseline. download->download()->tracker_list()->send_completed(); - std::for_each(slot_map_finished().begin(), slot_map_finished().end(), download_list_call(download)); + rpc::commands.call_catch("event.download.finished", rpc::make_target(download), torrent::Object(), "Download event action failed: "); if (download->resume_flags() != ~uint32_t()) throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != ~uint32_t()."); diff --git a/src/core/download_list.h b/src/core/download_list.h index 6c3de33f..d0ff3eea 100644 --- a/src/core/download_list.h +++ b/src/core/download_list.h @@ -60,7 +60,6 @@ class Download; class DownloadList : private std::list { public: typedef std::list base_type; - typedef std::map slot_map; using base_type::iterator; using base_type::const_iterator; @@ -115,40 +114,35 @@ public: void check_hash(Download* d); enum { - SLOTS_INSERT, - SLOTS_ERASE, - SLOTS_OPEN, - SLOTS_CLOSE, - SLOTS_START, - SLOTS_STOP, - SLOTS_HASH_QUEUED, - SLOTS_HASH_REMOVED, - SLOTS_HASH_DONE, - SLOTS_FINISHED, + D_SLOTS_INSERT, + D_SLOTS_ERASE, + D_SLOTS_OPEN, + D_SLOTS_CLOSE, + D_SLOTS_START, + D_SLOTS_STOP, + D_SLOTS_HASH_QUEUED, + D_SLOTS_HASH_REMOVED, + D_SLOTS_HASH_DONE, + D_SLOTS_FINISHED, SLOTS_MAX_SIZE }; - slot_map& slots(int m) { return m_slotMaps[m]; } - const slot_map& slots(int m) const { return m_slotMaps[m]; } - - slot_map* slot_map_begin() { return m_slotMaps; } - const slot_map* slot_map_begin() const { return m_slotMaps; } - slot_map* slot_map_end() { return m_slotMaps + SLOTS_MAX_SIZE; } - const slot_map* slot_map_end() const { return m_slotMaps + SLOTS_MAX_SIZE; } - - slot_map& slot_map_insert() { return m_slotMaps[SLOTS_INSERT]; } - const slot_map& slot_map_insert() const { return m_slotMaps[SLOTS_INSERT]; } - slot_map& slot_map_erase() { return m_slotMaps[SLOTS_ERASE]; } - const slot_map& slot_map_erase() const { return m_slotMaps[SLOTS_ERASE]; } - slot_map& slot_map_open() { return m_slotMaps[SLOTS_OPEN]; } - const slot_map& slot_map_open() const { return m_slotMaps[SLOTS_OPEN]; } - slot_map& slot_map_close() { return m_slotMaps[SLOTS_CLOSE]; } - const slot_map& slot_map_close() const { return m_slotMaps[SLOTS_CLOSE]; } - slot_map& slot_map_start() { return m_slotMaps[SLOTS_START]; } - const slot_map& slot_map_start() const { return m_slotMaps[SLOTS_START]; } - slot_map& slot_map_stop() { return m_slotMaps[SLOTS_STOP]; } - const slot_map& slot_map_stop() const { return m_slotMaps[SLOTS_STOP]; } + static const char* slot_name(int m) { + switch(m) { + case D_SLOTS_INSERT: return "event.download.inserted"; + case D_SLOTS_ERASE: return "event.download.erased"; + case D_SLOTS_OPEN: return "event.download.opened"; + case D_SLOTS_CLOSE: return "event.download.closed"; + case D_SLOTS_START: return "event.download.resumed"; + case D_SLOTS_STOP: return "event.download.paused"; + case D_SLOTS_HASH_QUEUED: return "event.download.hash_queued"; + case D_SLOTS_HASH_REMOVED: return "event.download.hash_removed"; + case D_SLOTS_HASH_DONE: return "event.download.hash_done"; + case D_SLOTS_FINISHED: return "event.download.finished"; + default: return "BORK"; + } + } // The finished slots will be called when an active download with // "finished" == 0 performs a hash check which returns a done @@ -158,28 +152,6 @@ public: // Also we need to handle cases when a hashing torrent starts up // after a shutdown. - slot_map& slot_map_hash_queued() { return m_slotMaps[SLOTS_HASH_QUEUED]; } - const slot_map& slot_map_hash_queued() const { return m_slotMaps[SLOTS_HASH_QUEUED]; } - slot_map& slot_map_hash_removed() { return m_slotMaps[SLOTS_HASH_REMOVED]; } - const slot_map& slot_map_hash_removed() const { return m_slotMaps[SLOTS_HASH_REMOVED]; } - slot_map& slot_map_hash_done() { return m_slotMaps[SLOTS_HASH_DONE]; } - const slot_map& slot_map_hash_done() const { return m_slotMaps[SLOTS_HASH_DONE]; } - slot_map& slot_map_finished() { return m_slotMaps[SLOTS_FINISHED]; } - const slot_map& slot_map_finished() const { return m_slotMaps[SLOTS_FINISHED]; } - - bool has_slot_insert(const std::string& key) const { return slot_map_insert().find(key) != slot_map_insert().end(); } - bool has_slot_erase(const std::string& key) const { return slot_map_erase().find(key) != slot_map_erase().end(); } - bool has_slot_open(const std::string& key) const { return slot_map_open().find(key) != slot_map_open().end(); } - bool has_slot_close(const std::string& key) const { return slot_map_close().find(key) != slot_map_close().end(); } - bool has_slot_start(const std::string& key) const { return slot_map_start().find(key) != slot_map_start().end(); } - bool has_slot_stop(const std::string& key) const { return slot_map_stop().find(key) != slot_map_stop().end(); } - - bool has_slot_hash_queued(const std::string& key) const{ return slot_map_hash_queued().find(key) != slot_map_hash_queued().end(); } - bool has_slot_hash_done(const std::string& key) const { return slot_map_hash_done().find(key) != slot_map_hash_done().end(); } - bool has_slot_finished(const std::string& key) const { return slot_map_finished().find(key) != slot_map_finished().end(); } - - static void erase_key(slot_map& sm, const std::string& key) { sm.erase(key); } - private: DownloadList(const DownloadList&); void operator = (const DownloadList&); @@ -191,8 +163,6 @@ private: void received_finished(Download* d); void confirm_finished(Download* d); - - slot_map m_slotMaps[SLOTS_MAX_SIZE]; }; } diff --git a/src/core/manager.cc b/src/core/manager.cc index 2fba8ad8..06001b3d 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -213,8 +213,8 @@ Manager::initialize_second() { // Register slots to be called when a download is inserted/erased, // opened or closed. - m_downloadList->slot_map_insert()["1_connect_logs"] = "d.initialize_logs="; - m_downloadList->slot_map_erase()["9_delete_tied"] = "d.delete_tied="; + rpc::parse_command_single(rpc::make_target(), "system.method.set_key = event.download.inserted, 1_connect_logs, d.initialize_logs="); + rpc::parse_command_single(rpc::make_target(), "system.method.set_key = event.download.erased, 9_delete_tied, d.delete_tied="); torrent::connection_manager()->set_signal_handshake_log(sigc::mem_fun(this, &Manager::handshake_log)); } diff --git a/src/core/view.cc b/src/core/view.cc index 7227d575..fc9693a9 100644 --- a/src/core/view.cc +++ b/src/core/view.cc @@ -116,9 +116,10 @@ View::~View() { if (m_name.empty()) return; - std::for_each(control->core()->download_list()->slot_map_begin(), control->core()->download_list()->slot_map_end(), - rak::bind2nd(std::ptr_fun(&DownloadList::erase_key), "0_view_" + m_name)); - + for (int i = 0; i < DownloadList::SLOTS_MAX_SIZE; i++) + rpc::commands.call("system.method.set_key", rpc::make_target(), + rpc::create_object_list(control->core()->download_list()->slot_name(i), "0_view_" + m_name)); + priority_queue_erase(&taskScheduler, &m_delayChanged); } @@ -133,7 +134,8 @@ View::initialize(const std::string& name) { std::string key = "0_view_" + name; core::DownloadList* dlist = control->core()->download_list(); - if (dlist->has_slot_insert(key) || dlist->has_slot_erase(key)) + if (rpc::commands.call("system.method.has_key", rpc::make_target(), rpc::create_object_list("event.download.inserted", key)).as_value() || + rpc::commands.call("system.method.has_key", rpc::make_target(), rpc::create_object_list("event.download.erased", key)).as_value()) throw torrent::internal_error("View::initialize(...) duplicate key name found in DownloadList."); m_name = name; @@ -296,18 +298,20 @@ View::filter_download(core::Download* download) { void View::set_filter_on(int event) { - if (event == DownloadList::SLOTS_INSERT || event == DownloadList::SLOTS_ERASE || event >= DownloadList::SLOTS_MAX_SIZE) + if (event == DownloadList::D_SLOTS_INSERT || event == DownloadList::D_SLOTS_ERASE || event >= DownloadList::SLOTS_MAX_SIZE) throw torrent::internal_error("View::filter_on(...) invalid event."); - control->core()->download_list()->slots(event)["0_view_" + m_name] = "view.filter_download=" + m_name; + rpc::commands.call("system.method.set_key", rpc::make_target(), + rpc::create_object_list(control->core()->download_list()->slot_name(event), "0_view_" + m_name, "view.filter_download=" + m_name)); } void View::clear_filter_on() { // Don't clear insert and erase as these are required to keep the // View up-to-date with the available downloads. - std::for_each(control->core()->download_list()->slot_map_begin() + DownloadList::SLOTS_OPEN, control->core()->download_list()->slot_map_end(), - rak::bind2nd(std::ptr_fun(&DownloadList::erase_key), "0_view_" + m_name)); + for (int i = DownloadList::D_SLOTS_OPEN; i < DownloadList::SLOTS_MAX_SIZE; i++) + rpc::commands.call("system.method.set_key", rpc::make_target(), + rpc::create_object_list(control->core()->download_list()->slot_name(i), "0_view_" + m_name)); } inline void diff --git a/src/core/view_manager.cc b/src/core/view_manager.cc index 23a698a5..92f3c06d 100644 --- a/src/core/view_manager.cc +++ b/src/core/view_manager.cc @@ -115,22 +115,22 @@ ViewManager::set_filter_on(const std::string& name, const filter_args& args) { for (filter_args::const_iterator itr = args.begin(); itr != args.end(); ++itr) { if (*itr == "start") - (*viewItr)->set_filter_on(DownloadList::SLOTS_START); + (*viewItr)->set_filter_on(DownloadList::D_SLOTS_START); else if (*itr == "stop") - (*viewItr)->set_filter_on(DownloadList::SLOTS_STOP); + (*viewItr)->set_filter_on(DownloadList::D_SLOTS_STOP); else if (*itr == "hash_queued") - (*viewItr)->set_filter_on(DownloadList::SLOTS_HASH_QUEUED); + (*viewItr)->set_filter_on(DownloadList::D_SLOTS_HASH_QUEUED); else if (*itr == "hash_removed") - (*viewItr)->set_filter_on(DownloadList::SLOTS_HASH_REMOVED); + (*viewItr)->set_filter_on(DownloadList::D_SLOTS_HASH_REMOVED); else if (*itr == "hash_done") - (*viewItr)->set_filter_on(DownloadList::SLOTS_HASH_DONE); + (*viewItr)->set_filter_on(DownloadList::D_SLOTS_HASH_DONE); else if (*itr == "finished") - (*viewItr)->set_filter_on(DownloadList::SLOTS_FINISHED); + (*viewItr)->set_filter_on(DownloadList::D_SLOTS_FINISHED); else throw torrent::input_error("Invalid filter on identifier."); diff --git a/src/main.cc b/src/main.cc index a740cfc9..42c549e9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -171,8 +171,20 @@ main(int argc, char** argv) { // torrent::ConnectionManager* are valid etc. initialize_commands(); - rpc::parse_command_multiple(rpc::make_target(), -// "set_name = $cat={$system.hostname=,:,$system.pid=}\n" + rpc::parse_command_multiple + (rpc::make_target(), + "system.method.insert = event.download.inserted,list\n" + "system.method.insert = event.download.erased,list\n" + "system.method.insert = event.download.opened,list\n" + "system.method.insert = event.download.closed,list\n" + "system.method.insert = event.download.resumed,list\n" + "system.method.insert = event.download.paused,list\n" + + "system.method.insert = event.download.finished,list\n" + "system.method.insert = event.download.hash_done,list\n" + "system.method.insert = event.download.hash_removed,list\n" + "system.method.insert = event.download.hash_queued,list\n" + "set_name = \"$cat=$system.hostname=,:,$system.pid=\"\n" // Currently not doing any sorting on main. diff --git a/src/rpc/command_function.cc b/src/rpc/command_function.cc index 27188936..0af63519 100644 --- a/src/rpc/command_function.cc +++ b/src/rpc/command_function.cc @@ -36,6 +36,10 @@ #include "config.h" +#include +#include +#include + #include "parse.h" #include "parse_commands.h" #include "command_function.h" @@ -51,4 +55,42 @@ CommandFunction::call(Command* rawCommand, target_type target, const torrent::Ob return torrent::Object(); } +const torrent::Object +CommandFunctionList::call(Command* rawCommand, target_type target, const torrent::Object& args) { + CommandFunctionList* command = reinterpret_cast(rawCommand); + + for (base_type::const_iterator itr = command->begin(), last = command->end(); itr != last; itr++) + parse_command_multiple(target, itr->second.c_str(), itr->second.c_str() + itr->second.size()); + + return torrent::Object(); +} + +CommandFunctionList::const_iterator +CommandFunctionList::find(const char* key) { + base_type::iterator itr = std::find_if(begin(), end(), rak::greater_equal(key, rak::mem_ref(&base_type::value_type::first))); + + if (itr == end() || itr->first != key) + return end(); + else + return itr; +} + +void +CommandFunctionList::insert(const std::string& key, const std::string& cmd) { + base_type::iterator itr = std::find_if(begin(), end(), rak::greater_equal(key, rak::mem_ref(&base_type::value_type::first))); + + if (itr != end() && itr->first == key) + itr->second = cmd; + else + base_type::insert(itr, base_type::value_type(key, cmd)); +} + +void +CommandFunctionList::erase(const std::string& key) { + base_type::iterator itr = std::find_if(begin(), end(), rak::greater_equal(key, rak::mem_ref(&base_type::value_type::first))); + + if (itr != end() && itr->first == key) + base_type::erase(itr); +} + } diff --git a/src/rpc/command_function.h b/src/rpc/command_function.h index 6a740a9e..bbf37387 100644 --- a/src/rpc/command_function.h +++ b/src/rpc/command_function.h @@ -38,6 +38,7 @@ #define RTORRENT_RPC_COMMAND_FUNCTION_H #include +#include #include #include #include @@ -50,14 +51,35 @@ class CommandFunction : public Command { public: CommandFunction(const std::string& cmd = std::string()) : m_command(cmd) {} - const std::string& command() const { return m_command; } - void set_command(const std::string& cmd) { m_command = cmd; } + const std::string& command() const { return m_command; } + void set_command(const std::string& cmd) { m_command = cmd; } static const torrent::Object call(Command* rawCommand, target_type target, const torrent::Object& args); private: // TODO: Replace with a delete-me flag and const char*. - std::string m_command; + std::string m_command; +}; + +class CommandFunctionList : public Command, + private std::vector > { +public: + typedef std::vector > base_type; + + using Command::value_type; + using base_type::iterator; + using base_type::const_iterator; + using base_type::begin; + using base_type::end; + + CommandFunctionList() {} + + const_iterator find(const char* key); + + void insert(const std::string& key, const std::string& cmd); + void erase(const std::string& key); + + static const torrent::Object call(Command* rawCommand, target_type target, const torrent::Object& args); }; } diff --git a/src/rpc/command_map.cc b/src/rpc/command_map.cc index c25c539a..c0c2c076 100644 --- a/src/rpc/command_map.cc +++ b/src/rpc/command_map.cc @@ -41,6 +41,11 @@ #include #include +// Get better logging... +#include "globals.h" +#include "control.h" +#include "core/manager.h" + #include "command.h" #include "command_map.h" @@ -99,6 +104,16 @@ CommandMap::erase(iterator itr) { delete [] key; } +const CommandMap::mapped_type +CommandMap::call_catch(key_type key, target_type target, const mapped_type& args, const char* err) { + try { + return call_command(key, args, target); + } catch (torrent::input_error& e) { + control->core()->push_log((err + std::string(e.what())).c_str()); + return torrent::Object(); + } +} + const CommandMap::mapped_type CommandMap::call_command(key_type key, const mapped_type& arg, target_type target) { const_iterator itr = base_type::find(key); diff --git a/src/rpc/command_map.h b/src/rpc/command_map.h index 9b443589..b13b1523 100644 --- a/src/rpc/command_map.h +++ b/src/rpc/command_map.h @@ -62,7 +62,7 @@ struct command_map_data_type { int target() const { return m_target; } - Command* m_variable; + Command* m_variable; union { Command::cleaned_slot m_genericSlot; @@ -124,6 +124,9 @@ public: void insert(key_type key, const command_map_data_type src); void erase(iterator itr); + const mapped_type call(key_type key, target_type target, const mapped_type& args = mapped_type()) { return call_command(key, args, target); } + const mapped_type call_catch(key_type key, target_type target, const mapped_type& args = mapped_type(), const char* err = "Command failed: "); + const mapped_type call_command (key_type key, const mapped_type& arg, target_type target = target_type((int)Command::target_generic, NULL)); const mapped_type call_command (const_iterator itr, const mapped_type& arg, target_type target = target_type((int)Command::target_generic, NULL)); @@ -151,6 +154,26 @@ inline target_type make_target_pair(T target1, T target2) { return target_type((int)target_type_id::value, target1, target2); } +// TODO: Helper-functions that really should be in the +// torrent/object.h header. + +inline torrent::Object +create_object_list(const torrent::Object& o1, const torrent::Object& o2) { + torrent::Object tmp = torrent::Object::create_list(); + tmp.as_list().push_back(o1); + tmp.as_list().push_back(o2); + return tmp; +} + +inline torrent::Object +create_object_list(const torrent::Object& o1, const torrent::Object& o2, const torrent::Object& o3) { + torrent::Object tmp = torrent::Object::create_list(); + tmp.as_list().push_back(o1); + tmp.as_list().push_back(o2); + tmp.as_list().push_back(o3); + return tmp; +} + } #endif diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 29140e92..8685a86a 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -98,7 +98,7 @@ DownloadList::activate(display::Frame* frame, bool focus) { m_frame = frame; control->input()->push_back(&m_bindings); - control->core()->download_list()->slot_map_erase()["0_download_list"] = "ui.unfocus_download="; + rpc::commands.call("system.method.set_key", rpc::make_target(), rpc::create_object_list("event.download.erased", "0_download_list", "ui.unfocus_download=")); activate_display(DISPLAY_DOWNLOAD_LIST); }