diff --git a/include/h.h b/include/h.h index aedd31b1e..8717c6464 100644 --- a/include/h.h +++ b/include/h.h @@ -838,6 +838,8 @@ extern MODVAR int (*make_oper)(Client *client, const char *operblock_name, const extern MODVAR int (*unreal_match_iplist)(Client *client, NameList *l); extern MODVAR void (*webserver_send_response)(Client *client, int status, char *msg); extern MODVAR void (*webserver_close_client)(Client *client); +extern MODVAR void (*rpc_response)(Client *client, json_t *request, json_t *result); +extern MODVAR void (*rpc_error)(Client *client, json_t *request, const char *msg); /* /Efuncs */ /* TLS functions */ @@ -876,6 +878,8 @@ extern void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, cons extern int make_oper_default_handler(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost); extern void webserver_send_response_default_handler(Client *client, int status, char *msg); extern void webserver_close_client_default_handler(Client *client); +extern void rpc_response_default_handler(Client *client, json_t *request, json_t *result); +extern void rpc_error_default_handler(Client *client, json_t *request, const char *msg); /* End of default handlers for efunctions */ extern MODVAR MOTDFile opermotd, svsmotd, motd, botmotd, smotd, rules; @@ -1131,7 +1135,9 @@ extern void skip_whitespace(char **p); extern void read_until(char **p, char *stopchars); extern int is_ip_valid(const char *ip); extern int is_file_readable(const char *file, const char *dir); -json_t *json_string_unreal(const char *s); +extern json_t *json_string_unreal(const char *s); +extern const char *json_object_get_string(json_t *j, const char *name); +extern void json_expand_client(json_t *j, const char *key, Client *client, int detail); /* securitygroup.c start */ extern MODVAR SecurityGroup *securitygroups; extern void unreal_delete_masks(ConfigItem_mask *m); diff --git a/include/modules.h b/include/modules.h index 5e334fb1a..027d22083 100644 --- a/include/modules.h +++ b/include/modules.h @@ -616,11 +616,11 @@ typedef struct { typedef struct RPCHandler RPCHandler; struct RPCHandler { RPCHandler *prev, *next; - char *method; /**< Name of the method handler, eg "client.get" */ - int flags; /**< A flag of RPC_HANDLER_FLAG_* */ - int (*call)(Client *, json_t *request); /**< RPC call */ - Module *owner; /**< Module introducing this. */ - char unloaded; /**< Internal flag to indicate module is being unloaded */ + char *method; /**< Name of the method handler, eg "client.get" */ + int flags; /**< A flag of RPC_HANDLER_FLAG_* */ + void (*call)(Client *, json_t *request, json_t *params); /**< RPC call */ + Module *owner; /**< Module introducing this. */ + char unloaded; /**< Internal flag to indicate module is being unloaded */ }; /** The struct used to register a RPC handler. @@ -629,7 +629,7 @@ struct RPCHandler { typedef struct { char *method; int flags; - int (*call)(Client *, json_t *request); + void (*call)(Client *, json_t *request, json_t *params); } RPCHandlerInfo; /** @} */ @@ -2479,6 +2479,8 @@ enum EfunctionType { EFUNC_UNREAL_MATCH_IPLIST, EFUNC_WEBSERVER_SEND_RESPONSE, EFUNC_WEBSERVER_CLOSE_CLIENT, + EFUNC_RPC_RESPONSE, + EFUNC_RPC_ERROR, }; /* Module flags */ diff --git a/src/api-efunctions.c b/src/api-efunctions.c index 7e02bd499..6adb5d48a 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -139,6 +139,8 @@ int (*make_oper)(Client *client, const char *operblock_name, const char *opercla int (*unreal_match_iplist)(Client *client, NameList *l); void (*webserver_send_response)(Client *client, int status, char *msg); void (*webserver_close_client)(Client *client); +void (*rpc_response)(Client *client, json_t *request, json_t *result); +void (*rpc_error)(Client *client, json_t *request, const char *msg); Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()) { @@ -413,4 +415,6 @@ void efunctions_init(void) efunc_init_function(EFUNC_UNREAL_MATCH_IPLIST, unreal_match_iplist, NULL); efunc_init_function(EFUNC_WEBSERVER_SEND_RESPONSE, webserver_send_response, webserver_send_response_default_handler); efunc_init_function(EFUNC_WEBSERVER_CLOSE_CLIENT, webserver_close_client, webserver_close_client_default_handler); + efunc_init_function(EFUNC_RPC_RESPONSE, rpc_response, rpc_response_default_handler); + efunc_init_function(EFUNC_RPC_ERROR, rpc_error, rpc_error_default_handler); } diff --git a/src/log.c b/src/log.c index a2748bfe6..0c2ec9c1b 100644 --- a/src/log.c +++ b/src/log.c @@ -86,6 +86,12 @@ json_t *json_string_unreal(const char *s) return json_string(verified_s); } +const char *json_object_get_string(json_t *j, const char *name) +{ + json_t *v = json_object_get(j, name); + return v ? json_string_value(v) : NULL; +} + #define json_string __BAD___DO__NOT__USE__JSON__STRING__PLZ json_t *json_timestamp(time_t v) @@ -540,9 +546,16 @@ void json_expand_client_security_groups(json_t *parent, Client *client) void json_expand_client(json_t *j, const char *key, Client *client, int detail) { char buf[BUFSIZE+1]; - json_t *child = json_object(); + json_t *child; json_t *user = NULL; - json_object_set_new(j, key, child); + + if (key) + { + child = json_object(); + json_object_set_new(j, key, child); + } else { + child = j; + } /* First the information that is available for ALL client types: */ diff --git a/src/misc.c b/src/misc.c index f8922010b..2aec49853 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1395,11 +1395,19 @@ int make_oper_default_handler(Client *client, const char *operblock_name, const return 0; } -extern void webserver_send_response_default_handler(Client *client, int status, char *msg) +void webserver_send_response_default_handler(Client *client, int status, char *msg) { } -extern void webserver_close_client_default_handler(Client *client) +void webserver_close_client_default_handler(Client *client) +{ +} + +void rpc_response_default_handler(Client *client, json_t *request, json_t *result) +{ +} + +void rpc_error_default_handler(Client *client, json_t *request, const char *msg) { } diff --git a/src/modules/rpc/rpc.c b/src/modules/rpc/rpc.c index 350c5b118..1ddfb3eda 100644 --- a/src/modules/rpc/rpc.c +++ b/src/modules/rpc/rpc.c @@ -26,7 +26,8 @@ int rpc_handle_request_data(Client *client, WebRequest *web, const char *readbuf int rpc_packet_in(Client *client, const char *readbuf, int *length); void rpc_call_text(Client *client, const char *buf, int len); void rpc_call(Client *client, json_t *request); -void rpc_error(Client *client, json_t *request, const char *msg); +void _rpc_response(Client *client, json_t *request, json_t *result); +void _rpc_error(Client *client, json_t *request, const char *msg); /* Structs */ typedef struct RPCUser RPCUser; @@ -43,7 +44,10 @@ ModDataInfo *rpc_md; MOD_TEST() { + MARK_AS_OFFICIAL_MODULE(modinfo); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, rpc_config_test); + EfunctionAddVoid(modinfo->handle, EFUNC_RPC_RESPONSE, _rpc_response); + EfunctionAddVoid(modinfo->handle, EFUNC_RPC_ERROR, _rpc_error); return MOD_SUCCESS; } @@ -243,19 +247,40 @@ void rpc_call_text(Client *client, const char *readbuf, int len) json_decref(request); } -const char *json_object_get_string(json_t *j, const char *name) -{ - json_t *v = json_object_get(j, name); - return v ? json_string_value(v) : NULL; -} - -void rpc_error(Client *client, json_t *request, const char *msg) +void _rpc_error(Client *client, json_t *request, const char *msg) { // FIXME //json_t *response = json_object; sendto_one(client, NULL, "{ ERROR: %s }", msg); } +void _rpc_response(Client *client, json_t *request, json_t *result) +{ + const char *method = json_object_get_string(request, "method"); + json_t *id = json_object_get(request, "id"); + const char *json_serialized; + json_t *j = json_object(); + + json_object_set_new(j, "jsonrpc", json_string_unreal("2.0")); + json_object_set_new(j, "method", json_string_unreal(method)); + if (id) + json_object_set_new(j, "id", id); /* 'id' is optional */ + json_object_set_new(j, "response", result); + + json_serialized = json_dumps(j, 0); + if (!json_serialized) + { + unreal_log(ULOG_WARNING, "rpc", "BUG_RPC_RESPONSE_SERIALIZE_FAILED", NULL, + "[BUG] rpc_response() failed to serialize response " + "for request from $client ($method)", + log_data_string("method", method)); + return; + } + dbuf_put(&client->local->sendQ, json_serialized, strlen(json_serialized)); + dbuf_put(&client->local->sendQ, "\n", 1); +} + + /** Handle the RPC request: request is in JSON */ void rpc_call(Client *client, json_t *request) { @@ -292,5 +317,5 @@ void rpc_call(Client *client, json_t *request) rpc_error(client, request, "Unsupported method"); return; } - handler->call(client, request); + handler->call(client, request, params); } diff --git a/src/modules/rpc/user.c b/src/modules/rpc/user.c index 4384f0eac..8548f6100 100644 --- a/src/modules/rpc/user.c +++ b/src/modules/rpc/user.c @@ -15,7 +15,7 @@ ModuleHeader MOD_HEADER }; /* Forward declarations */ -int rpc_user_list(Client *client, json_t *request); +void rpc_user_list(Client *client, json_t *request, json_t *params); MOD_INIT() { @@ -45,8 +45,29 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int rpc_user_list(Client *client, json_t *request) +#define RPC_USER_LIST_EXPAND_NONE 0 +#define RPC_USER_LIST_EXPAND_SELECT 1 +#define RPC_USER_LIST_EXPAND_ALL 2 + +void rpc_user_list(Client *client, json_t *request, json_t *params) { - config_status("YAY! user.list() called via RPC!"); - return 0; + json_t *result, *list, *item; + Client *acptr; + + result = json_object(); + list = json_array(); + json_object_set_new(result, "list", list); + + list_for_each_entry(acptr, &client_list, client_node) + { + if (!IsUser(acptr)) + continue; + + item = json_object(); + json_expand_client(item, NULL, acptr, 1); + json_array_append_new(list, item); + } + + rpc_response(client, request, result); + json_decref(result); } diff --git a/src/modules/webserver.c b/src/modules/webserver.c index bc5522187..214631257 100644 --- a/src/modules/webserver.c +++ b/src/modules/webserver.c @@ -46,6 +46,7 @@ ModDataInfo *webserver_md; MOD_TEST() { + MARK_AS_OFFICIAL_MODULE(modinfo); EfunctionAddVoid(modinfo->handle, EFUNC_WEBSERVER_SEND_RESPONSE, _webserver_send_response); EfunctionAddVoid(modinfo->handle, EFUNC_WEBSERVER_CLOSE_CLIENT, _webserver_close_client); return MOD_SUCCESS;