487 lines · 19.8 KB · C++
RawDownload
#include "config.h" #include <algorithm>#include <torrent/utils/log.h>#include <torrent/utils/option_strings.h> #include "command_helpers.h"#include "control.h"#include "globals.h"#include "rpc/parse.h"#include "rpc/parse_options.h" static std::vector<std::pair<const char*, int>> object_storage_flags = {  {"multi", rpc::object_storage::flag_multi_type},  {"simple", rpc::object_storage::flag_function_type},  {"value", rpc::object_storage::flag_value_type},  {"bool", rpc::object_storage::flag_bool_type},  {"string", rpc::object_storage::flag_string_type},  {"list", rpc::object_storage::flag_list_type},   {"static", rpc::object_storage::flag_static},  {"private", rpc::object_storage::flag_private},  {"const", rpc::object_storage::flag_constant},  {"rlookup", rpc::object_storage::flag_rlookup}}; static intobject_storage_parse_flag(const std::string& flag) {  for (auto [n, f] : object_storage_flags)    if (n == flag)      return f;   throw torrent::input_error("unknown flag");} std::stringsystem_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;} voidsystem_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) { // } // This is only used by tinyxml2, xmlrpc-c intercepts the call internallytorrent::Objectsystem_listMethods() {  torrent::Object resultRaw = torrent::Object::create_list();  torrent::Object::list_type& result = resultRaw.as_list();  result.push_back("system.multicall"); // Handled directly by the XMLRPC code  for (auto itr : rpc::commands) {    result.push_back(itr.first);  }  return resultRaw;} torrent::Objectsystem_method_insert_object(const torrent::Object::list_type& args, int flags) {  if (args.empty())    throw torrent::input_error("Invalid argument count.");   torrent::Object::list_const_iterator itrArgs = args.begin();  const std::string&                   raw_key = (itrArgs++)->as_string();   if (raw_key.empty() ||      control->object_storage()->find_raw_string(torrent::raw_string::from_string(raw_key)) != control->object_storage()->end() ||      rpc::commands.has(raw_key) || rpc::commands.has(raw_key + ".set"))    throw torrent::input_error("Invalid key.");   torrent::Object value;   switch (flags & rpc::object_storage::mask_type) {  case rpc::object_storage::flag_bool_type:  case rpc::object_storage::flag_value_type:    value = itrArgs != args.end() ? rpc::convert_to_value(*itrArgs) : int64_t();    break;  case rpc::object_storage::flag_string_type:    value = itrArgs != args.end() ? rpc::convert_to_string(*itrArgs) : "";    break;  case rpc::object_storage::flag_function_type:    system_method_generate_command2(&value, itrArgs, args.end());    break;  case rpc::object_storage::flag_list_type:    break;  case rpc::object_storage::flag_multi_type:    break;  default:    throw torrent::input_error("Invalid type.");  }   int cmd_flags = 0;   if (!(flags & rpc::object_storage::flag_static))    cmd_flags |= rpc::CommandMap::flag_modifiable;  if (!(flags & rpc::object_storage::flag_private))    cmd_flags |= rpc::CommandMap::flag_public_rpc;   if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_list_type) {    torrent::Object             valueList     = torrent::Object::create_list();    torrent::Object::list_type& valueListType = valueList.as_list();     if (itrArgs != args.end() && (itrArgs)->is_list())      valueListType = (itrArgs)->as_list();     control->object_storage()->insert_str(raw_key, valueList, flags);  } else {    control->object_storage()->insert_str(raw_key, value, flags);  }   if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_function_type ||      (flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_multi_type) {     rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call<rpc::target_type>>::type>(      raw_key,      std::bind(&rpc::object_storage::call_function_str, control->object_storage(), raw_key, std::placeholders::_1, std::placeholders::_2),      &rpc::command_base_call<rpc::target_type>,      cmd_flags,      NULL,      NULL);   } else {    rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call<rpc::target_type>>::type>(      raw_key,      std::bind(&rpc::object_storage::get_str, control->object_storage(), raw_key),      &rpc::command_base_call<rpc::target_type>,      cmd_flags,      NULL,      NULL);  }   // Not the right argument.  // if (flags & rpc::object_storage::flag_rlookup) {  //   rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_string<rpc::target_type> >::type>  //     (create_new_key<9>(raw_key, ".rlookup"),  //      std::bind(&rpc::object_storage::rlookup_obj_list, control->object_storage(), raw_key),  //      &rpc::command_base_call_string<rpc::target_type>,  //      cmd_flags, NULL, NULL);  // }   // TODO: Next... Make test class for this.   //   // Ehm... no proper handling if these throw.   if (!(flags & rpc::object_storage::flag_constant)) {    switch (flags & rpc::object_storage::mask_type) {    case rpc::object_storage::flag_bool_type:      rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_value<rpc::target_type>>::type>(        raw_key + ".set",        std::bind(&rpc::object_storage::set_str_bool, control->object_storage(), raw_key, std::placeholders::_2),        &rpc::command_base_call_value<rpc::target_type>,        cmd_flags,        NULL,        NULL);      break;    case rpc::object_storage::flag_value_type:      rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_value<rpc::target_type>>::type>(        raw_key + ".set",        std::bind(&rpc::object_storage::set_str_value, control->object_storage(), raw_key, std::placeholders::_2),        &rpc::command_base_call_value<rpc::target_type>,        cmd_flags,        NULL,        NULL);      break;    case rpc::object_storage::flag_string_type:      rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_string<rpc::target_type>>::type>(        raw_key + ".set",        std::bind(&rpc::object_storage::set_str_string, control->object_storage(), raw_key, std::placeholders::_2),        &rpc::command_base_call_string<rpc::target_type>,        cmd_flags,        NULL,        NULL);      break;    case rpc::object_storage::flag_list_type:      rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_list<rpc::target_type>>::type>(        raw_key + ".set",        std::bind(&rpc::object_storage::set_str_list, control->object_storage(), raw_key, std::placeholders::_2),        &rpc::command_base_call_list<rpc::target_type>,        cmd_flags,        NULL,        NULL);      break;    case rpc::object_storage::flag_function_type:    case rpc::object_storage::flag_multi_type:    default:      break;    }  }   return torrent::Object();} // method.insert <generic> {name, "simple|private|const", ...}// method.insert <generic> {name, "multi|private|const"}// method.insert <generic> {name, "value|private|const"}// method.insert <generic> {name, "value|private|const", value}// method.insert <generic> {name, "bool|private|const"}// method.insert <generic> {name, "bool|private|const", bool}// method.insert <generic> {name, "string|private|const"}// method.insert <generic> {name, "string|private|const", string}//// Add a new user-defined method called 'name' and any number of// lines.//// TODO: Make a static version of this that doesn't need to be called// as a command, and which takes advantage of static const char// strings, etc.torrent::Objectsystem_method_insert(const torrent::Object::list_type& args) {  if (args.empty() || ++args.begin() == args.end())    throw torrent::input_error("Invalid argument count.");   torrent::Object::list_const_iterator itrArgs = args.begin();  const std::string&                   raw_key  = (itrArgs++)->as_string();   if (raw_key.empty() || rpc::commands.has(raw_key))    throw torrent::input_error("Invalid key.");   int new_flags = rpc::parse_option_flags(itrArgs->as_string(), std::bind(&object_storage_parse_flag, std::placeholders::_1));   torrent::Object::list_type new_args;  new_args.push_back(raw_key);   if ((new_flags & rpc::object_storage::flag_function_type) ||      (new_flags & rpc::object_storage::flag_multi_type)) {    new_args.push_back(system_method_generate_command(++itrArgs, args.end()));   } else if ((new_flags & rpc::object_storage::flag_value_type) ||             (new_flags & rpc::object_storage::flag_bool_type) ||             (new_flags & rpc::object_storage::flag_string_type) ||             (new_flags & rpc::object_storage::flag_list_type)) {    if (++itrArgs != args.end())      new_args.insert(new_args.end(), itrArgs, args.end());   } else {    throw torrent::input_error("No object type specified.");  }   return system_method_insert_object(new_args, new_flags);} // 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::Objectsystem_method_erase(const torrent::Object::string_type& args) {  rpc::CommandMap::iterator itr = rpc::commands.find(args.c_str());   if (itr == rpc::commands.end())    return torrent::Object();   if (!rpc::commands.is_modifiable(itr))    throw torrent::input_error("Command not modifiable.");   rpc::commands.erase(itr);   return torrent::Object();} torrent::Objectsystem_method_redirect(const torrent::Object::list_type& args) {  if (args.size() != 2)    throw torrent::input_error("Invalid argument count.");   std::string new_key  = torrent::object_create_string(args.front());  std::string dest_key = torrent::object_create_string(args.back());   rpc::commands.create_redirect(new_key, dest_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_modifiable);   return torrent::Object();} torrent::Objectsystem_method_set_function(const torrent::Object::list_type& args) {  if (args.empty())    throw torrent::input_error("Invalid argument count.");   rpc::object_storage::iterator itr =    control->object_storage()->find_raw_string(torrent::raw_string::from_string(args.front().as_string()));   if (itr == control->object_storage()->end() || itr->second.flags & rpc::object_storage::flag_constant)    throw torrent::input_error("Command is not modifiable.");   return control->object_storage()->set_str_function(args.front().as_string(),                                                     system_method_generate_command(++args.begin(), args.end()));} torrent::Objectsystem_method_has_key(const torrent::Object::list_type& args) {  if (args.empty() || ++args.begin() == args.end())    throw torrent::input_error("Invalid argument count.");   torrent::Object::list_const_iterator itrArgs = args.begin();  const std::string&                   key     = (itrArgs++)->as_string();  const std::string&                   cmd_key = (itrArgs++)->as_string();   return control->object_storage()->has_str_multi_key(key, cmd_key);} torrent::Objectsystem_method_set_key(const torrent::Object::list_type& args) {  if (args.empty() || ++args.begin() == args.end())    throw torrent::input_error("Invalid argument count.");   torrent::Object::list_const_iterator itrArgs = args.begin();  const std::string&                   key     = (itrArgs++)->as_string();  const std::string&                   cmd_key = (itrArgs++)->as_string();   if (itrArgs == args.end()) {    control->object_storage()->erase_str_multi_key(key, cmd_key);    return torrent::Object();  }   if (itrArgs->is_dict_key() || itrArgs->is_list())    control->object_storage()->set_str_multi_key_obj(key.c_str(), cmd_key, *itrArgs);  else    control->object_storage()->set_str_multi_key(key, cmd_key, system_method_generate_command(itrArgs, args.end()));   return torrent::Object();} torrent::Objectsystem_method_list_keys(const torrent::Object::string_type& args) {  torrent::Object                  rawResult = torrent::Object::create_list();  torrent::Object::list_type&      result    = rawResult.as_list();   for (const auto& itr : control->object_storage()->get_str(args).as_map())    result.push_back(itr.first);   return rawResult;} torrent::Objectcmd_catch(rpc::target_type target, const torrent::Object& args) {  try {    return rpc::call_object(args, target);  } catch (torrent::input_error& e) {    lt_log_print(torrent::LOG_WARN, "Caught exception: '%s'.", e.what());    return torrent::Object();  }} #define CMD2_METHOD_INSERT(key, flags) \  CMD2_ANY_LIST(key, std::bind(&system_method_insert_object, std::placeholders::_2, flags)); voidinitialize_command_dynamic() {  // clang-format off#ifdef HAVE_XMLRPC_TINYXML2  CMD2_ANY         ("system.listMethods", std::bind(&system_listMethods)); // only used by tinyxml2#endif   // Keep these for future use when we deprecate more commands.  CMD2_VAR_BOOL    ("method.use_deprecated", false);  CMD2_VAR_VALUE   ("method.use_intermediate", 3);   CMD2_ANY_LIST    ("method.insert",             std::bind(&system_method_insert, std::placeholders::_2));  CMD2_ANY_LIST    ("method.insert.value",       std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_value_type));  CMD2_ANY_LIST    ("method.insert.bool",        std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_bool_type));  CMD2_ANY_LIST    ("method.insert.string",      std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_string_type));  CMD2_ANY_LIST    ("method.insert.list",        std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_list_type));   CMD2_METHOD_INSERT("method.insert.simple",     rpc::object_storage::flag_function_type);  CMD2_METHOD_INSERT("method.insert.c_simple",   rpc::object_storage::flag_constant | rpc::object_storage::flag_function_type);  CMD2_METHOD_INSERT("method.insert.s_c_simple", rpc::object_storage::flag_static |                     rpc::object_storage::flag_constant |rpc::object_storage::flag_function_type);   CMD2_ANY_STRING  ("method.erase",     std::bind(&system_method_erase, std::placeholders::_2));  CMD2_ANY_LIST    ("method.redirect",  std::bind(&system_method_redirect, std::placeholders::_2));  CMD2_ANY_STRING  ("method.get",       std::bind(&rpc::object_storage::get_str, control->object_storage(),                                                       std::placeholders::_2));  CMD2_ANY_LIST    ("method.set",       std::bind(&system_method_set_function, std::placeholders::_2));   CMD2_ANY_STRING  ("method.const",        std::bind(&rpc::object_storage::has_flag_str, control->object_storage(),                                                      std::placeholders::_2, rpc::object_storage::flag_constant));  CMD2_ANY_STRING_V("method.const.enable", std::bind(&rpc::object_storage::enable_flag_str, control->object_storage(),                                                     std::placeholders::_2, rpc::object_storage::flag_constant));   CMD2_ANY_LIST    ("method.has_key",   std::bind(&system_method_has_key, std::placeholders::_2));  CMD2_ANY_LIST    ("method.set_key",   std::bind(&system_method_set_key, std::placeholders::_2));  CMD2_ANY_STRING  ("method.list_keys", std::bind(&system_method_list_keys, std::placeholders::_2));   CMD2_ANY_STRING  ("method.rlookup",       std::bind(&rpc::object_storage::rlookup_obj_list, control->object_storage(), std::placeholders::_2));  CMD2_ANY_STRING_V("method.rlookup.clear", std::bind(&rpc::object_storage::rlookup_clear, control->object_storage(), std::placeholders::_2));   CMD2_ANY         ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2));   CMD2_ANY_STRING  ("enum.log_group",                    [](auto, const auto& str) { return torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, str); });   CMD2_ANY         ("strings.choke_heuristics",          [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS); });  CMD2_ANY         ("strings.choke_heuristics.upload",   [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_UPLOAD); });  CMD2_ANY         ("strings.choke_heuristics.download", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD); });  CMD2_ANY         ("strings.connection_type",           [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CONNECTION_TYPE); });  CMD2_ANY         ("strings.encryption",                [](auto, auto) { return torrent::Object::create_list(); });  CMD2_ANY         ("strings.encryption.handshake",      [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_HANDSHAKE); });  CMD2_ANY         ("strings.encryption.stream",         [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_STREAM); });  CMD2_ANY         ("strings.ip_filter",                 [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_FILTER); });  CMD2_ANY         ("strings.ip_tos",                    [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_TOS); });  CMD2_ANY         ("strings.log_group",                 [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_LOG_GROUP); });  CMD2_ANY         ("strings.tracker_event",             [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_EVENT); });  CMD2_ANY         ("strings.tracker_mode",              [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_MODE); });  // clang-format on #ifdef HAVE_XMLRPC_TINYXML2  rpc::rpc.mark_safe("system.listMethods");#endif   rpc::rpc.mark_safe("method.use_deprecated");  rpc::rpc.mark_safe("method.const");  rpc::rpc.mark_safe("method.has_key");  rpc::rpc.mark_safe("method.list_keys");  rpc::rpc.mark_safe("method.get");  rpc::rpc.mark_safe("method.rlookup");  rpc::rpc.mark_safe("catch");   rpc::rpc.mark_safe("enum.log_group");   rpc::rpc.mark_safe("strings.choke_heuristics");  rpc::rpc.mark_safe("strings.choke_heuristics.upload");  rpc::rpc.mark_safe("strings.choke_heuristics.download");  rpc::rpc.mark_safe("strings.connection_type");  rpc::rpc.mark_safe("strings.encryption");  rpc::rpc.mark_safe("strings.ip_filter");  rpc::rpc.mark_safe("strings.ip_tos");  rpc::rpc.mark_safe("strings.log_group");  rpc::rpc.mark_safe("strings.tracker_event");  rpc::rpc.mark_safe("strings.tracker_mode");}