Skip to content

Commit 6e26e1b

Browse files
committed
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
1 parent 7891574 commit 6e26e1b

30 files changed

+26374
-306
lines changed

src/Makefile.am

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ libsub_root_a_SOURCES = \
109109
rpc/ip_table_list.h \
110110
rpc/lua.h \
111111
rpc/lua.cc \
112+
rpc/jsonrpc.cc \
113+
rpc/jsonrpc.h \
114+
rpc/rpc_manager.cc \
115+
rpc/rpc_manager.h \
112116
rpc/object_storage.cc \
113117
rpc/object_storage.h \
114118
rpc/parse.cc \
@@ -127,6 +131,7 @@ libsub_root_a_SOURCES = \
127131
rpc/xmlrpc_tinyxml2.cc \
128132
rpc/tinyxml2/tinyxml2.h \
129133
rpc/tinyxml2/tinyxml2.cc \
134+
rpc/nlohmann/json.h \
130135
\
131136
ui/download.cc \
132137
ui/download.h \
@@ -157,6 +162,7 @@ libsub_root_a_SOURCES = \
157162
ui/root.cc \
158163
ui/root.h \
159164
\
165+
utils/base64.cc \
160166
utils/base64.h \
161167
utils/directory.cc \
162168
utils/directory.h \

src/command_dynamic.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
187187
if (!(flags & rpc::object_storage::flag_static))
188188
cmd_flags |= rpc::CommandMap::flag_modifiable;
189189
if (!(flags & rpc::object_storage::flag_private))
190-
cmd_flags |= rpc::CommandMap::flag_public_xmlrpc;
190+
cmd_flags |= rpc::CommandMap::flag_public_rpc;
191191

192192
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_list_type) {
193193
torrent::Object valueList = torrent::Object::create_list();
@@ -360,7 +360,7 @@ system_method_redirect(const torrent::Object::list_type& args) {
360360
std::string new_key = torrent::object_create_string(args.front());
361361
std::string dest_key = torrent::object_create_string(args.back());
362362

363-
rpc::commands.create_redirect(new_key, dest_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_modifiable);
363+
rpc::commands.create_redirect(new_key, dest_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_modifiable);
364364

365365
return torrent::Object();
366366
}

src/command_helpers.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ void initialize_commands();
4949

5050
#define CMD2_A_FUNCTION(key, function, slot, parm, doc) \
5151
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::function>::type>(key, slot, &rpc::function, \
52-
rpc::CommandMap::flag_dont_delete | rpc::CommandMap::flag_public_xmlrpc, NULL, NULL);
52+
rpc::CommandMap::flag_dont_delete | rpc::CommandMap::flag_public_rpc, NULL, NULL);
5353

5454
#define CMD2_A_FUNCTION_PRIVATE(key, function, slot, parm, doc) \
5555
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::function>::type>(key, slot, &rpc::function, \
@@ -137,19 +137,19 @@ void initialize_commands();
137137
std::placeholders::_1, std::placeholders::_2));
138138

139139
#define CMD2_REDIRECT(from_key, to_key) \
140-
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_dont_delete);
140+
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_dont_delete);
141141
#define CMD2_REDIRECT_GENERIC(from_key, to_key) \
142-
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_no_target | rpc::CommandMap::flag_dont_delete);
142+
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_no_target | rpc::CommandMap::flag_dont_delete);
143143
#define CMD2_REDIRECT_GENERIC_NO_EXPORT(from_key, to_key) \
144144
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_no_target | rpc::CommandMap::flag_dont_delete);
145145
#define CMD2_REDIRECT_FILE(from_key, to_key) \
146-
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_file_target | rpc::CommandMap::flag_dont_delete);
146+
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_file_target | rpc::CommandMap::flag_dont_delete);
147147
#define CMD2_REDIRECT_TRACKER(from_key, to_key) \
148-
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete);
148+
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete);
149149

150150
#define CMD2_REDIRECT_GENERIC_STR(from_key, to_key) \
151151
rpc::commands.create_redirect(from_key, to_key, \
152-
rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_no_target);
152+
rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_no_target);
153153

154154
#define CMD2_REDIRECT_GENERIC_STR_NO_EXPORT(from_key, to_key) \
155155
rpc::commands.create_redirect(from_key, to_key, \

src/command_logging.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,6 @@ initialize_command_logging() {
142142

143143
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
144144
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
145-
CMD2_ANY_STRING_V("log.xmlrpc", std::bind(&ThreadWorker::set_xmlrpc_log, worker_thread, std::placeholders::_2));
145+
CMD2_ANY_STRING_V("log.rpc", std::bind(&ThreadWorker::set_rpc_log, worker_thread, std::placeholders::_2));
146+
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
146147
}

src/command_network.cc

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ apply_tos(const torrent::Object::string_type& arg) {
9595
torrent::Object apply_encoding_list(const std::string& arg) { torrent::encoding_list()->push_back(arg); return torrent::Object(); }
9696

9797
torrent::File*
98-
xmlrpc_find_file(core::Download* download, uint32_t index) {
98+
rpc_find_file(core::Download* download, uint32_t index) {
9999
if (index >= download->file_list()->size_files())
100100
return NULL;
101101

@@ -104,15 +104,15 @@ xmlrpc_find_file(core::Download* download, uint32_t index) {
104104

105105
// Ergh... time to update the Tracker API to allow proper ptrs.
106106
torrent::Tracker*
107-
xmlrpc_find_tracker(core::Download* download, uint32_t index) {
107+
rpc_find_tracker(core::Download* download, uint32_t index) {
108108
if (index >= download->tracker_list()->size())
109109
return NULL;
110110

111111
return download->tracker_list()->at(index);
112112
}
113113

114114
torrent::Peer*
115-
xmlrpc_find_peer(core::Download* download, const torrent::HashString& hash) {
115+
rpc_find_peer(core::Download* download, const torrent::HashString& hash) {
116116
torrent::ConnectionList::iterator itr = download->connection_list()->find(hash.c_str());
117117

118118
if (itr == download->connection_list()->end())
@@ -122,32 +122,31 @@ xmlrpc_find_peer(core::Download* download, const torrent::HashString& hash) {
122122
}
123123

124124
void
125-
initialize_xmlrpc() {
126-
rpc::xmlrpc.initialize();
127-
rpc::xmlrpc.slot_find_download() = std::bind(&core::DownloadList::find_hex_ptr, control->core()->download_list(), std::placeholders::_1);
128-
rpc::xmlrpc.slot_find_file() = std::bind(&xmlrpc_find_file, std::placeholders::_1, std::placeholders::_2);
129-
rpc::xmlrpc.slot_find_tracker() = std::bind(&xmlrpc_find_tracker, std::placeholders::_1, std::placeholders::_2);
130-
rpc::xmlrpc.slot_find_peer() = std::bind(&xmlrpc_find_peer, std::placeholders::_1, std::placeholders::_2);
125+
initialize_rpc() {
126+
rpc::rpc.initialize();
127+
rpc::rpc.slot_find_download() = [](const char* hash) { return control->core()->download_list()->find_hex_ptr(hash); };
128+
rpc::rpc.slot_find_file() = [](core::Download* d, uint32_t index) { return rpc_find_file(d, index); };
129+
rpc::rpc.slot_find_tracker() = [](core::Download* d, uint32_t index) { return rpc_find_tracker(d, index); };
130+
rpc::rpc.slot_find_peer() = [](core::Download* d, const torrent::HashString& hash) { return rpc_find_peer(d, hash); };
131131

132132
unsigned int count = 0;
133133

134134
for (rpc::CommandMap::const_iterator itr = rpc::commands.begin(), last = rpc::commands.end(); itr != last; itr++, count++) {
135-
if (!(itr->second.m_flags & rpc::CommandMap::flag_public_xmlrpc))
135+
if (!(itr->second.m_flags & rpc::CommandMap::flag_public_rpc))
136136
continue;
137137

138-
rpc::xmlrpc.insert_command(itr->first.c_str(), itr->second.m_parm, itr->second.m_doc);
138+
rpc::rpc.insert_command(itr->first.c_str(), itr->second.m_parm, itr->second.m_doc);
139139
}
140140

141-
lt_log_print(torrent::LOG_RPC_EVENTS, "XMLRPC initialized with %u functions.", count);
141+
lt_log_print(torrent::LOG_RPC_EVENTS, "RPC initialized with %u functions.", count);
142142
}
143143

144144
torrent::Object
145145
apply_scgi(const std::string& arg, int type) {
146146
if (worker_thread->scgi() != NULL)
147147
throw torrent::input_error("SCGI already enabled.");
148148

149-
if (!rpc::xmlrpc.is_valid())
150-
initialize_xmlrpc();
149+
initialize_rpc();
151150

152151
rpc::SCgi* scgi = new rpc::SCgi;
153152

@@ -225,7 +224,7 @@ apply_xmlrpc_dialect(const std::string& arg) {
225224
else
226225
value = -1;
227226

228-
rpc::xmlrpc.set_dialect(value);
227+
rpc::rpc.set_dialect(value);
229228
return torrent::Object();
230229
}
231230

@@ -298,9 +297,12 @@ initialize_command_network() {
298297
CMD2_ANY_STRING ("network.scgi.open_local", std::bind(&apply_scgi, std::placeholders::_2, 2));
299298
CMD2_VAR_BOOL ("network.scgi.dont_route", false);
300299

301-
CMD2_ANY_STRING ("network.xmlrpc.dialect.set", std::bind(&apply_xmlrpc_dialect, std::placeholders::_2));
302-
CMD2_ANY ("network.xmlrpc.size_limit", std::bind(&rpc::XmlRpc::size_limit, rpc::xmlrpc));
303-
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", std::bind(&rpc::XmlRpc::set_size_limit, rpc::xmlrpc, std::placeholders::_2));
300+
CMD2_ANY_STRING ("network.xmlrpc.dialect.set", [](const auto&, const auto& arg) { return apply_xmlrpc_dialect(arg); })
301+
CMD2_ANY ("network.xmlrpc.size_limit", [](const auto&, const auto&){ return rpc::rpc.size_limit(); });
302+
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](const auto&, const auto& arg){ return rpc::rpc.set_size_limit(arg); });
303+
304+
CMD2_VAR_BOOL ("network.rpc.use_xmlrpc", true);
305+
CMD2_VAR_BOOL ("network.rpc.use_jsonrpc", true);
304306

305307
CMD2_ANY ("network.block.ipv4", std::bind(&torrent::ConnectionManager::is_block_ipv4, cm));
306308
CMD2_ANY_VALUE_V ("network.block.ipv4.set", std::bind(&torrent::ConnectionManager::set_block_ipv4, cm, std::placeholders::_2));

src/control.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Control::initialize() {
9393
void
9494
Control::cleanup() {
9595
// delete m_scgi; m_scgi = NULL;
96-
rpc::xmlrpc.cleanup();
96+
rpc::rpc.cleanup();
9797

9898
priority_queue_erase(&taskScheduler, &m_taskShutdown);
9999

src/core/manager.cc

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include "rpc/parse_commands.h"
2525
#include "utils/directory.h"
26+
#include "utils/base64.h"
2627
#include "utils/file_status_cache.h"
2728

2829
#include "globals.h"
@@ -313,13 +314,29 @@ Manager::receive_http_failed(std::string msg) {
313314
push_log_std("Http download error: \"" + msg + "\"");
314315
}
315316

317+
bool
318+
is_data_uri(const std::string& uri) {
319+
return std::strncmp(uri.c_str(), "data:", 5) == 0;
320+
}
321+
322+
std::string
323+
decode_data_uri(const std::string& uri) {
324+
const auto start = uri.find("base64,", 5) + 7;
325+
if (start == std::string::npos)
326+
throw torrent::input_error("Invalid data uri: not base64 encoded.");
327+
if (start >= uri.size())
328+
throw torrent::input_error("Empty base64.");
329+
return utils::decode_base64(uri.substr(start));
330+
}
331+
316332
void
317333
Manager::try_create_download(const std::string& uri, int flags, const command_list_type& commands) {
318334
// If the path was attempted loaded before, skip it.
319335
if ((flags & create_tied) &&
320336
!(flags & create_raw_data) &&
321337
!is_network_uri(uri) &&
322338
!is_magnet_uri(uri) &&
339+
!is_data_uri(uri) &&
323340
!file_status_cache()->insert(uri, 0))
324341
return;
325342

@@ -333,10 +350,16 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
333350
f->set_print_log(!(flags & create_quiet));
334351
f->slot_finished([f]() { delete f; });
335352

336-
if (flags & create_raw_data)
353+
if (is_data_uri(uri)) {
354+
// Allow the use of data URIs, primarily for JSON-RPC which
355+
// doesn't have a defined mechanism for binary data
356+
f->load_raw_data(decode_data_uri(uri));
357+
f->variables()["tied_to_file"] = (int64_t)false;
358+
} else if (flags & create_raw_data) {
337359
f->load_raw_data(uri);
338-
else
360+
} else {
339361
f->load(uri);
362+
}
340363

341364
f->commit();
342365
}

src/rpc/command_map.cc

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@
4848
#include "command.h"
4949
#include "command_map.h"
5050

51-
// For XMLRPC stuff, clean up.
52-
#include "xmlrpc.h"
51+
#include "rpc_manager.h"
5352
#include "parse_commands.h"
5453

5554
namespace rpc {
@@ -64,8 +63,8 @@ CommandMap::insert(const key_type& key, int flags, const char* parm, const char*
6463
throw torrent::internal_error("CommandMap::insert(...) tried to insert an already existing key.");
6564

6665
// TODO: This is not honoring the public_xmlrpc flags!!!
67-
if (rpc::xmlrpc.is_valid() && (flags & flag_public_xmlrpc))
68-
rpc::xmlrpc.insert_command(key.c_str(), parm, doc);
66+
if (rpc::rpc.is_initialized() && (flags & flag_public_rpc))
67+
rpc::rpc.insert_command(key.c_str(), parm, doc);
6968

7069
return base_type::insert(itr, value_type(key, command_map_data_type(flags, parm, doc)));
7170
}
@@ -102,11 +101,11 @@ CommandMap::create_redirect(const key_type& key_new, const key_type& key_dest, i
102101

103102
dest_itr->second.m_flags |= flag_has_redirects;
104103

105-
flags |= dest_itr->second.m_flags & ~(flag_has_redirects | flag_public_xmlrpc);
104+
flags |= dest_itr->second.m_flags & ~(flag_has_redirects | flag_public_rpc);
106105

107106
// TODO: This is not honoring the public_xmlrpc flags!!!
108-
if (rpc::xmlrpc.is_valid() && (flags & flag_public_xmlrpc))
109-
rpc::xmlrpc.insert_command(key_new.c_str(), dest_itr->second.m_parm, dest_itr->second.m_doc);
107+
if (rpc::rpc.is_initialized() && (flags & flag_public_rpc))
108+
rpc::rpc.insert_command(key_new.c_str(), dest_itr->second.m_parm, dest_itr->second.m_doc);
110109

111110
iterator itr = base_type::insert(base_type::end(),
112111
value_type(key_new, command_map_data_type(flags,

src/rpc/command_map.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class CommandMap : public std::map<std::string, command_map_data_type> {
8686
using base_type::find;
8787

8888
static const int flag_dont_delete = 0x1;
89-
static const int flag_public_xmlrpc = 0x4;
89+
static const int flag_public_rpc = 0x4;
9090
static const int flag_modifiable = 0x10;
9191
static const int flag_is_redirect = 0x20;
9292
static const int flag_has_redirects = 0x40;

0 commit comments

Comments
 (0)