mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-07 14:49:21 +00:00
Add JSON-RPC capability
Inline nlohmann/json for the JSON parsing itself, and handle requests with the same SCGI interface as XML-RPC. Based off the work in https://github.com/jesec/rtorrent
This commit is contained in:
@@ -48,8 +48,7 @@
|
||||
#include "command.h"
|
||||
#include "command_map.h"
|
||||
|
||||
// For XMLRPC stuff, clean up.
|
||||
#include "xmlrpc.h"
|
||||
#include "rpc_manager.h"
|
||||
#include "parse_commands.h"
|
||||
|
||||
namespace rpc {
|
||||
@@ -64,8 +63,8 @@ CommandMap::insert(const key_type& key, int flags, const char* parm, const char*
|
||||
throw torrent::internal_error("CommandMap::insert(...) tried to insert an already existing key.");
|
||||
|
||||
// TODO: This is not honoring the public_xmlrpc flags!!!
|
||||
if (rpc::xmlrpc.is_valid() && (flags & flag_public_xmlrpc))
|
||||
rpc::xmlrpc.insert_command(key.c_str(), parm, doc);
|
||||
if (rpc::rpc.is_initialized() && (flags & flag_public_rpc))
|
||||
rpc::rpc.insert_command(key.c_str(), parm, doc);
|
||||
|
||||
return base_type::insert(itr, value_type(key, command_map_data_type(flags, parm, doc)));
|
||||
}
|
||||
@@ -102,11 +101,11 @@ CommandMap::create_redirect(const key_type& key_new, const key_type& key_dest, i
|
||||
|
||||
dest_itr->second.m_flags |= flag_has_redirects;
|
||||
|
||||
flags |= dest_itr->second.m_flags & ~(flag_has_redirects | flag_public_xmlrpc);
|
||||
flags |= dest_itr->second.m_flags & ~(flag_has_redirects | flag_public_rpc);
|
||||
|
||||
// TODO: This is not honoring the public_xmlrpc flags!!!
|
||||
if (rpc::xmlrpc.is_valid() && (flags & flag_public_xmlrpc))
|
||||
rpc::xmlrpc.insert_command(key_new.c_str(), dest_itr->second.m_parm, dest_itr->second.m_doc);
|
||||
if (rpc::rpc.is_initialized() && (flags & flag_public_rpc))
|
||||
rpc::rpc.insert_command(key_new.c_str(), dest_itr->second.m_parm, dest_itr->second.m_doc);
|
||||
|
||||
iterator itr = base_type::insert(base_type::end(),
|
||||
value_type(key_new, command_map_data_type(flags,
|
||||
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
using base_type::find;
|
||||
|
||||
static const int flag_dont_delete = 0x1;
|
||||
static const int flag_public_xmlrpc = 0x4;
|
||||
static const int flag_public_rpc = 0x4;
|
||||
static const int flag_modifiable = 0x10;
|
||||
static const int flag_is_redirect = 0x20;
|
||||
static const int flag_has_redirects = 0x40;
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "rpc/jsonrpc.h"
|
||||
|
||||
#include "rpc/rpc_manager.h"
|
||||
#include "torrent/exceptions.h"
|
||||
#include "torrent/object.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/torrent.h>
|
||||
|
||||
#include "rpc/command.h"
|
||||
#include "rpc/command_map.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "thread_base.h"
|
||||
|
||||
#include "nlohmann/json.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
// Taken from https://www.jsonrpc.org/specification
|
||||
constexpr int JSONRPC_PARSE_ERROR = -32700;
|
||||
constexpr int JSONRPC_INVALID_REQUEST_ERROR = -32600;
|
||||
constexpr int JSONRPC_METHOD_NOT_FOUND_ERROR = -32601;
|
||||
constexpr int JSONRPC_INVALID_PARAMS_ERROR = -32602;
|
||||
constexpr int JSONRPC_INTERNAL_ERROR = -32000;
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
torrent::Object
|
||||
json_to_object(const json& value) {
|
||||
switch (value.type()) {
|
||||
case json::value_t::number_unsigned:
|
||||
case json::value_t::number_integer:
|
||||
return torrent::Object(value.get<int64_t>());
|
||||
case json::value_t::boolean:
|
||||
return value.get<bool>() ? torrent::Object(int64_t(1)) : torrent::Object(int64_t(0));
|
||||
case json::value_t::string:
|
||||
return torrent::Object(value.get<std::string>());
|
||||
case json::value_t::array: {
|
||||
auto array_raw = torrent::Object::create_list();
|
||||
auto& array = array_raw.as_list();
|
||||
for (const auto& entry : value) {
|
||||
array.push_back(json_to_object(entry));
|
||||
}
|
||||
return array_raw;
|
||||
}
|
||||
case json::value_t::object: {
|
||||
auto map_raw = torrent::Object::create_map();
|
||||
auto& map = map_raw.as_map();
|
||||
for (const auto& entry : value.items()) {
|
||||
map[entry.key()] = json_to_object(entry.value());
|
||||
}
|
||||
return map_raw;
|
||||
}
|
||||
case json::value_t::number_float:
|
||||
// type_name() for floats returns 'number', which is accurate for JSON but misleading
|
||||
throw torrent::input_error("invalid parameters: unexpected data type float");
|
||||
default:
|
||||
throw torrent::input_error("invalid parameters: unexpected data type " + std::string(value.type_name()));
|
||||
}
|
||||
}
|
||||
|
||||
json
|
||||
object_to_json(const torrent::Object& object) noexcept {
|
||||
switch (object.type()) {
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
return object.as_value();
|
||||
case torrent::Object::TYPE_STRING:
|
||||
return object.as_string();
|
||||
case torrent::Object::TYPE_LIST: {
|
||||
json result = json::array();
|
||||
std::transform(object.as_list().cbegin(), object.as_list().cend(), std::back_inserter(result), [](const torrent::Object& element) {
|
||||
return object_to_json(element);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
case torrent::Object::TYPE_MAP: {
|
||||
json result = json::object();
|
||||
for (const auto& entry : object.as_map()) {
|
||||
result.emplace(entry.first, object_to_json(entry.second));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case torrent::Object::TYPE_DICT_KEY: {
|
||||
json result = json::array();
|
||||
result.push_back(object_to_json(object.as_dict_key()));
|
||||
|
||||
const auto& dict_obj = object.as_dict_obj();
|
||||
if (dict_obj.is_list()) {
|
||||
std::transform(dict_obj.as_list().cbegin(), dict_obj.as_list().cend(), std::back_inserter(result), [](const torrent::Object& element) {
|
||||
return object_to_json(element);
|
||||
});
|
||||
} else {
|
||||
result.push_back(object_to_json(dict_obj));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
json
|
||||
jsonrpc_call_command(const std::string& method, const json& params) {
|
||||
if (params.type() == json::value_t::object) {
|
||||
// Named parameters is valid JSON-RPC, rtorrent just doesn't support it
|
||||
throw rpc_error(JSONRPC_INVALID_PARAMS_ERROR, "invalid parameter: procedure named parameter not supported");
|
||||
} else if (params.type() != json::value_t::array) {
|
||||
throw rpc_error(JSONRPC_INVALID_REQUEST_ERROR, "invalid request: params field must be an array");
|
||||
}
|
||||
|
||||
CommandMap::iterator itr = commands.find(method.c_str());
|
||||
|
||||
if (itr == commands.end()) {
|
||||
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, "method not found: " + method);
|
||||
}
|
||||
|
||||
torrent::Object params_object = json_to_object(params);
|
||||
auto& params_object_list = params_object.as_list();
|
||||
rpc::target_type target = rpc::make_target();
|
||||
|
||||
if (!(itr->second.m_flags & CommandMap::flag_no_target)) {
|
||||
// Provide a blank target if none was provided
|
||||
if (params_object_list.empty())
|
||||
params_object_list.push_back("");
|
||||
|
||||
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);
|
||||
params_object_list.erase(params_object_list.begin());
|
||||
}
|
||||
|
||||
const auto& result = rpc::commands.call_command(itr, params_object, target);
|
||||
|
||||
return object_to_json(result);
|
||||
}
|
||||
|
||||
json
|
||||
json_error(int code, const std::string& msg, json id) {
|
||||
return json{{"jsonrpc", "2.0"}, {"id", id}, {"error", {{"code", code}, {"message", msg}}}};
|
||||
}
|
||||
|
||||
json
|
||||
handle_request(const json& request) {
|
||||
json response = {{"jsonrpc", "2.0"}, {"id", nullptr}};
|
||||
|
||||
try {
|
||||
if (!request["id"].is_number() && !request["id"].is_string() && !request["id"].is_null())
|
||||
return json_error(JSONRPC_INVALID_REQUEST_ERROR, "request id is invalid type " + std::string(request["id"].type_name()), response["id"]);
|
||||
response["id"] = request["id"];
|
||||
if (!request.contains("method") || !request["method"].is_string())
|
||||
return json_error(JSONRPC_INVALID_REQUEST_ERROR, "method string not present", response["id"]);
|
||||
if (request.contains("params"))
|
||||
response["result"] = jsonrpc_call_command(request["method"], request["params"]);
|
||||
else
|
||||
response["result"] = jsonrpc_call_command(request["method"], json::array({""}));
|
||||
return response;
|
||||
} catch (rpc_error& e) {
|
||||
return json_error(e.type(), e.what(), response["id"]);
|
||||
} catch (torrent::input_error& e) {
|
||||
return json_error(JSONRPC_INVALID_PARAMS_ERROR, e.what(), response["id"]);
|
||||
} catch (torrent::local_error& e) {
|
||||
return json_error(JSONRPC_INTERNAL_ERROR, e.what(), response["id"]);
|
||||
}
|
||||
}
|
||||
|
||||
// Notifications are basically the same as requests, except we can
|
||||
// just drop the message on the floor if there are any errors
|
||||
void
|
||||
handle_notification(const json& request) noexcept {
|
||||
if (!request.contains("method") || !request["method"].is_string())
|
||||
return;
|
||||
try {
|
||||
if (request.contains("params"))
|
||||
jsonrpc_call_command(request["method"], request["params"]);
|
||||
else
|
||||
jsonrpc_call_command(request["method"], json::array({""}));
|
||||
} catch (std::exception& e) {
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
|
||||
json response;
|
||||
json body;
|
||||
|
||||
try {
|
||||
body = json::parse(in_buffer, in_buffer + length);
|
||||
switch (body.type()) {
|
||||
case json::value_t::object: {
|
||||
if (!body.contains("id")) {
|
||||
handle_notification(body);
|
||||
return callback("", 0);
|
||||
} else {
|
||||
response = handle_request(body);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case json::value_t::array: {
|
||||
// Empty batch requests are invalid as per the spec
|
||||
if (body.empty()) {
|
||||
response = json_error(JSONRPC_INVALID_REQUEST_ERROR, "invalid request: empty batch", nullptr);
|
||||
break;
|
||||
}
|
||||
response = json::array();
|
||||
for (const auto& sub_body : body) {
|
||||
if (!sub_body.contains("id"))
|
||||
handle_notification(sub_body);
|
||||
else
|
||||
response.push_back(handle_request(sub_body));
|
||||
}
|
||||
// This indicates the batch was composed entirely of
|
||||
// notifications, in which case nothing is returned
|
||||
if (response.empty())
|
||||
return callback("", 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
response = json_error(JSONRPC_PARSE_ERROR, "message type " + std::string(body.type_name()) + " unsupported", nullptr);
|
||||
}
|
||||
|
||||
std::string response_str = response.dump();
|
||||
|
||||
return callback(response_str.c_str(), response_str.size());
|
||||
|
||||
} catch (json::parse_error& e) {
|
||||
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
} catch (json::type_error& e) {
|
||||
// Type errors may be caused by invalid UTF-8 strings in exception strings, hence the ::replace
|
||||
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rpc
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef RTORRENT_RPC_JSONRPC_H
|
||||
#define RTORRENT_RPC_JSONRPC_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class JsonRpc {
|
||||
public:
|
||||
using slot_write = std::function<bool(const char*, uint32_t)>;
|
||||
|
||||
void initialize() {};
|
||||
void cleanup() {};
|
||||
|
||||
bool process(const char* in_buffer, uint32_t length, slot_write callback);
|
||||
|
||||
void insert_command(const char* name, const char* parm, const char* doc) {};
|
||||
};
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
#endif
|
||||
+25427
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rpc/rpc_manager.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
@@ -49,7 +50,7 @@
|
||||
namespace rpc {
|
||||
|
||||
CommandMap commands;
|
||||
XmlRpc xmlrpc;
|
||||
RpcManager rpc;
|
||||
ExecFile execFile;
|
||||
|
||||
inline bool command_map_is_space(char c) {
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "command_map.h"
|
||||
#include "exec_file.h"
|
||||
#include "xmlrpc.h"
|
||||
#include "rpc_manager.h"
|
||||
|
||||
namespace core {
|
||||
class Download;
|
||||
@@ -52,7 +53,7 @@ namespace rpc {
|
||||
|
||||
// Move to another file?
|
||||
extern CommandMap commands;
|
||||
extern XmlRpc xmlrpc;
|
||||
extern RpcManager rpc;
|
||||
extern ExecFile execFile;
|
||||
|
||||
typedef std::pair<torrent::Object, const char*> parse_command_type;
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "parse_commands.h"
|
||||
#include "rpc/rpc_manager.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
void
|
||||
RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* target) {
|
||||
if (call_flags & CommandMap::flag_no_target)
|
||||
return;
|
||||
|
||||
if (!obj.is_string()) {
|
||||
throw torrent::input_error("invalid parameters: target must be a string");
|
||||
}
|
||||
std::string target_string = obj.as_string();
|
||||
bool require_index = (call_flags & (CommandMap::flag_tracker_target | CommandMap::flag_file_target));
|
||||
if (target_string.size() == 0 && !require_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Length of SHA1 hash is 40
|
||||
if (target_string.size() < 40) {
|
||||
throw torrent::input_error("invalid parameters: invalid target");
|
||||
}
|
||||
|
||||
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()) {
|
||||
if (require_index) {
|
||||
throw torrent::input_error("invalid parameters: no index");
|
||||
}
|
||||
hash = target_string;
|
||||
} else {
|
||||
hash = target_string.substr(0, delim_pos);
|
||||
type = target_string[delim_pos + 1];
|
||||
index = target_string.substr(delim_pos + 2);
|
||||
}
|
||||
core::Download* download = rpc.slot_find_download()(hash.c_str());
|
||||
|
||||
if (download == nullptr)
|
||||
throw torrent::input_error("invalid parameters: info-hash not found");
|
||||
|
||||
try {
|
||||
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))));
|
||||
break;
|
||||
case 't':
|
||||
*target = rpc::make_target(
|
||||
command_base::target_tracker,
|
||||
rpc.slot_find_tracker()(download, std::stoi(std::string(index))));
|
||||
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));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw torrent::input_error("invalid parameters: unexpected target type");
|
||||
}
|
||||
} catch (const std::logic_error&) {
|
||||
throw torrent::input_error("invalid parameters: invalid index");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
|
||||
switch (type) {
|
||||
case RPCType::XML: {
|
||||
if (m_xmlrpc.is_valid() && rpc::call_command_value("network.rpc.use_xmlrpc")) {
|
||||
return m_xmlrpc.process(in_buffer, length, callback);
|
||||
} else {
|
||||
const std::string response = "<?xml version=\"1.0\"?><methodResponse><fault><struct><member><name>faultCode</name><value><i8>-501</i8></value></member><member><name>faultString</name><value><string>XML-RPC not supported</string></value></member></struct></fault></methodResponse>";
|
||||
return callback(response.c_str(), response.size());
|
||||
}
|
||||
}
|
||||
case RPCType::JSON: {
|
||||
if (rpc::call_command_value("network.rpc.use_jsonrpc")) {
|
||||
return m_jsonrpc.process(in_buffer, length, callback);
|
||||
} else {
|
||||
const std::string response = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"JSON-RPC not supported\"},\"id\":null}";
|
||||
return callback(response.c_str(), response.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::initialize() {
|
||||
m_xmlrpc.initialize();
|
||||
m_jsonrpc.initialize();
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::cleanup() {
|
||||
m_xmlrpc.cleanup();
|
||||
m_jsonrpc.cleanup();
|
||||
}
|
||||
|
||||
bool
|
||||
RpcManager::is_type_enabled(RPCType type) const {
|
||||
switch (type) {
|
||||
case RPCType::XML:
|
||||
return m_is_xmlrpc_enabled;
|
||||
case RPCType::JSON:
|
||||
return m_is_jsonrpc_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::set_type_enabled(RPCType type, bool enabled) {
|
||||
switch (type) {
|
||||
case RPCType::XML:
|
||||
m_is_xmlrpc_enabled = enabled;
|
||||
case RPCType::JSON:
|
||||
m_is_jsonrpc_enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RpcManager::is_initialized() const {
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::insert_command(const char* name, const char* parm, const char* doc) {
|
||||
m_xmlrpc.insert_command(name, parm, doc);
|
||||
m_jsonrpc.insert_command(name, parm, doc);
|
||||
}
|
||||
|
||||
} // namespace rpc
|
||||
@@ -0,0 +1,88 @@
|
||||
#ifndef RTORRENT_RPC_MANAGER_H
|
||||
#define RTORRENT_RPC_MANAGER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include "rpc/command.h"
|
||||
#include "rpc/jsonrpc.h"
|
||||
#include "rpc/xmlrpc.h"
|
||||
|
||||
namespace core {
|
||||
class Download;
|
||||
}
|
||||
|
||||
namespace torrent {
|
||||
class File;
|
||||
class Object;
|
||||
class Tracker;
|
||||
} // namespace torrent
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class rpc_error : public torrent::base_error {
|
||||
public:
|
||||
rpc_error(int type, std::string msg) :
|
||||
m_type(type), m_msg(msg) {}
|
||||
virtual ~rpc_error() throw() {}
|
||||
|
||||
virtual int type() const throw() { return m_type; }
|
||||
virtual const char* what() const throw() { return m_msg.c_str(); }
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
class RpcManager {
|
||||
public:
|
||||
using slot_download = std::function<core::Download*(const char*)>;
|
||||
using slot_file = std::function<torrent::File*(core::Download*, uint32_t)>;
|
||||
using slot_tracker = std::function<torrent::Tracker*(core::Download*, uint32_t)>;
|
||||
using slot_peer = std::function<torrent::Peer*(core::Download*, const torrent::HashString&)>;
|
||||
using slot_response_callback = std::function<bool(const char*, uint32_t)>;
|
||||
|
||||
enum RPCType { XML,
|
||||
JSON };
|
||||
|
||||
RpcManager() = default;
|
||||
~RpcManager() = default;
|
||||
|
||||
void initialize();
|
||||
void cleanup();
|
||||
bool is_initialized() const;
|
||||
|
||||
int64_t size_limit() { return m_xmlrpc.size_limit(); };
|
||||
void set_size_limit(uint64_t size) { m_xmlrpc.set_size_limit(size); };
|
||||
int dialect() { return m_xmlrpc.dialect(); }
|
||||
void set_dialect(int dialect) { m_xmlrpc.set_dialect(dialect); }
|
||||
|
||||
bool is_type_enabled(RPCType type) const;
|
||||
void set_type_enabled(RPCType type, bool enabled);
|
||||
|
||||
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; }
|
||||
|
||||
private:
|
||||
XmlRpc m_xmlrpc;
|
||||
JsonRpc m_jsonrpc;
|
||||
|
||||
bool m_initialized = false;
|
||||
bool m_is_jsonrpc_enabled = true;
|
||||
bool m_is_xmlrpc_enabled = true;
|
||||
|
||||
slot_download m_slot_find_download;
|
||||
slot_file m_slot_find_file;
|
||||
slot_tracker m_slot_find_tracker;
|
||||
slot_peer m_slot_find_peer;
|
||||
};
|
||||
} // namespace rpc
|
||||
|
||||
#endif
|
||||
+10
-2
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rpc/scgi_task.h"
|
||||
#include <rak/error_number.h>
|
||||
#include <rak/socket_address.h>
|
||||
#include <sys/un.h>
|
||||
@@ -166,11 +167,18 @@ SCgi::event_error() {
|
||||
|
||||
bool
|
||||
SCgi::receive_call(SCgiTask* task, const char* buffer, uint32_t length) {
|
||||
bool result = false;
|
||||
torrent::thread_base::acquire_global_lock();
|
||||
torrent::main_thread()->interrupt();
|
||||
|
||||
bool result = xmlrpc.process(buffer, length, [task](const char* b, uint32_t l) { return task->receive_write(b, l); });
|
||||
|
||||
switch (task->content_type()) {
|
||||
case rpc::SCgiTask::ContentType::JSON:
|
||||
result = rpc.process(RpcManager::RPCType::JSON, buffer, length, [task](const char* b, uint32_t l) { return task->receive_write(b, l); });
|
||||
break;
|
||||
case rpc::SCgiTask::ContentType::XML:
|
||||
result = rpc.process(RpcManager::RPCType::XML, buffer, length, [task](const char* b, uint32_t l) { return task->receive_write(b, l); });
|
||||
break;
|
||||
}
|
||||
torrent::thread_base::release_global_lock();
|
||||
|
||||
return result;
|
||||
|
||||
+76
-60
@@ -1,44 +1,9 @@
|
||||
// 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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <rak/allocators.h>
|
||||
#include <rak/error_number.h>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <torrent/exceptions.h>
|
||||
@@ -126,47 +91,94 @@ SCgiTask::event_read() {
|
||||
// receive all the data we need the first time.
|
||||
char* current;
|
||||
|
||||
int contentSize;
|
||||
int headerSize = strtol(m_buffer, ¤t, 0);
|
||||
int header_size = strtol(m_buffer, ¤t, 0);
|
||||
|
||||
if (current == m_position)
|
||||
return;
|
||||
|
||||
// If the request doesn't start with an integer or if it didn't
|
||||
// end in ':', then close the connection.
|
||||
if (current == m_buffer || *current != ':' || headerSize < 17 || headerSize > max_header_size)
|
||||
if (current == m_buffer || *current != ':' || header_size < 17 || header_size > max_header_size)
|
||||
goto event_read_failed;
|
||||
|
||||
if (std::distance(++current, m_position) < headerSize + 1)
|
||||
if (std::distance(++current, m_position) < header_size + 1)
|
||||
return;
|
||||
|
||||
// We'll parse this fully below, but the SCGI spec requires it to
|
||||
// be the first header.
|
||||
if (std::memcmp(current, "CONTENT_LENGTH", 15) != 0)
|
||||
goto event_read_failed;
|
||||
|
||||
char* contentPos;
|
||||
contentSize = strtol(current + 15, &contentPos, 0);
|
||||
std::string content_type = "";
|
||||
size_t content_length = 0;
|
||||
const char* header_end = current + header_size;
|
||||
|
||||
if (*contentPos != '\0' || contentSize <= 0 || contentSize > max_content_size)
|
||||
// Parse out the null-terminated header keys and values, with
|
||||
// checks to ensure it doesn't scan beyond the limits of the
|
||||
// header
|
||||
while (current < header_end) {
|
||||
char* key = current;
|
||||
char* key_end = static_cast<char*>(std::memchr(current, '\0', header_end - current));
|
||||
if (!key_end)
|
||||
goto event_read_failed;
|
||||
|
||||
current = key_end + 1;
|
||||
if (current >= header_end)
|
||||
goto event_read_failed;
|
||||
|
||||
char* value = current;
|
||||
char* value_end = static_cast<char*>(std::memchr(current, '\0', header_end - current));
|
||||
if (!value_end)
|
||||
goto event_read_failed;
|
||||
current = value_end + 1;
|
||||
|
||||
if (strcmp(key, "CONTENT_LENGTH") == 0) {
|
||||
char* content_pos;
|
||||
content_length = strtol(value, &content_pos, 10);
|
||||
if (*content_pos != '\0' || content_length <= 0 || content_length > max_content_size)
|
||||
goto event_read_failed;
|
||||
} else if (strcmp(key, "CONTENT_TYPE") == 0) {
|
||||
content_type = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (current != header_end)
|
||||
goto event_read_failed;
|
||||
|
||||
m_body = current + headerSize + 1;
|
||||
headerSize = std::distance(m_buffer, m_body);
|
||||
if (content_length <= 0)
|
||||
goto event_read_failed;
|
||||
|
||||
if ((unsigned int)(contentSize + headerSize) < m_bufferSize) {
|
||||
m_bufferSize = contentSize + headerSize;
|
||||
m_body = current + 1;
|
||||
header_size = std::distance(m_buffer, m_body);
|
||||
|
||||
} else if ((unsigned int)contentSize <= default_buffer_size) {
|
||||
m_bufferSize = contentSize;
|
||||
if (content_type == "") {
|
||||
// If no CONTENT_TYPE was supplied, peek at the body to check if it's JSON
|
||||
// { is a single request object, while [ is a batch array
|
||||
if (*m_body == '{' || *m_body == '[')
|
||||
m_content_type = ContentType::JSON;
|
||||
} else if (content_type == "application/json") {
|
||||
m_content_type = ContentType::JSON;
|
||||
} else if (content_type == "text/xml") {
|
||||
m_content_type = ContentType::XML;
|
||||
} else {
|
||||
goto event_read_failed;
|
||||
}
|
||||
|
||||
if ((unsigned int)(content_length + header_size) < m_bufferSize) {
|
||||
m_bufferSize = content_length + header_size;
|
||||
|
||||
} else if ((unsigned int)content_length <= default_buffer_size) {
|
||||
m_bufferSize = content_length;
|
||||
|
||||
std::memmove(m_buffer, m_body, std::distance(m_body, m_position));
|
||||
m_position = m_buffer + std::distance(m_body, m_position);
|
||||
m_body = m_buffer;
|
||||
m_body = m_buffer;
|
||||
|
||||
} else {
|
||||
realloc_buffer((m_bufferSize = contentSize) + 1, m_body, std::distance(m_body, m_position));
|
||||
realloc_buffer((m_bufferSize = content_length) + 1, m_body, std::distance(m_body, m_position));
|
||||
|
||||
m_position = m_buffer + std::distance(m_body, m_position);
|
||||
m_body = m_buffer;
|
||||
m_body = m_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,14 +205,14 @@ SCgiTask::event_read() {
|
||||
|
||||
return;
|
||||
|
||||
event_read_failed:
|
||||
// throw torrent::internal_error("SCgiTask::event_read() fault not handled.");
|
||||
event_read_failed:
|
||||
// throw torrent::internal_error("SCgiTask::event_read() fault not handled.");
|
||||
close();
|
||||
}
|
||||
|
||||
void
|
||||
SCgiTask::event_write() {
|
||||
// Apple and Solaris do not support MSG_NOSIGNAL,
|
||||
// Apple and Solaris do not support MSG_NOSIGNAL,
|
||||
// so disable this fix until we find a better solution
|
||||
#if defined(__APPLE__) || defined(__sun__)
|
||||
int bytes = ::send(m_fileDesc, m_position, m_bufferSize, 0);
|
||||
@@ -236,12 +248,16 @@ SCgiTask::receive_write(const char* buffer, uint32_t length) {
|
||||
if (length + 256 > std::max(m_bufferSize, (unsigned int)default_buffer_size))
|
||||
realloc_buffer(length + 256, NULL, 0);
|
||||
|
||||
// Who ever bothers to check the return value?
|
||||
int headerSize = sprintf(m_buffer, "Status: 200 OK\r\nContent-Type: text/xml\r\nContent-Length: %i\r\n\r\n", length);
|
||||
const auto header = m_content_type == ContentType::JSON
|
||||
? "Status: 200 OK\r\nContent-Type: application/json\r\nContent-Length: %i\r\n\r\n"
|
||||
: "Status: 200 OK\r\nContent-Type: text/xml\r\nContent-Length: %i\r\n\r\n";
|
||||
|
||||
m_position = m_buffer;
|
||||
// Who ever bothers to check the return value?
|
||||
int headerSize = sprintf(m_buffer, header, length);
|
||||
|
||||
m_position = m_buffer;
|
||||
m_bufferSize = length + headerSize;
|
||||
|
||||
|
||||
std::memcpy(m_buffer + headerSize, buffer, length);
|
||||
|
||||
if (m_parent->log_fd() >= 0) {
|
||||
@@ -258,4 +274,4 @@ SCgiTask::receive_write(const char* buffer, uint32_t length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace rpc
|
||||
|
||||
@@ -53,6 +53,8 @@ public:
|
||||
static const int max_header_size = 2000;
|
||||
static const int max_content_size = (2 << 23);
|
||||
|
||||
enum ContentType { XML, JSON };
|
||||
|
||||
SCgiTask() { m_fileDesc = -1; }
|
||||
|
||||
bool is_open() const { return m_fileDesc != -1; }
|
||||
@@ -61,6 +63,8 @@ public:
|
||||
void open(SCgi* parent, int fd);
|
||||
void close();
|
||||
|
||||
ContentType content_type() const { return m_content_type; }
|
||||
|
||||
virtual void event_read();
|
||||
virtual void event_write();
|
||||
virtual void event_error();
|
||||
@@ -79,6 +83,8 @@ private:
|
||||
char* m_body;
|
||||
|
||||
unsigned int m_bufferSize;
|
||||
|
||||
ContentType m_content_type{ XML };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+1
-89
@@ -44,98 +44,10 @@
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class xmlrpc_error : public torrent::base_error {
|
||||
public:
|
||||
xmlrpc_error(int type, std::string msg) : m_type(type), m_msg(msg) {}
|
||||
virtual ~xmlrpc_error() throw() {}
|
||||
|
||||
virtual int type() const throw() { return m_type; }
|
||||
virtual const char* what() const throw() { return m_msg.c_str(); }
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
void
|
||||
XmlRpc::object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target) {
|
||||
if (callFlags & CommandMap::flag_no_target)
|
||||
return;
|
||||
|
||||
if (!obj.is_string()) {
|
||||
throw torrent::input_error("invalid parameters: target must be a string");
|
||||
}
|
||||
std::string target_string = obj.as_string();
|
||||
bool require_index = (callFlags & (CommandMap::flag_tracker_target | CommandMap::flag_file_target));
|
||||
if (target_string.size() == 0 && !require_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Length of SHA1 hash is 40
|
||||
if (target_string.size() < 40) {
|
||||
throw torrent::input_error("invalid parameters: invalid target");
|
||||
}
|
||||
|
||||
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()) {
|
||||
if (require_index) {
|
||||
throw torrent::input_error("invalid parameters: no index");
|
||||
}
|
||||
hash = target_string;
|
||||
} else {
|
||||
hash = target_string.substr(0, delim_pos);
|
||||
type = target_string[delim_pos + 1];
|
||||
index = target_string.substr(delim_pos + 2);
|
||||
}
|
||||
core::Download* download = xmlrpc.slot_find_download()(hash.c_str());
|
||||
|
||||
if (download == nullptr)
|
||||
throw torrent::input_error("invalid parameters: info-hash not found");
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case 'd':
|
||||
*target = rpc::make_target(download);
|
||||
break;
|
||||
case 'f':
|
||||
*target = rpc::make_target(
|
||||
command_base::target_file,
|
||||
xmlrpc.slot_find_file()(download, std::stoi(std::string(index))));
|
||||
break;
|
||||
case 't':
|
||||
*target = rpc::make_target(
|
||||
command_base::target_tracker,
|
||||
xmlrpc.slot_find_tracker()(download, std::stoi(std::string(index))));
|
||||
break;
|
||||
case 'p': {
|
||||
if (index.size() < 40) {
|
||||
// -501 == XMLRPC_TYPE_ERROR, used here directly to avoid
|
||||
// conflicts between tinyxml2 and xmlrpc-c
|
||||
throw xmlrpc_error(-501, "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,
|
||||
xmlrpc.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");
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_XMLRPC_C
|
||||
#ifndef HAVE_XMLRPC_TINYXML2
|
||||
|
||||
void XmlRpc::initialize() { throw torrent::resource_error("XMLRPC not supported."); }
|
||||
void XmlRpc::initialize() {}
|
||||
void XmlRpc::cleanup() {}
|
||||
|
||||
void XmlRpc::insert_command(__UNUSED const char* name, __UNUSED const char* parm, __UNUSED const char* doc) {}
|
||||
|
||||
+2
-1
@@ -51,6 +51,7 @@
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "rpc_manager.h"
|
||||
#include "xmlrpc.h"
|
||||
#include "parse_commands.h"
|
||||
|
||||
@@ -138,7 +139,7 @@ rpc::target_type
|
||||
xmlrpc_to_target(xmlrpc_env* env, xmlrpc_value* value, int callType) {
|
||||
rpc::target_type target;
|
||||
|
||||
XmlRpc::object_to_target(xmlrpc_to_object(env, value, -1, nullptr), callType, &target);
|
||||
Rpc::object_to_target(xmlrpc_to_object(env, value, -1, nullptr), callType, &target);
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
+19
-32
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "parse_commands.h"
|
||||
#include "rpc/tinyxml2/tinyxml2.h"
|
||||
#include "rpc/rpc_manager.h"
|
||||
#include "utils/base64.h"
|
||||
#include "xmlrpc.h"
|
||||
|
||||
@@ -35,20 +36,6 @@ const int XMLRPC_INTROSPECTION_DISABLED_ERROR = -508;
|
||||
const int XMLRPC_LIMIT_EXCEEDED_ERROR = -509;
|
||||
const int XMLRPC_INVALID_UTF8_ERROR = -510;
|
||||
|
||||
class xmlrpc_error : public torrent::base_error {
|
||||
public:
|
||||
xmlrpc_error(int type, std::string msg) :
|
||||
m_type(type), m_msg(msg) {}
|
||||
virtual ~xmlrpc_error() throw() {}
|
||||
|
||||
virtual int type() const throw() { return m_type; }
|
||||
virtual const char* what() const throw() { return m_msg.c_str(); }
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
const tinyxml2::XMLElement*
|
||||
element_access(const tinyxml2::XMLElement* elem, std::initializer_list<std::string> names) {
|
||||
// Helper function to check each step of a element access, in lieu of XPath
|
||||
@@ -56,7 +43,7 @@ element_access(const tinyxml2::XMLElement* elem, std::initializer_list<std::stri
|
||||
for (auto itr : names) {
|
||||
result = result->FirstChildElement(itr.c_str());
|
||||
if (result == nullptr) {
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, "could not find expected element " + itr);
|
||||
throw rpc_error(XMLRPC_PARSE_ERROR, "could not find expected element " + itr);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -66,22 +53,22 @@ long long
|
||||
element_to_int(const tinyxml2::XMLNode* elem) {
|
||||
char* pos;
|
||||
if (elem->FirstChild() == nullptr) {
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer");
|
||||
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer");
|
||||
}
|
||||
auto str = elem->FirstChild()->ToText()->Value();
|
||||
auto result = std::strtoll(str, &pos, 10);
|
||||
if (pos == str || *pos != '\0')
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value");
|
||||
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value");
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
||||
if (elem == nullptr) {
|
||||
throw xmlrpc_error(XMLRPC_INTERNAL_ERROR, "received null element to convert");
|
||||
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received null element to convert");
|
||||
}
|
||||
if (std::strncmp(elem->Value(), "value", sizeof("value")) != 0) {
|
||||
throw xmlrpc_error(XMLRPC_INTERNAL_ERROR, "received non-value element to convert");
|
||||
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received non-value element to convert");
|
||||
}
|
||||
auto root_element = elem->FirstChild();
|
||||
auto root_type = root_element->Value();
|
||||
@@ -102,13 +89,13 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
||||
} else if (boolean_text == "0") {
|
||||
return torrent::Object((int64_t)0);
|
||||
}
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "unknown boolean value: " + boolean_text);
|
||||
throw rpc_error(XMLRPC_TYPE_ERROR, "unknown boolean value: " + boolean_text);
|
||||
} else if (std::strncmp(root_type, "array", sizeof("array")) == 0) {
|
||||
auto array_raw = torrent::Object::create_list();
|
||||
auto& array = array_raw.as_list();
|
||||
auto data_element = root_element->ToElement()->FirstChildElement("data");
|
||||
if (data_element == nullptr)
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, "could not find expected data element in array");
|
||||
throw rpc_error(XMLRPC_PARSE_ERROR, "could not find expected data element in array");
|
||||
for (auto child = data_element->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
|
||||
array.push_back(xml_value_to_object(child));
|
||||
}
|
||||
@@ -128,7 +115,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
||||
}
|
||||
return torrent::Object(utils::decode_base64(utils::remove_newlines(child_element->ToText()->Value())));
|
||||
} else {
|
||||
throw xmlrpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
|
||||
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
|
||||
}
|
||||
return torrent::Object();
|
||||
}
|
||||
@@ -202,8 +189,8 @@ print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) {
|
||||
torrent::Object
|
||||
execute_command(std::string method_name, const tinyxml2::XMLElement* params_element) {
|
||||
CommandMap::iterator cmd_itr = commands.find(method_name.c_str());
|
||||
if (cmd_itr == commands.end() || !(cmd_itr->second.m_flags & CommandMap::flag_public_xmlrpc)) {
|
||||
throw xmlrpc_error(XMLRPC_NO_SUCH_METHOD_ERROR, "method '" + method_name + "' not defined");
|
||||
if (cmd_itr == commands.end() || !(cmd_itr->second.m_flags & CommandMap::flag_public_rpc)) {
|
||||
throw rpc_error(XMLRPC_NO_SUCH_METHOD_ERROR, "method '" + method_name + "' not defined");
|
||||
}
|
||||
torrent::Object params_raw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& params = params_raw.as_list();
|
||||
@@ -214,7 +201,7 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
||||
const auto* child = params_element->FirstChildElement("param");
|
||||
if (child != nullptr) {
|
||||
if (!(cmd_itr->second.m_flags & CommandMap::flag_no_target)) {
|
||||
XmlRpc::object_to_target(xml_value_to_object(child->FirstChildElement("value")), cmd_itr->second.m_flags, &target);
|
||||
RpcManager::object_to_target(xml_value_to_object(child->FirstChildElement("value")), cmd_itr->second.m_flags, &target);
|
||||
child = child->NextSiblingElement("param");
|
||||
}
|
||||
// Parse out any other params
|
||||
@@ -228,7 +215,7 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
||||
const auto* child = params_element->FirstChildElement("data")->FirstChildElement("value");
|
||||
if (child != nullptr) {
|
||||
if (!(cmd_itr->second.m_flags & CommandMap::flag_no_target)) {
|
||||
XmlRpc::object_to_target(xml_value_to_object(child), cmd_itr->second.m_flags, &target);
|
||||
RpcManager::object_to_target(xml_value_to_object(child), cmd_itr->second.m_flags, &target);
|
||||
child = child->NextSiblingElement("value");
|
||||
}
|
||||
while (child != nullptr) {
|
||||
@@ -239,7 +226,7 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
||||
}
|
||||
}
|
||||
if (params.empty() && (cmd_itr->second.m_flags & (CommandMap::flag_file_target | CommandMap::flag_tracker_target))) {
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "invalid parameters: too few");
|
||||
throw rpc_error(XMLRPC_TYPE_ERROR, "invalid parameters: too few");
|
||||
}
|
||||
return rpc::commands.call_command(cmd_itr, params_raw, target);
|
||||
}
|
||||
@@ -247,11 +234,11 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
||||
void
|
||||
process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer) {
|
||||
if (doc->Error())
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, doc->ErrorStr());
|
||||
throw rpc_error(XMLRPC_PARSE_ERROR, doc->ErrorStr());
|
||||
if (doc->FirstChildElement("methodCall") == nullptr)
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, "methodCall element not found");
|
||||
throw rpc_error(XMLRPC_PARSE_ERROR, "methodCall element not found");
|
||||
if (doc->FirstChildElement("methodCall")->FirstChildElement("methodName") == nullptr)
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, "methodName element not found");
|
||||
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element not found");
|
||||
auto method_name = doc->FirstChildElement("methodCall")->FirstChildElement("methodName")->GetText();
|
||||
torrent::Object result;
|
||||
|
||||
@@ -276,7 +263,7 @@ process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer
|
||||
auto sub_result = torrent::Object::create_list();
|
||||
sub_result.as_list().push_back(execute_command(sub_method_name, sub_params));
|
||||
result_list.push_back(sub_result);
|
||||
} catch (xmlrpc_error& e) {
|
||||
} catch (rpc_error& e) {
|
||||
auto fault = torrent::Object::create_map();
|
||||
fault.as_map()["faultString"] = e.what();
|
||||
fault.as_map()["faultCode"] = e.type();
|
||||
@@ -357,7 +344,7 @@ XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) {
|
||||
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
||||
process_document(&doc, &printer);
|
||||
return slotWrite(printer.CStr(), printer.CStrSize() - 1);
|
||||
} catch (xmlrpc_error& e) {
|
||||
} catch (rpc_error& e) {
|
||||
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
||||
print_xmlrpc_fault(e.type(), e.what(), &printer);
|
||||
return slotWrite(printer.CStr(), printer.CStrSize() - 1);
|
||||
|
||||
Reference in New Issue
Block a user