diff --git a/src/command_dynamic.cc b/src/command_dynamic.cc index 2dfa5972..b8d25f1b 100644 --- a/src/command_dynamic.cc +++ b/src/command_dynamic.cc @@ -57,6 +57,55 @@ system_method_generate_command(torrent::Object::list_const_iterator first, torre return command; } +void +system_method_generate_command2(torrent::Object* object, torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last) { + if (first == last) { + // TODO: Use empty object. + *object = ""; + return; + } + + if (first->is_string()) { + std::string command; + + while (first != last) { + if (!command.empty()) + command += " ;"; + + command += (first++)->as_string(); + } + + *object = command; + + return; + } + + if (first + 1 == last) { + if (!first->is_dict_key()) + throw torrent::input_error("New command of wrong type."); + + *object = *first; + + uint32_t flags = object->flags() & torrent::Object::mask_function; + object->unset_flags(torrent::Object::mask_function); + object->set_flags((flags >> 1) & torrent::Object::mask_function); + + } else { + *object = torrent::Object::create_list(); + + while (first != last) { + if (!first->is_dict_key()) + throw torrent::input_error("New command of wrong type."); + + object->as_list().push_back(*first++); + + uint32_t flags = object->as_list().back().flags() & torrent::Object::mask_function; + object->as_list().back().unset_flags(torrent::Object::mask_function); + object->as_list().back().set_flags((flags >> 1) & torrent::Object::mask_function); + } + } +} + // torrent::Object // system_method_insert_function(const torrent::Object::list_type& args, int flags) { @@ -86,7 +135,7 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) { value = itrArgs != args.end() ? rpc::convert_to_string(*itrArgs) : ""; break; case rpc::object_storage::flag_function_type: - value = itrArgs != args.end() ? system_method_generate_command(itrArgs, args.end()) : ""; + system_method_generate_command2(&value, itrArgs, args.end()); break; case rpc::object_storage::flag_multi_type: break; diff --git a/src/command_events.cc b/src/command_events.cc index 5b90aacd..5a9a967e 100644 --- a/src/command_events.cc +++ b/src/command_events.cc @@ -186,9 +186,8 @@ apply_schedule(const torrent::Object::list_type& args) { const std::string& arg1 = (itr++)->as_string(); const std::string& arg2 = (itr++)->as_string(); const std::string& arg3 = (itr++)->as_string(); - const std::string& arg4 = (itr++)->as_string(); - control->command_scheduler()->parse(arg1, arg2, arg3, arg4); + control->command_scheduler()->parse(arg1, arg2, arg3, *itr); return torrent::Object(); } diff --git a/src/main.cc b/src/main.cc index 7b1ce6c7..49d01fb1 100644 --- a/src/main.cc +++ b/src/main.cc @@ -212,9 +212,10 @@ main(int argc, char** argv) { // "method.insert = test.value2,value,6\n" // "method.insert = test.string,string,6\n" -// "method.insert = test.bool,bool,true\n" +// "method.insert = test.bool,bool,true\n" - "method.insert = test.method.simple,simple,\"print=simple_test_,$argument.0=\"\n" + // "method.insert.simple = test.method.simple,((print,simple_test_,$argument.0=))\n" + // "method.insert.simple = test.method.double,((print,simple_test_,$argument.0=)),\"print=simple_test_,$argument.1=\"\n" "method.insert = event.download.inserted,multi\n" "method.insert = event.download.inserted_new,multi\n" @@ -239,8 +240,8 @@ main(int argc, char** argv) { "method.set_key = event.download.erased, !_download_list, ui.unfocus_download=\n" "method.set_key = event.download.erased, ~_delete_tied, d.delete_tied=\n" - "method.insert = group.insert_persistent_view,simple|const," - "view.add=$argument.0=,view.persistent=$argument.0=,\"group.insert=$argument.0=,$argument.0=\"\n" + "method.insert.c_simple = group.insert_persistent_view," + "((view.add,((argument.0)))),((view.persistent,((argument.0)))),((group.insert,((argument.0)),((argument.0))))\n" // Allow setting 'group2.view' as constant, so that we can't // modify the value. And look into the possibility of making @@ -302,12 +303,12 @@ main(int argc, char** argv) { "view.sort_new = seeding,less=d.state_changed=\n" "view.sort_current = seeding,less=d.state_changed=\n" - "schedule2 = view.main,10,10,\"view.sort=main,20\"\n" - "schedule2 = view.name,10,10,\"view.sort=name,20\"\n" + "schedule2 = view.main,10,10,((view.sort,main,20))\n" + "schedule2 = view.name,10,10,((view.sort,name,20))\n" - "schedule2 = session_save,1200,1200,session.save=\n" - "schedule2 = low_diskspace,5,60,close_low_diskspace=500M\n" - "schedule2 = prune_file_status,3600,86400,system.file_status_cache.prune=\n" + "schedule2 = session_save,1200,1200,((session.save))\n" + "schedule2 = low_diskspace,5,60,((close_low_diskspace,500M))\n" + "schedule2 = prune_file_status,3600,86400,((system.file_status_cache.prune))\n" "protocol.encryption.set=allow_incoming,prefer_plaintext,enable_retry\n" ); diff --git a/src/rpc/command_scheduler.cc b/src/rpc/command_scheduler.cc index 52317a56..9010ffce 100644 --- a/src/rpc/command_scheduler.cc +++ b/src/rpc/command_scheduler.cc @@ -97,7 +97,22 @@ CommandScheduler::call_item(value_type item) { // removed. try { - rpc::parse_command_multiple_std(item->command()); + if (item->command().is_string()) { + rpc::parse_command_multiple_std(item->command().as_string()); + + } else if (item->command().is_dict_key()) { + // This can/should be optimized... + torrent::Object tmp_command = item->command(); + + // Unquote the root function object so 'parse_command_execute' + // doesn't end up calling it. + uint32_t flags = tmp_command.flags() & torrent::Object::mask_function; + tmp_command.unset_flags(torrent::Object::mask_function); + tmp_command.set_flags((flags >> 1) & torrent::Object::mask_function); + + rpc::parse_command_execute(rpc::make_target(), &tmp_command); + rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj()); + } } catch (torrent::input_error& e) { if (m_slotErrorMessage.is_valid()) @@ -119,13 +134,19 @@ CommandScheduler::call_item(value_type item) { } void -CommandScheduler::parse(const std::string& key, const std::string& bufAbsolute, const std::string& bufInterval, const std::string& command) { +CommandScheduler::parse(const std::string& key, + const std::string& bufAbsolute, + const std::string& bufInterval, + const torrent::Object& command) { + if (!command.is_string() && !command.is_dict_key()) + throw torrent::bencode_error("Invalid type passed to command scheduler."); + uint32_t absolute = parse_absolute(bufAbsolute.c_str()); uint32_t interval = parse_interval(bufInterval.c_str()); CommandSchedulerItem* item = *insert(key); - item->set_command(command); + item->command() = command; item->set_interval(interval); item->enable((cachedTime + rak::timer::from_seconds(absolute)).round_seconds()); diff --git a/src/rpc/command_scheduler.h b/src/rpc/command_scheduler.h index 6a5d816b..3a3bd893 100644 --- a/src/rpc/command_scheduler.h +++ b/src/rpc/command_scheduler.h @@ -42,6 +42,10 @@ #include #include +namespace torrent { +class Object; +} + namespace rpc { class CommandSchedulerItem; @@ -71,7 +75,8 @@ public: void erase(iterator itr); void erase_str(const std::string& key) { erase(find(key)); } - void parse(const std::string& key, const std::string& bufAbsolute, const std::string& bufInterval, const std::string& command); + void parse(const std::string& key, const std::string& bufAbsolute, + const std::string& bufInterval, const torrent::Object& command); static uint32_t parse_absolute(const char* str); static uint32_t parse_interval(const char* str); diff --git a/src/rpc/command_scheduler_item.h b/src/rpc/command_scheduler_item.h index 20925fbd..8c8c7431 100644 --- a/src/rpc/command_scheduler_item.h +++ b/src/rpc/command_scheduler_item.h @@ -39,6 +39,8 @@ #include "globals.h" +#include + namespace rpc { class CommandSchedulerItem { @@ -54,9 +56,7 @@ public: void disable(); const std::string& key() const { return m_key; } - - const std::string& command() const { return m_command; } - void set_command(const std::string& s) { m_command = s; } + torrent::Object& command() { return m_command; } // 'interval()' should in the future return some more dynamic values. uint32_t interval() const { return m_interval; } @@ -72,7 +72,7 @@ private: void operator = (const CommandSchedulerItem&); std::string m_key; - std::string m_command; + torrent::Object m_command; uint32_t m_interval; rak::timer m_timeScheduled; diff --git a/src/rpc/object_storage.cc b/src/rpc/object_storage.cc index 38cecd51..5d3c85bc 100644 --- a/src/rpc/object_storage.cc +++ b/src/rpc/object_storage.cc @@ -62,13 +62,14 @@ object_storage::insert(const char* key_data, uint32_t key_size, const torrent::O // Check for size > key_size. // Check for empty string. + bool use_raw = false; torrent::Object object; switch (flags & mask_type) { case flag_bool_type: object = !!convert_to_value(rawObject); break; case flag_value_type: object = convert_to_value(rawObject); break; case flag_string_type: object = convert_to_string(rawObject); break; - case flag_function_type: object = convert_to_string(rawObject); break; + case flag_function_type: use_raw = true; break; case flag_multi_type: object = torrent::Object::create_map(); break; } @@ -81,7 +82,7 @@ object_storage::insert(const char* key_data, uint32_t key_size, const torrent::O throw torrent::input_error("Key already exists in object_storage."); result.first->second.flags = flags; - result.first->second.object = object; + result.first->second.object = use_raw ? rawObject : object; return result.first; } @@ -147,7 +148,7 @@ object_storage::call_function(const torrent::raw_string& key, target_type target switch (itr->second.flags & mask_type) { case flag_function_type: - return command_function_call_str(itr->second.object.as_string(), target, object); + return command_function_call_object(itr->second.object, target, object); case flag_multi_type: return command_function_multi_call(itr->second.object.as_map(), target, object); default: diff --git a/src/rpc/parse.cc b/src/rpc/parse.cc index 0ae2b25e..5453f043 100644 --- a/src/rpc/parse.cc +++ b/src/rpc/parse.cc @@ -181,6 +181,43 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (* return ++first; + } else if (*first == '(') { + int32_t depth = 1; + + while (first + 1 != last && *(first + 1) == '(') { + first++; + depth++; + } + + if (depth > 3) + throw torrent::input_error("Max 3 parantheses per object allowed."); + + *dest = torrent::Object::create_dict_key(); + dest->set_flags(torrent::Object::flag_function << (depth - 1)); + + first = parse_string(first + 1, last, &dest->as_dict_key(), &parse_is_delim_func); + first = parse_skip_wspace(first, last); + + if (first == last || !parse_is_delim_func(*first)) + throw torrent::input_error("Could not find closing ')'."); + + if (*first == ',') { + // This will always create a list even for single argument functions... + dest->as_dict_obj() = torrent::Object::create_list(); + first = parse_list(first + 1, last, &dest->as_dict_obj(), &parse_is_delim_func); + first = parse_skip_wspace(first, last); + } + + while (depth != 0 && first != last && *first == ')') { + first++; + depth--; + } + + if (depth != 0) + throw torrent::input_error("Parantheses mismatch."); + + return first; + } else { *dest = std::string(); diff --git a/src/rpc/parse.h b/src/rpc/parse.h index ed1077ad..1ccf8f9c 100644 --- a/src/rpc/parse.h +++ b/src/rpc/parse.h @@ -64,6 +64,7 @@ inline bool parse_is_delim_list(const char c) { return parse_is_seperator(c) inline bool parse_is_delim_command(const char c) { return parse_is_seperator(c) || c == ';' || std::isspace(c); } // inline bool parse_is_delim_block(const char c) { return c == ';' || c == '}'; } inline bool parse_is_delim_block(const char c) { return parse_is_seperator(c) || c == '}'; } +inline bool parse_is_delim_func(const char c) { return parse_is_seperator(c) || c == ')'; } const char* parse_skip_wspace(const char* first); const char* parse_skip_wspace(const char* first, const char* last); diff --git a/src/rpc/parse_commands.cc b/src/rpc/parse_commands.cc index da253e4f..f957edec 100644 --- a/src/rpc/parse_commands.cc +++ b/src/rpc/parse_commands.cc @@ -93,7 +93,19 @@ parse_command_execute(target_type target, torrent::Object* object) { parse_command_execute(target, &*itr); } - } else if (*object->as_string().c_str() == '$') { + } else if (object->is_dict_key()) { + parse_command_execute(target, &object->as_dict_obj()); + + if (object->flags() & torrent::Object::flag_function) { + *object = rpc::commands.call_command(object->as_dict_key().c_str(), object->as_dict_obj(), target); + + } else { + uint32_t flags = object->flags() & torrent::Object::mask_function; + object->unset_flags(torrent::Object::mask_function); + object->set_flags((flags >> 1) & torrent::Object::mask_function); + } + + } else if (object->is_string() && *object->as_string().c_str() == '$') { const std::string& str = object->as_string(); *object = parse_command(target, str.c_str() + 1, str.c_str() + str.size()).first; @@ -252,6 +264,48 @@ command_function_call(const torrent::raw_string& cmd, target_type target, const } } +const torrent::Object +command_function_call_object(const torrent::Object& cmd, target_type target, const torrent::Object& args) { + rpc::command_base::stack_type stack; + torrent::Object* last_stack; + + if (args.is_list()) + last_stack = rpc::command_base::push_stack(args.as_list(), &stack); + else if (args.type() != torrent::Object::TYPE_NONE) + last_stack = rpc::command_base::push_stack(&args, &args + 1, &stack); + else + last_stack = rpc::command_base::push_stack(NULL, NULL, &stack); + + try { + torrent::Object result; + + if (cmd.is_string()) { + result = parse_command_multiple(target, cmd.as_string().c_str(), cmd.as_string().c_str() + cmd.as_string().size()); + + } else if (cmd.is_list()){ + for (torrent::Object::list_const_iterator first = cmd.as_list().begin(), last = cmd.as_list().end(); first != last; first++) { + torrent::Object tmp_cmd = *first; + + rpc::parse_command_execute(target, &tmp_cmd); + result = rpc::commands.call_command(tmp_cmd.as_dict_key().c_str(), tmp_cmd.as_dict_obj()); + } + + } else { + torrent::Object tmp_cmd = cmd; + + rpc::parse_command_execute(target, &tmp_cmd); + result = rpc::commands.call_command(tmp_cmd.as_dict_key().c_str(), tmp_cmd.as_dict_obj()); + } + + rpc::command_base::pop_stack(&stack, last_stack); + return result; + + } catch (torrent::bencode_error& e) { + rpc::command_base::pop_stack(&stack, last_stack); + throw e; + } +} + const torrent::Object command_function_multi_call(const torrent::Object::map_type& cmd, target_type target, const torrent::Object& args) { rpc::command_base::stack_type stack; diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h index 0faac265..8fc162d0 100644 --- a/src/rpc/parse_commands.h +++ b/src/rpc/parse_commands.h @@ -62,6 +62,8 @@ typedef std::pair parse_command_type; parse_command_type parse_command(target_type target, const char* first, const char* last); torrent::Object parse_command_multiple(target_type target, const char* first, const char* last); +void parse_command_execute(target_type target, torrent::Object* object); + // Make this take care of lists too. parse_command_type parse_command_object(target_type target, const torrent::Object& object); @@ -130,6 +132,8 @@ call_command_d_range(const char* key, core::Download* download, torrent::Object: const torrent::Object command_function_call(const torrent::raw_string& cmd, target_type target, const torrent::Object& args); const torrent::Object +command_function_call_object(const torrent::Object& cmd, target_type target, const torrent::Object& args); +const torrent::Object command_function_multi_call(const torrent::Object::map_type& cmd, target_type target, const torrent::Object& args); inline const torrent::Object diff --git a/src/thread_worker.cc b/src/thread_worker.cc index 204b6ddc..7e9b972d 100644 --- a/src/thread_worker.cc +++ b/src/thread_worker.cc @@ -56,6 +56,8 @@ ThreadWorker::ThreadWorker() { } ThreadWorker::~ThreadWorker() { + if (m_safe.scgi) + m_safe.scgi->deactivate(); } void