123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796 |
- /*-
- * Copyright 2016 Vsevolod Stakhov
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "lua_common.h"
-
-
- LUA_FUNCTION_DEF(xmlrpc, parse_reply);
- LUA_FUNCTION_DEF(xmlrpc, make_request);
-
- static const struct luaL_reg xmlrpclib_m[] = {
- LUA_INTERFACE_DEF(xmlrpc, parse_reply),
- LUA_INTERFACE_DEF(xmlrpc, make_request),
- {"__tostring", rspamd_lua_class_tostring},
- {NULL, NULL}};
-
- #define msg_debug_xmlrpc(...) rspamd_conditional_debug_fast(NULL, NULL, \
- rspamd_xmlrpc_log_id, "xmlrpc", "", \
- RSPAMD_LOG_FUNC, \
- __VA_ARGS__)
-
- INIT_LOG_MODULE(xmlrpc)
-
- enum lua_xmlrpc_state {
- read_method_response = 0,
- read_params = 1,
- read_param = 2,
- read_param_value = 3,
- read_param_element = 4,
- read_struct = 5,
- read_struct_member_name = 6,
- read_struct_member_value = 7,
- read_struct_element = 8,
- read_string = 9,
- read_int = 10,
- read_double = 11,
- read_array = 12,
- read_array_value = 13,
- read_array_element = 14,
- error_state = 99,
- success_state = 100,
- };
-
- enum lua_xmlrpc_stack {
- st_array = 1,
- st_struct = 2,
- };
-
- struct lua_xmlrpc_ud {
- enum lua_xmlrpc_state parser_state;
- GQueue *st;
- int param_count;
- gboolean got_text;
- lua_State *L;
- };
-
- static void xmlrpc_start_element(GMarkupParseContext *context,
- const char *name,
- const char **attribute_names,
- const char **attribute_values,
- gpointer user_data,
- GError **error);
- static void xmlrpc_end_element(GMarkupParseContext *context,
- const char *element_name,
- gpointer user_data,
- GError **error);
- static void xmlrpc_error(GMarkupParseContext *context,
- GError *error,
- gpointer user_data);
- static void xmlrpc_text(GMarkupParseContext *context,
- const char *text,
- gsize text_len,
- gpointer user_data,
- GError **error);
-
- static GMarkupParser xmlrpc_parser = {
- .start_element = xmlrpc_start_element,
- .end_element = xmlrpc_end_element,
- .passthrough = NULL,
- .text = xmlrpc_text,
- .error = xmlrpc_error,
- };
-
- static GQuark
- xmlrpc_error_quark(void)
- {
- return g_quark_from_static_string("xmlrpc-error-quark");
- }
-
- static void
- xmlrpc_start_element(GMarkupParseContext *context,
- const char *name,
- const char **attribute_names,
- const char **attribute_values,
- gpointer user_data,
- GError **error)
- {
- struct lua_xmlrpc_ud *ud = user_data;
- enum lua_xmlrpc_state last_state;
-
- last_state = ud->parser_state;
-
- msg_debug_xmlrpc("got start element %s on state %d", name, last_state);
-
- switch (ud->parser_state) {
- case read_method_response:
- /* Expect tag methodResponse */
- if (g_ascii_strcasecmp(name, "methodResponse") == 0) {
- ud->parser_state = read_params;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_params:
- /* Expect tag params */
- if (g_ascii_strcasecmp(name, "params") == 0) {
- ud->parser_state = read_param;
- /* result -> table of params indexed by int */
- lua_newtable(ud->L);
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_param:
- /* Expect tag param */
- if (g_ascii_strcasecmp(name, "param") == 0) {
- ud->parser_state = read_param_value;
- /* Create new param */
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_param_value:
- /* Expect tag value */
- if (g_ascii_strcasecmp(name, "value") == 0) {
- ud->parser_state = read_param_element;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_param_element:
- /* Expect tag struct */
- if (g_ascii_strcasecmp(name, "struct") == 0) {
- ud->parser_state = read_struct;
- /* Create new param of table type */
- lua_newtable(ud->L);
- g_queue_push_head(ud->st, GINT_TO_POINTER(st_struct));
- msg_debug_xmlrpc("push struct");
- }
- else if (g_ascii_strcasecmp(name, "array") == 0) {
- ud->parser_state = read_array;
- /* Create new param of table type */
- lua_newtable(ud->L);
- g_queue_push_head(ud->st, GINT_TO_POINTER(st_array));
- msg_debug_xmlrpc("push array");
- }
- else if (g_ascii_strcasecmp(name, "string") == 0) {
- ud->parser_state = read_string;
- ud->got_text = FALSE;
- }
- else if (g_ascii_strcasecmp(name, "int") == 0) {
- ud->parser_state = read_int;
- ud->got_text = FALSE;
- }
- else if (g_ascii_strcasecmp(name, "double") == 0) {
- ud->parser_state = read_double;
- ud->got_text = FALSE;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_struct:
- /* Parse structure */
- /* Expect tag member */
- if (g_ascii_strcasecmp(name, "member") == 0) {
- ud->parser_state = read_struct_member_name;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_struct_member_name:
- /* Expect tag name */
- if (g_ascii_strcasecmp(name, "name") == 0) {
- ud->parser_state = read_struct_member_value;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_struct_member_value:
- /* Accept value */
- if (g_ascii_strcasecmp(name, "value") == 0) {
- ud->parser_state = read_struct_element;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_struct_element:
- /* Parse any values */
- /* Primitives */
- if (g_ascii_strcasecmp(name, "string") == 0) {
- ud->parser_state = read_string;
- ud->got_text = FALSE;
- }
- else if (g_ascii_strcasecmp(name, "int") == 0) {
- ud->parser_state = read_int;
- ud->got_text = FALSE;
- }
- else if (g_ascii_strcasecmp(name, "double") == 0) {
- ud->parser_state = read_double;
- ud->got_text = FALSE;
- }
- /* Structure */
- else if (g_ascii_strcasecmp(name, "struct") == 0) {
- ud->parser_state = read_struct;
- /* Create new param of table type */
- lua_newtable(ud->L);
- g_queue_push_head(ud->st, GINT_TO_POINTER(st_struct));
- msg_debug_xmlrpc("push struct");
- }
- else if (g_ascii_strcasecmp(name, "array") == 0) {
- ud->parser_state = read_array;
- /* Create new param of table type */
- lua_newtable(ud->L);
- g_queue_push_head(ud->st, GINT_TO_POINTER(st_array));
- msg_debug_xmlrpc("push array");
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_array:
- /* Parse array */
- /* Expect data */
- if (g_ascii_strcasecmp(name, "data") == 0) {
- ud->parser_state = read_array_value;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_array_value:
- /* Accept array value */
- if (g_ascii_strcasecmp(name, "value") == 0) {
- ud->parser_state = read_array_element;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_array_element:
- /* Parse any values */
- /* Primitives */
- if (g_ascii_strcasecmp(name, "string") == 0) {
- ud->parser_state = read_string;
- ud->got_text = FALSE;
- }
- else if (g_ascii_strcasecmp(name, "int") == 0) {
- ud->parser_state = read_int;
- ud->got_text = FALSE;
- }
- else if (g_ascii_strcasecmp(name, "double") == 0) {
- ud->parser_state = read_double;
- ud->got_text = FALSE;
- }
- /* Structure */
- else if (g_ascii_strcasecmp(name, "struct") == 0) {
- ud->parser_state = read_struct;
- /* Create new param of table type */
- lua_newtable(ud->L);
- g_queue_push_head(ud->st, GINT_TO_POINTER(st_struct));
- msg_debug_xmlrpc("push struct");
- }
- else if (g_ascii_strcasecmp(name, "array") == 0) {
- ud->parser_state = read_array;
- /* Create new param of table type */
- lua_newtable(ud->L);
- g_queue_push_head(ud->st, GINT_TO_POINTER(st_array));
- msg_debug_xmlrpc("push array");
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- default:
- break;
- }
-
- msg_debug_xmlrpc("switched state on start tag %d->%d", last_state,
- ud->parser_state);
-
- if (ud->parser_state == error_state) {
- g_set_error(error,
- xmlrpc_error_quark(), 1, "xmlrpc parse error on state: %d, while parsing start tag: %s",
- last_state, name);
- }
- }
-
- static void
- xmlrpc_end_element(GMarkupParseContext *context,
- const char *name,
- gpointer user_data,
- GError **error)
- {
- struct lua_xmlrpc_ud *ud = user_data;
- enum lua_xmlrpc_state last_state;
- int last_queued;
-
- last_state = ud->parser_state;
-
- msg_debug_xmlrpc("got end element %s on state %d", name, last_state);
-
- switch (ud->parser_state) {
- case read_method_response:
- ud->parser_state = error_state;
- break;
- case read_params:
- /* Got methodResponse */
- if (g_ascii_strcasecmp(name, "methodResponse") == 0) {
- /* End processing */
- ud->parser_state = success_state;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_param:
- /* Got tag params */
- if (g_ascii_strcasecmp(name, "params") == 0) {
- ud->parser_state = read_params;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_param_value:
- /* Got tag param */
- if (g_ascii_strcasecmp(name, "param") == 0) {
- ud->parser_state = read_param;
- lua_rawseti(ud->L, -2, ++ud->param_count);
- msg_debug_xmlrpc("set param element idx: %d", ud->param_count);
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_param_element:
- /* Got tag value */
- if (g_ascii_strcasecmp(name, "value") == 0) {
- if (g_queue_get_length(ud->st) == 0) {
- ud->parser_state = read_param_value;
- }
- else {
- if (GPOINTER_TO_INT(g_queue_peek_head(ud->st)) == st_struct) {
- ud->parser_state = read_struct_member_name;
- }
- else {
- ud->parser_state = read_array_value;
- }
- }
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_struct:
- /* Got tag struct */
- if (g_ascii_strcasecmp(name, "struct") == 0) {
- g_assert(GPOINTER_TO_INT(g_queue_pop_head(ud->st)) == st_struct);
-
- if (g_queue_get_length(ud->st) == 0) {
- ud->parser_state = read_param_element;
- }
- else {
- last_queued = GPOINTER_TO_INT(g_queue_peek_head(ud->st));
- if (last_queued == st_struct) {
- ud->parser_state = read_struct_element;
- }
- else {
- ud->parser_state = read_array_element;
- }
- }
-
- msg_debug_xmlrpc("pop struct");
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_struct_member_name:
- /* Got tag member */
- if (g_ascii_strcasecmp(name, "member") == 0) {
- ud->parser_state = read_struct;
- /* Set table */
- msg_debug_xmlrpc("set struct element idx: %s",
- lua_tostring(ud->L, -2));
- lua_settable(ud->L, -3);
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_struct_member_value:
- /* Got tag name */
- if (g_ascii_strcasecmp(name, "name") == 0) {
- ud->parser_state = read_struct_member_value;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_struct_element:
- /* Got tag value */
- if (g_ascii_strcasecmp(name, "value") == 0) {
- ud->parser_state = read_struct_member_name;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_string:
- case read_int:
- case read_double:
- /* Parse any values */
- /* Handle empty tags */
- if (!ud->got_text) {
- lua_pushnil(ud->L);
- }
- else {
- ud->got_text = FALSE;
- }
- /* Primitives */
- if (g_ascii_strcasecmp(name, "string") == 0 ||
- g_ascii_strcasecmp(name, "int") == 0 ||
- g_ascii_strcasecmp(name, "double") == 0) {
- if (GPOINTER_TO_INT(g_queue_peek_head(ud->st)) == st_struct) {
- ud->parser_state = read_struct_element;
- }
- else {
- ud->parser_state = read_array_element;
- }
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_array:
- /* Got tag array */
- if (g_ascii_strcasecmp(name, "array") == 0) {
- g_assert(GPOINTER_TO_INT(g_queue_pop_head(ud->st)) == st_array);
-
- if (g_queue_get_length(ud->st) == 0) {
- ud->parser_state = read_param_element;
- }
- else {
- last_queued = GPOINTER_TO_INT(g_queue_peek_head(ud->st));
- if (last_queued == st_struct) {
- ud->parser_state = read_struct_element;
- }
- else {
- ud->parser_state = read_array_element;
- }
- }
-
- msg_debug_xmlrpc("pop array");
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_array_value:
- /* Got tag data */
- if (g_ascii_strcasecmp(name, "data") == 0) {
- ud->parser_state = read_array;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- case read_array_element:
- /* Got tag value */
- if (g_ascii_strcasecmp(name, "value") == 0) {
- unsigned int tbl_len = rspamd_lua_table_size(ud->L, -2);
- lua_rawseti(ud->L, -2, tbl_len + 1);
- msg_debug_xmlrpc("set array element idx: %d", tbl_len + 1);
- ud->parser_state = read_array_value;
- }
- else {
- /* Error state */
- ud->parser_state = error_state;
- }
- break;
- default:
- break;
- }
-
- msg_debug_xmlrpc("switched state on end tag %d->%d",
- last_state, ud->parser_state);
-
- if (ud->parser_state == error_state) {
- g_set_error(error,
- xmlrpc_error_quark(), 1, "xmlrpc parse error on state: %d, while parsing end tag: %s",
- last_state, name);
- }
- }
-
- static void
- xmlrpc_text(GMarkupParseContext *context,
- const char *text,
- gsize text_len,
- gpointer user_data,
- GError **error)
- {
- struct lua_xmlrpc_ud *ud = user_data;
- gulong num;
- double dnum;
-
- /* Strip line */
- while (text_len > 0 && g_ascii_isspace(*text)) {
- text++;
- text_len--;
- }
- while (text_len > 0 && g_ascii_isspace(text[text_len - 1])) {
- text_len--;
- }
-
- if (text_len > 0) {
- msg_debug_xmlrpc("got data on state %d", ud->parser_state);
- switch (ud->parser_state) {
- case read_struct_member_value:
- /* Push key */
- lua_pushlstring(ud->L, text, text_len);
- break;
- case read_string:
- /* Push string value */
- lua_pushlstring(ud->L, text, text_len);
- break;
- case read_int:
- /* Push integer value */
- rspamd_strtoul(text, text_len, &num);
- lua_pushinteger(ud->L, num);
- break;
- case read_double:
- /* Push integer value */
- dnum = strtod(text, NULL);
- lua_pushnumber(ud->L, dnum);
- break;
- default:
- break;
- }
- ud->got_text = TRUE;
- }
- }
-
- static void
- xmlrpc_error(GMarkupParseContext *context, GError *error, gpointer user_data)
- {
- msg_err("xmlrpc parser error: %s", error->message);
- }
-
- static int
- lua_xmlrpc_parse_reply(lua_State *L)
- {
- LUA_TRACE_POINT;
- const char *data;
- GMarkupParseContext *ctx;
- GError *err = NULL;
- struct lua_xmlrpc_ud ud;
- gsize s;
- gboolean res;
-
- data = luaL_checklstring(L, 1, &s);
-
- if (data != NULL) {
- ud.L = L;
- ud.parser_state = read_method_response;
- ud.param_count = 0;
- ud.st = g_queue_new();
-
- ctx = g_markup_parse_context_new(&xmlrpc_parser,
- G_MARKUP_TREAT_CDATA_AS_TEXT, &ud, NULL);
- res = g_markup_parse_context_parse(ctx, data, s, &err);
-
- g_markup_parse_context_free(ctx);
- if (!res) {
- lua_pushnil(L);
- }
- }
- else {
- lua_pushnil(L);
- }
-
- /* Return table or nil */
- return 1;
- }
-
- static int
- lua_xmlrpc_parse_table(lua_State *L,
- int pos,
- char *databuf,
- int pr,
- gsize size)
- {
- int r = pr, num;
- double dnum;
-
- r += rspamd_snprintf(databuf + r, size - r, "<struct>");
- lua_pushnil(L); /* first key */
- while (lua_next(L, pos) != 0) {
- /* uses 'key' (at index -2) and 'value' (at index -1) */
- if (lua_type(L, -2) != LUA_TSTRING) {
- /* Ignore non sting keys */
- lua_pop(L, 1);
- continue;
- }
- r += rspamd_snprintf(databuf + r,
- size - r,
- "<member><name>%s</name><value>",
- lua_tostring(L, -2));
- switch (lua_type(L, -1)) {
- case LUA_TNUMBER:
- num = lua_tointeger(L, -1);
- dnum = lua_tonumber(L, -1);
-
- /* Try to avoid conversion errors */
- if (dnum != (double) num) {
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "<double>%f</double>",
- dnum);
- }
- else {
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "<int>%d</int>",
- num);
- }
- break;
- case LUA_TBOOLEAN:
- r += rspamd_snprintf(databuf + r,
- size - r,
- "<boolean>%d</boolean>",
- lua_toboolean(L, -1) ? 1 : 0);
- break;
- case LUA_TSTRING:
- r += rspamd_snprintf(databuf + r, size - r, "<string>%s</string>",
- lua_tostring(L, -1));
- break;
- case LUA_TTABLE:
- /* Recursive call */
- r += lua_xmlrpc_parse_table(L, -1, databuf + r, r, size);
- break;
- }
- r += rspamd_snprintf(databuf + r, size - r, "</value></member>");
- /* removes 'value'; keeps 'key' for next iteration */
- lua_pop(L, 1);
- }
- r += rspamd_snprintf(databuf + r, size - r, "</struct>");
-
- return r - pr;
- }
-
- /*
- * Internal limitation: xmlrpc request must NOT be more than
- * BUFSIZ * 2 (16384 bytes)
- */
- static int
- lua_xmlrpc_make_request(lua_State *L)
- {
- LUA_TRACE_POINT;
- char databuf[BUFSIZ * 2];
- const char *func;
- int r, top, i, num;
- double dnum;
-
- func = luaL_checkstring(L, 1);
-
- if (func) {
- r = rspamd_snprintf(databuf, sizeof(databuf),
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
- "<methodCall><methodName>%s</methodName><params>",
- func);
- /* Extract arguments */
- top = lua_gettop(L);
- /* Get additional options */
- for (i = 2; i <= top; i++) {
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "<param><value>");
- switch (lua_type(L, i)) {
- case LUA_TNUMBER:
- num = lua_tointeger(L, i);
- dnum = lua_tonumber(L, i);
-
- /* Try to avoid conversion errors */
- if (dnum != (double) num) {
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "<double>%f</double>",
- dnum);
- }
- else {
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "<int>%d</int>",
- num);
- }
- break;
- case LUA_TBOOLEAN:
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "<boolean>%d</boolean>",
- lua_toboolean(L, i) ? 1 : 0);
- break;
- case LUA_TSTRING:
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "<string>%s</string>",
- lua_tostring(L, i));
- break;
- case LUA_TTABLE:
- r +=
- lua_xmlrpc_parse_table(L, i, databuf, r, sizeof(databuf));
- break;
- }
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "</value></param>");
- }
-
- r += rspamd_snprintf(databuf + r,
- sizeof(databuf) - r,
- "</params></methodCall>");
- lua_pushlstring(L, databuf, r);
- }
- else {
- lua_pushnil(L);
- }
-
- return 1;
- }
-
- static int
- lua_load_xmlrpc(lua_State *L)
- {
- lua_newtable(L);
- luaL_register(L, NULL, xmlrpclib_m);
-
- return 1;
- }
-
- void luaopen_xmlrpc(lua_State *L)
- {
- rspamd_lua_add_preload(L, "rspamd_xmlrpc", lua_load_xmlrpc);
- }
|