/
niceSOFT
/
dovecot
Обзор
Документация
Войти
/
niceSOFT
/
dovecot
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/lib-lua/dlua-dovecot-http.c
552 строки
15 KB
Michael M Slusarz
COPYING: Simplify and standardize copyright handling
03 июл 2026, 02:25
03 июл 2026, 02:25
2a5809c
Код
Авторство
О чём код?
/* Copyright (c) Dovecot authors, see top-level COPYING file */ #include "lib.h" #include "ioloop.h" #include "str.h" #include "dlua-script-private.h" #include "http-url.h" #include "http-client.h" #include "http-client-private.h" #include "istream.h" #include "iostream-ssl.h" #include "settings.h" #include "settings-parser.h" #include "master-service.h" #include "master-service-settings.h" #include "dns-lookup.h" #define DLUA_DOVECOT_HTTP "http" #define DLUA_HTTP_CLIENT "struct http_client" #define DLUA_HTTP_CLIENT_REQUEST "struct http_client_request" #define DLUA_HTTP_RESPONSE "struct dlua_http_response" struct dlua_http_response { unsigned char version_major; unsigned char version_minor; unsigned int status; const char *reason; const char *location; string_t *payload; time_t date, retry_after; ARRAY_TYPE(http_header_field) headers; pool_t pool; const char *error; struct event *event; }; struct dlua_http_response_payload_context { struct io *io; struct istream *payload_istream; string_t *payload_str; char *error; struct event *event; pool_t pool; }; static struct http_client_request * dlua_check_http_request(lua_State *L, int arg) { if (!lua_istable(L, arg)) { (void)luaL_error(L, "Bad argument #%d, expected %s got %s", arg, DLUA_HTTP_CLIENT_REQUEST, lua_typename(L, lua_type(L, arg))); } lua_pushliteral(L, "item"); lua_rawget(L, arg); struct http_client_request **bp = lua_touserdata(L, -1); lua_pop(L, 1); return *bp; } static int dlua_http_request_gc(lua_State *L) { struct http_client_request **req = lua_touserdata(L, 1); http_client_request_unref(req); return 0; } static int dlua_http_request_add_header(lua_State *L) { DLUA_REQUIRE_ARGS(L, 3); struct http_client_request *req = dlua_check_http_request(L, 1); const char *name = luaL_checkstring(L, 2); const char *value = luaL_checkstring(L, 3); http_client_request_add_header(req, name, value); return 0; } static int dlua_http_request_remove_header(lua_State *L) { DLUA_REQUIRE_ARGS(L, 2); struct http_client_request *req = dlua_check_http_request(L, 1); const char *name = luaL_checkstring(L, 2); http_client_request_remove_header(req, name); return 0; } static int dlua_http_request_set_payload(lua_State *L) { DLUA_REQUIRE_ARGS_IN(L, 2, 3); struct http_client_request *req = dlua_check_http_request(L, 1); struct istream *payload_istream; const char *payload = luaL_checkstring(L, 2); bool do_sync = FALSE; if (lua_gettop(L) >= 3) do_sync = lua_toboolean(L, 3); payload_istream = i_stream_create_copy_from_data(payload, strlen(payload)); http_client_request_set_payload(req, payload_istream, do_sync); i_stream_unref(&payload_istream); return 0; } static int dlua_http_request_submit(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); struct http_client_request *req = dlua_check_http_request(L, 1); /* Clear the GC hook for this request. It will be freed after it's submitted. */ lua_getfield(L, -1, "item"); if (lua_getmetatable(L, -1) != 1) return luaL_error(L, "Couldn't get metatable for the request"); lua_pushnil(L); lua_setfield(L, -2, "__gc"); lua_pop(L, 2); http_client_request_submit(req); http_client_wait(req->client); return 1; } static int dlua_http_request_set_event(lua_State *L) { DLUA_REQUIRE_ARGS(L, 2); struct http_client_request *req = dlua_check_http_request(L, 1); struct event *event = dlua_check_event(L, 2); http_client_request_set_event(req, event); return 0; } static int dlua_http_request_get_event(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); struct http_client_request *req = dlua_check_http_request(L, 1); dlua_push_event(L, req->event); return 1; } static luaL_Reg lua_dovecot_http_request_methods[] = { { "add_header", dlua_http_request_add_header }, { "remove_header", dlua_http_request_remove_header }, { "set_payload", dlua_http_request_set_payload }, { "submit", dlua_http_request_submit }, { "set_event", dlua_http_request_set_event }, { "get_event", dlua_http_request_get_event }, { NULL, NULL } }; static void dlua_push_http_request(lua_State *L, struct http_client_request *req) { luaL_checkstack(L, 3, "out of memory"); lua_createtable(L, 0, 1); luaL_setmetatable(L, DLUA_HTTP_CLIENT_REQUEST); /* we need to attach gc to userdata to support older lua*/ struct http_client_request **ptr = lua_newuserdata(L, sizeof(struct http_client_request*)); *ptr = req; lua_createtable(L, 0, 1); lua_pushcfunction(L, dlua_http_request_gc); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); lua_setfield(L, -2, "item"); luaL_setfuncs(L, lua_dovecot_http_request_methods, 0); } static struct http_client *dlua_check_http_client(lua_State *L, int arg) { if (!lua_istable(L, arg)) { (void)luaL_error(L, "Bad argument #%d, expected %s got %s", arg, DLUA_HTTP_CLIENT, lua_typename(L, lua_type(L, arg))); } lua_pushliteral(L, "item"); lua_rawget(L, arg); struct http_client **bp = lua_touserdata(L, -1); lua_pop(L, 1); return *bp; } static int dlua_http_client_gc(lua_State *L) { struct http_client **_client = lua_touserdata(L, 1); struct event *event = event_get_parent((*_client)->event); struct settings_instance *instance = event_get_ptr(event, SETTINGS_EVENT_INSTANCE); i_assert(instance != NULL); settings_instance_free(&instance); http_client_deinit(_client); return 0; } static int dlua_http_resp_gc(lua_State *L) { struct dlua_http_response **_resp = lua_touserdata(L, 1); array_free(&(*_resp)->headers); pool_unref(&(*_resp)->pool); return 0; } static struct dlua_http_response *dlua_check_http_response(lua_State *L, int arg) { if (!lua_istable(L, arg)) { (void)luaL_error(L, "Bad argument #%d, expected %s got %s", arg, DLUA_HTTP_RESPONSE, lua_typename(L, lua_type(L, arg))); } lua_pushliteral(L, "item"); lua_rawget(L, arg); struct dlua_http_response **bp = lua_touserdata(L, -1); lua_pop(L, 1); return *bp; } static int dlua_http_response_get_status(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); const struct dlua_http_response *resp = dlua_check_http_response(L, 1); lua_pushinteger(L, resp->status); return 1; } static int dlua_http_response_get_payload(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); const struct dlua_http_response *resp = dlua_check_http_response(L, 1); lua_pushlstring(L, resp->payload->data, resp->payload->used); return 1; } static int dlua_http_response_get_header(lua_State *L) { DLUA_REQUIRE_ARGS(L, 2); const struct dlua_http_response *resp = dlua_check_http_response(L, 1); const char *name = luaL_checkstring(L, 2); const char *value = ""; const struct http_header_field *hfield; array_foreach(&resp->headers, hfield) { if (http_header_field_is(hfield, name)) { value = hfield->value; break; } } lua_pushstring(L, value); return 1; } static int dlua_http_response_get_reason(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); const struct dlua_http_response *resp = dlua_check_http_response(L, 1); lua_pushstring(L, resp->reason); return 1; } static const luaL_Reg dovecot_http_response_methods[] = { { "status", dlua_http_response_get_status }, { "payload", dlua_http_response_get_payload }, { "header", dlua_http_response_get_header }, { "reason", dlua_http_response_get_reason }, { NULL, NULL } }; static void dlua_push_http_response(lua_State *L, const struct dlua_http_response *resp) { luaL_checkstack(L, 3, "out of memory"); lua_createtable(L, 0, 1); luaL_setmetatable(L, DLUA_HTTP_RESPONSE); /* we need to attach gc to userdata to support older lua*/ const struct dlua_http_response **ptr = lua_newuserdata(L, sizeof(struct dlua_http_response*)); *ptr = resp; lua_createtable(L, 0, 1); lua_pushcfunction(L, dlua_http_resp_gc); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); lua_setfield(L, -2, "item"); luaL_setfuncs(L, dovecot_http_response_methods, 0); } static void dlua_http_response_input_payload(struct dlua_http_response_payload_context *ctx) { const unsigned char *data; size_t size; int ret; /* read payload */ while ((ret=i_stream_read_more(ctx->payload_istream, &data, &size)) > 0) { str_append_data(ctx->payload_str, data, size); i_stream_skip(ctx->payload_istream, size); } if (ctx->payload_istream->stream_errno != 0) { ctx->error = p_strdup_printf(ctx->pool, "Response payload read error: %s", i_stream_get_error(ctx->payload_istream)); } if (ret == 0) { e_debug(ctx->event, "DEBUG: REQUEST: NEED MORE DATA"); /* we will be called again for this request */ } else { if (ctx->payload_istream->stream_errno != 0) { e_error(ctx->event, "ERROR: REQUEST PAYLOAD READ ERROR: %s", i_stream_get_error(ctx->payload_istream)); } else e_debug(ctx->event, "DEBUG: REQUEST: Finished"); io_remove(&ctx->io); i_free(ctx); } } static void dlua_http_response_read_payload(const struct http_response *response, struct dlua_http_response *dlua_resp) { struct dlua_http_response_payload_context *ctx = i_new(struct dlua_http_response_payload_context ,1); ctx->payload_istream = response->payload; ctx->io = io_add_istream(response->payload, dlua_http_response_input_payload, ctx); ctx->payload_str = dlua_resp->payload; ctx->pool = dlua_resp->pool; ctx->event = dlua_resp->event; dlua_http_response_input_payload(ctx); } static void dlua_http_request_callback(const struct http_response *response, lua_State *L) { struct dlua_script *script = dlua_script_from_state(L); /* we need a keep a copy of http_response, otherwise the data will be * lost when the object is freed. */ pool_t pool = pool_alloconly_create("http_response", 1024); struct dlua_http_response *resp = p_new(pool, struct dlua_http_response, 1); resp->pool = pool; resp->date = response->date; resp->version_major = response->version_major; resp->version_minor = response->version_minor; resp->status = response->status; resp->reason = p_strdup(resp->pool, response->reason); resp->location = p_strdup(resp->pool, response->location); resp->date = response->date; resp->retry_after = response->retry_after; resp->payload = str_new(resp->pool, 528); resp->event = script->event; p_array_init(&resp->headers, resp->pool, 2); const ARRAY_TYPE(http_header_field) *hdrs; const struct http_header_field *hdr; struct http_header_field *hdr_cpy; hdrs = http_response_header_get_fields(response); if (hdrs != NULL) { array_foreach(hdrs, hdr) { hdr_cpy = array_append_space(&resp->headers); hdr_cpy->name = p_strdup(resp->pool, hdr->name); hdr_cpy->size = hdr->size; hdr_cpy->value = p_strdup(resp->pool, hdr->value); } } if (response->payload != NULL) { /* got payload */ dlua_http_response_read_payload(response, resp); } dlua_push_http_response(L, resp); } static int dlua_http_request_new(lua_State *L) { DLUA_REQUIRE_ARGS(L, 2); const char *url, *method = "GET"; struct http_client_request *http_req; struct http_url *http_url; const char *error; struct http_client *client = dlua_check_http_client(L, 1); luaL_checktype(L, 2, LUA_TTABLE); lua_getfield(L, -1, "url"); if (lua_isnil(L, -1)) return luaL_error(L, "cannot create request: url not specified"); else url = luaL_checkstring(L, -1); lua_pop(L, 1); lua_getfield(L, -1, "method"); if (!lua_isnil(L, -1)) method = luaL_checkstring(L, -1); lua_pop(L, 1); if (http_url_parse(url, NULL, HTTP_URL_ALLOW_USERINFO_PART, pool_datastack_create(), &http_url, &error) < 0) { return luaL_error(L, "Failed to parse url %s: %s", url, error); return -1; } http_req = http_client_request_url(client, method, http_url, dlua_http_request_callback, L); dlua_push_http_request(L, http_req); return 1; } static const luaL_Reg dovecot_http_client_methods[] = { { "request", dlua_http_request_new }, { NULL, NULL } }; static void dlua_push_http_client(lua_State *L, struct http_client *client) { luaL_checkstack(L, 3, "out of memory"); lua_createtable(L, 0, 1); luaL_setmetatable(L, DLUA_HTTP_CLIENT); /* we need to attach gc to userdata to support older lua*/ struct http_client **ptr = lua_newuserdata(L, sizeof(struct http_client*)); *ptr = client; lua_createtable(L, 0, 1); lua_pushcfunction(L, dlua_http_client_gc); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); lua_setfield(L, -2, "item"); luaL_setfuncs(L, dovecot_http_client_methods, 0); } static int parse_client_settings(lua_State *L, struct settings_instance *instance, const char **error_r) { if (!lua_istable(L, -1)) { *error_r = t_strdup_printf("Table expected"); return -1; } lua_pushnil(L); *error_r = NULL; while (*error_r == NULL && lua_next(L, -2) != 0) { const char *key = lua_tostring(L, -2); const char *value = lua_tostring(L, -1); const char *real_key; unsigned int idx ATTR_UNUSED; /* ignore event_parent */ if (strcmp(key, "event_parent") == 0) { lua_pop(L, 1); continue; } real_key = t_strconcat("http_client_", key, NULL); if (setting_parser_info_find_key(&http_client_setting_parser_info, real_key, &idx)) { settings_override(instance, real_key, value, SETTINGS_OVERRIDE_TYPE_CODE); } else if (setting_parser_info_find_key(&ssl_setting_parser_info, key, &idx)) { settings_override(instance, key, value, SETTINGS_OVERRIDE_TYPE_CODE); } else if (setting_parser_info_find_key(&dns_client_setting_parser_info, key, &idx)) { settings_override(instance, key, value, SETTINGS_OVERRIDE_TYPE_CODE); } else { *error_r = t_strdup_printf("%s is unknown setting", key); } lua_pop(L, 1); } if (*error_r != NULL) return -1; return 0; } static int dlua_http_client_new(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); luaL_checktype(L, 1, LUA_TTABLE); struct dlua_script *script = dlua_script_from_state(L); struct event *event_parent = script->event; if (dlua_table_get_by_str(L, 1, LUA_TTABLE, "event_parent") == 1) { event_parent = dlua_check_event(L, -1); lua_pop(L, 1); } struct http_client *client; struct event *event = event_create(event_parent); const char *error; struct settings_root *root = settings_root_find(event); struct settings_instance *instance = settings_instance_new(root); bool fail = FALSE; event_set_ptr(event, SETTINGS_EVENT_INSTANCE, instance); if (parse_client_settings(L, instance, &error) < 0 || http_client_init_auto(event, &client, &error) < 0) fail = TRUE; event_unref(&event); if (fail) { settings_instance_free(&instance); /* Convert the error into something readable by dropping out several prefixes and the http_client_ prefix from the setting name, so that it will match what was provided in the constructor. */ (void)str_begins(error, "http_client settings: Failed to override configuration from hardcoded: Invalid http_client_", &error); return luaL_error(L, "Invalid HTTP client setting: %s", error); } dlua_push_http_client(L, client); return 1; } static const luaL_Reg dovecot_http_methods[] = { { "client", dlua_http_client_new }, { NULL, NULL } }; void dlua_dovecot_http_register(struct dlua_script *script) { i_assert(script != NULL); lua_State *L = script->L; /* push dovecot table on the stack */ dlua_get_dovecot(L); /* populate http methods in a table and add them as dovecot.http */ lua_newtable(L); luaL_setfuncs(L, dovecot_http_methods, 0); lua_setfield(script->L, -2, DLUA_DOVECOT_HTTP); lua_pop(script->L, 1); }