From 3b6cdb0f923c3de8276f19a8113b49300946d1bd Mon Sep 17 00:00:00 2001 From: Daniel Gruno Date: Tue, 11 Dec 2012 20:08:24 +0000 Subject: [PATCH] mod_lua: Add a lot of core httpd/apr functionality to mod_lua (such as regex matching, expr evaluation, changing/fetching server configuration/info - see docs for a complete list). This also includes a bunch of automatically scraped functions, which may or may not be super useful. Comments appreciated as always, especially on the more hacky bits. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1420377 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/mod/mod_lua.xml | 149 ++++- modules/lua/lua_apr.c | 1049 ++++++++++++++++++++++++++++++++++- modules/lua/lua_apr.h | 12 + modules/lua/mod_lua.c | 1 + 4 files changed, 1208 insertions(+), 3 deletions(-) diff --git a/docs/manual/mod/mod_lua.xml b/docs/manual/mod/mod_lua.xml index f0175eac2d..2475aa0c87 100644 --- a/docs/manual/mod/mod_lua.xml +++ b/docs/manual/mod/mod_lua.xml @@ -631,7 +631,7 @@ end -r:parsebody([sizeLimit]) -- parse the request body as a POST and return a lua table. + r:parsebody([sizeLimit]) -- parse the request body as a POST and return a lua table. -- An optional number may be passed to specify the maximum number -- of bytes to parse. Default is 8192 bytes. @@ -647,6 +647,153 @@ r:parsebody([sizeLimit]) -- parse the request body as a POST and return a lua ta r:escape_html("<html>test</html>") -- Escapes HTML code and returns the escaped result + + + r:base64_encode(string) -- Encodes a string using the Base64 encoding standard + + + + r:base64_decode(string) -- Decodes a Base64-encoded string + + + + r:md5(string) -- Calculates and returns the MD5 digest of a string (binary safe) + + + + r:sha1(string) -- Calculates and returns the SHA1 digest of a string (binary safe) + + + + r:escape(string) -- URL-Escapes a string + + + + r:unescape(string) -- Unescapes an URL-escaped string + + + + r:banner() -- Returns the current server banner + + + + r:port() -- Returns the current server port used for the request + + + + r:mpm_query(number) -- Queries the server for MPM information using ap_mpm_query + + + + r:expr(string) -- Evaluates an expr string. + + + + r:scoreboard_process(a) -- Queries the server for information about the process at position a + + + + r:scoreboard_worker(a, b) -- Queries for information about the worker thread, b, in process a + + + + r:started() -- Returns the time of the last server (re)start + + + + r:clock() -- Returns the current time with microsecond precision + + + +r:requestbody(filename) -- Reads and returns the request body of a request. + -- If 'filename' is specified, it instead saves the + -- contents to that file. + + + + r:add_input_filter(filter_name) -- Adds 'filter_name' as an input filter + + + + r:module_info(module_name) -- Queries the server for information about a module + + + + r:loaded_modules() -- Returns a list of modules loaded by httpd + + + +r:runtime_dir_relative(filename) -- Compute the name of a run-time file (e.g., shared memory "file") + -- relative to the appropriate run-time directory. + + + + r:server_info() -- Returns a table containing server information, such as + -- the name of the httpd executable file, mpm used etc. + + + + r:set_document_root(file_path) -- Sets the document root for the request to file_path + + + + r:add_version_component(component_string) -- Adds a component to the server banner. + + + + r:set_context_info(prefix, docroot) -- Sets the context prefix and context document root for a request + + + + r:os_escape_path(file_path) -- Converts an OS path to a URL in an OS dependant way + + + + r:escape_logitem(string) -- Escapes a string for logging + + + +r:strcmp_match(string, pattern) -- Checks if 'string' matches 'pattern' using strcmp_match (GLOBs). + -- fx. whether 'www.example.com' matches '*.example.com' + + + + r:set_keepalive() -- Sets the keepalive status for a request. Returns true if possible, false otherwise. + + + + r:make_etag() -- Constructs and returns the etag for the current request. + + + +r:send_interim_response(clear) -- Sends an interim (1xx) response to the client. + -- if 'clear' is true, available headers will be sent and cleared. + + + +r:custom_response(status_code, string) -- Construct and set a custom response for a given status code. + -- This works much like the ErrorDocument directive. + + + + r:exists_config_define(string) -- Checks whether a configuration definition exists or not. + + + + r:state_query(string) -- Queries the server for state information + + + + r:stat(filename) -- Runs stat() on a file, and returns a table with file information + + + + r:regex(string, pattern) -- Runs a regular expression match on a string, returning captures if matched. + + + + r:sleep(number_of_seconds) -- Puts the script to sleep for a given number of seconds. + diff --git a/modules/lua/lua_apr.c b/modules/lua/lua_apr.c index c93ea9b1e1..569bc1ec53 100644 --- a/modules/lua/lua_apr.c +++ b/modules/lua/lua_apr.c @@ -14,12 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "apr.h" -#include "apr_tables.h" #include "mod_lua.h" #include "lua_apr.h" + /** * make a userdata out of a C pointer, and vice versa * instead of using lightuserdata @@ -88,3 +87,1049 @@ AP_LUA_DECLARE(int) ap_lua_init(lua_State *L, apr_pool_t *p) return 0; } + + +/* + ======================================================================================================================= + util_read(request_rec *r, const char **rbuf, apr_off_t *size): Reads any additional form data sent in POST/PUT + requests. + ======================================================================================================================= + */ +static int util_read(request_rec *r, const char **rbuf, apr_off_t *size) +{ + /*~~~~~~~~*/ + int rc = OK; + /*~~~~~~~~*/ + + if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) { + return (rc); + } + + if (ap_should_client_block(r)) { + + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + char argsbuffer[HUGE_STRING_LEN]; + apr_off_t rsize, len_read, rpos = 0; + apr_off_t length = r->remaining; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + *rbuf = (const char *) apr_pcalloc(r->pool, (apr_size_t) (length + 1)); + *size = length; + while ((len_read = ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0) { + if ((rpos + len_read) > length) { + rsize = length - rpos; + } + else { + rsize = len_read; + } + + memcpy((char *) *rbuf + rpos, argsbuffer, (size_t) rsize); + rpos += rsize; + } + } + + return (rc); +} + +/* + ======================================================================================================================= + util_write(request_rec *r, const char **rbuf, apr_off_t *size): Reads any additional form data sent in POST/PUT + requests and writes to a file. + ======================================================================================================================= + */ +static int util_write(request_rec *r, apr_file_t *file, apr_off_t *size) +{ + /*~~~~~~~~*/ + int rc = OK; + /*~~~~~~~~*/ + + if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) { + return (rc); + } + + if (ap_should_client_block(r)) { + + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + char argsbuffer[HUGE_STRING_LEN]; + apr_off_t rsize, len_read, rpos = 0; + apr_off_t length = r->remaining; + apr_size_t written; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + *size = length; + while ((len_read = ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0) { + if ((rpos + len_read) > length) { + rsize = (apr_size_t) length - rpos; + } + else { + rsize = len_read; + } + + rc = apr_file_write_full(file, argsbuffer, (apr_size_t) rsize, &written); + if (written != rsize) return -1; + rpos += rsize; + } + } + + return (rc); +} + +static request_rec *ap_lua_check_request_rec(lua_State *L, int index) +{ + request_rec *r; + luaL_checkudata(L, index, "Apache2.Request"); + r = (request_rec*) lua_unboxpointer(L, index); + return r; +} + +/* lua_apr_b64encode; r:encode_base64(string) - encodes a string to Base64 format */ +static int lua_apr_b64encode (lua_State *L) { + const char *plain; + char *encoded; + size_t x,y,z; + request_rec *r; + + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + plain = lua_tolstring(L, 2, &x); + y = apr_base64_encode_len(x)+1; + if (y) { + encoded = apr_palloc(r->pool, y); + z = apr_base64_encode(encoded, plain, x); + lua_pushlstring(L, encoded, z); + return 1; + } + return 0; +} + +/* lua_apr_b64decode; r:decode_base64(string) - decodes a Base64 string*/ +static int lua_apr_b64decode (lua_State *L) { + const char *encoded; + char *plain; + size_t x,y,z; + request_rec *r; + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + encoded = lua_tolstring(L, 2, &x); + y = apr_base64_decode_len(encoded)+1; + if (y) { + plain = apr_palloc(r->pool, y); + z = apr_base64_decode(plain, encoded); + lua_pushlstring(L, plain, z); + return 1; + } + return 0; +} + +/* lua_ap_unescape; r:unescape(string) - Unescapes an URL-encoded string */ +static int lua_ap_unescape (lua_State *L) { + const char *escaped; + char *plain; + size_t x,y; + request_rec *r; + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + escaped = lua_tolstring(L, 2, &x); + plain = apr_pstrdup(r->pool, escaped); + strncpy(plain, escaped, x); + y = ap_unescape_urlencoded(plain); + lua_pushstring(L, plain); + return 1; +} + +/* lua_ap_escape; r:escape(string) - URL-escapes a string */ +static int lua_ap_escape (lua_State *L) { + const char *plain; + char *escaped; + size_t x; + request_rec *r; + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + plain = lua_tolstring(L, 2, &x); + escaped = apr_pcalloc(r->pool, x*3); + escaped = ap_escape_urlencoded(r->pool, plain); + lua_pushstring(L, escaped); + return 1; +} + +/* lua_apr_md5; r:md5(string) - Calculates an MD5 digest of a string */ +static int lua_apr_md5(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~*/ + int n; + union { + char chr[16]; + uint32_t num[4]; + } digest; + apr_md5_ctx_t md5; + const char* buffer; + char* result; + char Rmd5[16]; + uint32_t *md5X; + size_t x,y; + request_rec *r; + /*~~~~~~~~~~~~~~~~*/ + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + result = apr_pcalloc(r->pool, (APR_MD5_DIGESTSIZE*2)+1); + buffer = lua_tolstring(L, 2, &y); + apr_md5_init(&md5); + apr_md5_update(&md5, buffer, y); + apr_md5_final(digest.chr, &md5); + + for (x = 0; x < 16; x += 4) { + Rmd5[x] = digest.chr[x + 3]; + Rmd5[x + 1] = digest.chr[x + 2]; + Rmd5[x + 2] = digest.chr[x + 1]; + Rmd5[x + 3] = digest.chr[x]; + } + + md5X = (uint32_t *) Rmd5; + sprintf(result, "%08x%08x%08x%08x", md5X[0], md5X[1], md5X[2], md5X[3]); + lua_pushstring(L, result); + return 1; +} + +/* lua_apr_sha1; r:sha1(string) - Calculates the SHA1 digest of a string */ +static int lua_apr_sha1(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~*/ + int n; + union { + char chr[16]; + uint32_t num[4]; + } digest; + apr_sha1_ctx_t sha1; + const char* buffer; + char* result; + char Rsha1[16]; + uint32_t *sha1X; + size_t x,y; + request_rec *r; + /*~~~~~~~~~~~~~~~~*/ + + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + result = apr_pcalloc(r->pool, (APR_SHA1_DIGESTSIZE*2)+1); + buffer = lua_tolstring(L, 2, &y); + apr_sha1_init(&sha1); + apr_sha1_update(&sha1, buffer, y); + apr_sha1_final(digest.chr, &sha1); + + for (x = 0; x < 20; x += 4) { + Rsha1[x] = digest.chr[x + 3]; + Rsha1[x + 1] = digest.chr[x + 2]; + Rsha1[x + 2] = digest.chr[x + 1]; + Rsha1[x + 3] = digest.chr[x]; + } + + sha1X = (uint32_t *) Rsha1; + sprintf(result, "%08x%08x%08x%08x%08x", sha1X[0], sha1X[1], sha1X[2], sha1X[3], sha1X[4]); + lua_pushstring(L, result); + return 1; +} + + + +/* lua_ap_banner; r:banner() - Returns the current server banner */ +static int lua_ap_banner(lua_State *L) +{ + lua_pushstring(L, ap_get_server_banner()); + return 1; +} + +/* lua_ap_port; r:port() - Returns the port used by the request */ +static int lua_ap_port(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + apr_port_t port; + /*~~~~~~~~~~~~~~~~~~*/ + r = ap_lua_check_request_rec(L, 1); + port = ap_get_server_port(r); + lua_pushnumber(L, port); + return 1; +} + +/* lua_ap_mpm_query; r:mpm_query(info) - Queries for MPM info */ +static int lua_ap_mpm_query(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + int x,y; + /*~~~~~~~~~~~~~~~~~~*/ + x = lua_tonumber(L, 1); + ap_mpm_query(x, &y); + lua_pushnumber(L, y); + return 1; +} + +/* lua_ap_expr; r:expr(string) - Evaluates an expr statement. */ +static int lua_ap_expr(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + int x = 0; + const char *expr, *err; + ap_expr_info_t res; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checktype(L, 2, LUA_TSTRING); + r = ap_lua_check_request_rec(L, 1); + expr = lua_tostring(L, 2); + + + res.filename = NULL; + res.flags = 0; + res.line_number = 0; + res.module_index = 0; + + err = ap_expr_parse(r->pool, r->pool, &res, expr, NULL); + if (!err) { + x = ap_expr_exec(r, &res, &err); + lua_pushboolean(L, x); + if (x < 0) { + lua_pushstring(L, err); + return 2; + } + return 1; + } + else { + lua_pushboolean(L, 0); + lua_pushstring(L, err); + return 2; + } + lua_pushboolean(L, 0); + return 1; +} + + +/* lua_ap_regex; r:regex(string, pattern) - Evaluates a regex and returns captures if matched */ +static int lua_ap_regex(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + int x = 0; + const char *pattern, *source, *err; + ap_regex_t regex; + ap_regmatch_t matches[10]; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checktype(L, 2, LUA_TSTRING); + luaL_checktype(L, 3, LUA_TSTRING); + r = ap_lua_check_request_rec(L, 1); + pattern = lua_tostring(L, 2); + source = lua_tostring(L, 3); + + + if (ap_regcomp(®ex, pattern,0)) { + return 0; + } + + + if (!err) { + int i; + x = ap_regexec(®ex, source, 10, matches, 0); + if (x < 0) { + lua_pushstring(L, err); + return 1; + } + lua_newtable(L); + for (i=0;i<10;i++) { + lua_pushinteger(L, i); + if (matches[i].rm_so >= 0 && matches[i].rm_eo >= 0) { + lua_pushstring(L,apr_pstrndup(r->pool, source+matches[i].rm_so, matches[i].rm_eo - matches[i].rm_so)); + } + else { + lua_pushnil(L); + } + lua_settable(L, -3); + + } + return 1; + } + return 0; +} + + + + +/* lua_ap_scoreboard_process; r:scoreboard_process(a) - returns scoreboard info */ +static int lua_ap_scoreboard_process(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + int i; + process_score* ps_record; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checktype(L, 2, LUA_TNUMBER); + r = ap_lua_check_request_rec(L, 1); + i = lua_tonumber(L, 2); + ps_record = ap_get_scoreboard_process(i); + if (ps_record) { + lua_newtable(L); + + lua_pushstring(L, "connections"); + lua_pushnumber(L, ps_record->connections); + lua_settable(L, -3); + + lua_pushstring(L, "keepalive"); + lua_pushnumber(L, ps_record->keep_alive); + lua_settable(L, -3); + + lua_pushstring(L, "lingering_close"); + lua_pushnumber(L, ps_record->lingering_close); + lua_settable(L, -3); + + lua_pushstring(L, "pid"); + lua_pushnumber(L, ps_record->pid); + lua_settable(L, -3); + + lua_pushstring(L, "suspended"); + lua_pushnumber(L, ps_record->suspended); + lua_settable(L, -3); + + lua_pushstring(L, "write_completion"); + lua_pushnumber(L, ps_record->write_completion); + lua_settable(L, -3); + + lua_pushstring(L, "not_accepting"); + lua_pushnumber(L, ps_record->not_accepting); + lua_settable(L, -3); + + lua_pushstring(L, "quiescing"); + lua_pushnumber(L, ps_record->quiescing); + lua_settable(L, -3); + + return 1; + } + return 0; +} + +/* lua_ap_scoreboard_worker; r:scoreboard_worker(proc, thread) - Returns thread info */ +static int lua_ap_scoreboard_worker(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + int i,j; + worker_score* ws_record; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checktype(L, 2, LUA_TNUMBER); + luaL_checktype(L, 3, LUA_TNUMBER); + r = ap_lua_check_request_rec(L, 1); + i = lua_tonumber(L, 2); + j = lua_tonumber(L, 3); + ws_record = ap_get_scoreboard_worker_from_indexes(i, j); + if (ws_record) { + lua_newtable(L); + + lua_pushstring(L, "access_count"); + lua_pushnumber(L, ws_record->access_count); + lua_settable(L, -3); + + lua_pushstring(L, "bytes_served"); + lua_pushnumber(L, ws_record->bytes_served); + lua_settable(L, -3); + + lua_pushstring(L, "client"); + lua_pushstring(L, ws_record->client); + lua_settable(L, -3); + + lua_pushstring(L, "conn_bytes"); + lua_pushnumber(L, ws_record->conn_bytes); + lua_settable(L, -3); + + lua_pushstring(L, "conn_count"); + lua_pushnumber(L, ws_record->conn_count); + lua_settable(L, -3); + + lua_pushstring(L, "generation"); + lua_pushnumber(L, ws_record->generation); + lua_settable(L, -3); + + lua_pushstring(L, "last_used"); + lua_pushnumber(L, ws_record->last_used); + lua_settable(L, -3); + + lua_pushstring(L, "pid"); + lua_pushnumber(L, ws_record->pid); + lua_settable(L, -3); + + lua_pushstring(L, "request"); + lua_pushstring(L, ws_record->request); + lua_settable(L, -3); + + lua_pushstring(L, "start_time"); + lua_pushnumber(L, ws_record->start_time); + lua_settable(L, -3); + + lua_pushstring(L, "status"); + lua_pushnumber(L, ws_record->status); + lua_settable(L, -3); + + lua_pushstring(L, "stop_time"); + lua_pushnumber(L, ws_record->stop_time); + lua_settable(L, -3); + + lua_pushstring(L, "tid"); + lua_pushnumber(L, ws_record->tid); + lua_settable(L, -3); + + lua_pushstring(L, "vhost"); + lua_pushstring(L, ws_record->vhost); + lua_settable(L, -3); + + lua_pushstring(L, "stimes"); + lua_pushnumber(L, ws_record->times.tms_stime); + lua_settable(L, -3); + + lua_pushstring(L, "utimes"); + lua_pushnumber(L, ws_record->times.tms_utime); + lua_settable(L, -3); + + return 1; + } + return 0; +} + +/* lua_ap_restarted; r:started() - Returns the timestamp of last server (re)start */ +static int lua_ap_restarted(lua_State *L) +{ + lua_pushnumber(L, ap_scoreboard_image->global->restart_time); + return 1; +} + +/* lua_ap_clock; r:clock() - Returns timestamp with microsecond precision*/ +static int lua_ap_clock(lua_State *L) { + apr_time_t now; + now = apr_time_now(); + lua_pushnumber(L, now); + return 1; +} + + +/* lua_ap_requestbody; r:requestbody([filename]) - Reads or stores the request body */ +static int lua_ap_requestbody(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + const char *filename; + request_rec* r; + /*~~~~~~~~~~~~~~~~~~*/ + + r = r = ap_lua_check_request_rec(L, 1); + filename = luaL_optstring(L, 2, 0); + + if (r) { + + /*~~~~~~~~~~~~~*/ + apr_off_t size; + /*~~~~~~~~~~~~~*/ + + if + ( + r->method_number != M_POST + && r->method_number != M_PUT + ) return (0); + if (!filename) { + + /*~~~~~~~~~~~~~~*/ + const char *data; + /*~~~~~~~~~~~~~~*/ + + if (util_read(r, &data, &size) != OK) { + return (0); + } + + lua_pushlstring(L, data, (size_t) size); + lua_pushinteger(L, (lua_Integer) size); + return (2); + } + else { + + /*~~~~~~~~~~~~~~~~~~*/ + apr_status_t rc; + apr_file_t *file; + /*~~~~~~~~~~~~~~~~~~*/ + + rc = apr_file_open(&file, filename, APR_CREATE | APR_FOPEN_WRITE, + APR_FPROT_OS_DEFAULT, r->pool); + lua_settop(L, 0); + if (rc == APR_SUCCESS) { + rc = util_write(r, file, &size); + apr_file_close(file); + if (rc == -1) { + return (0); + } + + lua_pushinteger(L, (lua_Integer) size); + return (1); + } + else + lua_pushboolean(L, 0); + return (1); + } + } + + return (0); +} + +/* lua_ap_add_input_filter; r:add_input_filter(name) - Adds an input filter to the chain */ +static int lua_ap_add_input_filter(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + const char* filterName; + ap_filter_rec_t *filter; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checktype(L, 2, LUA_TSTRING); + r = ap_lua_check_request_rec(L, 1); + filterName = lua_tostring(L, 2); + filter = ap_get_input_filter_handle(filterName); + if (filter) { + ap_add_input_filter_handle(filter, NULL, r, r->connection); + lua_pushboolean(L, 1); + } + else { + lua_pushboolean(L, 0); + } + return 1; +} + + +/* lua_ap_module_info; r:module_info(mod_name) - Returns information about a loaded module */ +static int lua_ap_module_info(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + const char* moduleName; + module* mod; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TSTRING); + moduleName = lua_tostring(L, 1); + mod = ap_find_linked_module(moduleName); + if (mod) { + int i = 0; + const command_rec *cmd; + lua_newtable(L); + lua_pushstring(L, "commands"); + lua_newtable(L); + for (cmd = mod->cmds; cmd->name; ++cmd) { + lua_pushstring(L, cmd->name); + lua_pushstring(L, cmd->errmsg); + lua_settable(L, -3); + } + lua_settable(L, -3); + return 1; + } + return 0; +} + +/* lua_ap_runtime_dir_relative: r:runtime_dir_relative(file): Returns the filename as relative to the runtime dir*/ +static int lua_ap_runtime_dir_relative(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + const char* file; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + file = luaL_optstring(L, 2, "."); + lua_pushstring(L, ap_runtime_dir_relative(r->pool, file)); + return 1; +} + +/* lua_ap_set_document_root; r:set_document_root(path) - sets the current doc root for the request */ +static int lua_ap_set_document_root(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + const char* root; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checktype(L, 2, LUA_TSTRING); + r = ap_lua_check_request_rec(L, 1); + root = lua_tostring(L, 2); + ap_set_document_root(r, root); + return 0; +} + +/* lua_ap_stat; r:stat(filename) - Runs stat on a file and returns the file info as a table */ +static int lua_ap_stat(lua_State *L) +{ + /*~~~~~~~~~~~~~~~~~~*/ + request_rec *r; + const char* filename; + apr_finfo_t file_info; + /*~~~~~~~~~~~~~~~~~~*/ + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checktype(L, 2, LUA_TSTRING); + r = ap_lua_check_request_rec(L, 1); + filename = lua_tostring(L, 2); + apr_stat(&file_info, filename, APR_FINFO_NORM, r->pool); + lua_newtable(L); + + lua_pushstring(L, "mtime"); + lua_pushinteger(L, file_info.mtime); + lua_settable(L, -3); + + lua_pushstring(L, "atime"); + lua_pushinteger(L, file_info.atime); + lua_settable(L, -3); + + lua_pushstring(L, "ctime"); + lua_pushinteger(L, file_info.ctime); + lua_settable(L, -3); + + lua_pushstring(L, "size"); + lua_pushinteger(L, file_info.size); + lua_settable(L, -3); + + lua_pushstring(L, "filetype"); + lua_pushinteger(L, file_info.filetype); + lua_settable(L, -3); + + return 1; +} + +/* lua_ap_loaded_modules; r:loaded_modules() - Returns a list of loaded modules */ +static int lua_ap_loaded_modules(lua_State *L) +{ + int i; + lua_newtable(L); + for (i = 0; ap_loaded_modules[i] && ap_loaded_modules[i]->name; i++) { + lua_pushinteger(L, i+1); + lua_pushstring(L, ap_loaded_modules[i]->name); + lua_settable(L, -3); + } + return 1; +} + +/* lua_ap_server_info; r:server_info() - Returns server info, such as the executable filename, server root, mpm etc*/ +static int lua_ap_server_info(lua_State *L) +{ + lua_newtable(L); + + lua_pushstring(L, "server_executable"); + lua_pushstring(L, ap_server_argv0); + lua_settable(L, -3); + + lua_pushstring(L, "server_root"); + lua_pushstring(L, ap_server_root); + lua_settable(L, -3); + + lua_pushstring(L, "scoreboard_fname"); + lua_pushstring(L, ap_scoreboard_fname); + lua_settable(L, -3); + + lua_pushstring(L, "server_mpm"); + lua_pushstring(L, ap_show_mpm()); + lua_settable(L, -3); + + return 1; +} + + +/* === Auto-scraped functions === */ + +/** + * ap_add_version_component (apr_pool_t *pconf, const char *component) + * Add a component to the server description and banner strings + * @param pconf The pool to allocate the component from + * @param component The string to add + */ +static int lua_ap_add_version_component (lua_State *L) { + + request_rec *r; + const char* component; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + component = lua_tostring(L, 2); + ap_add_version_component(r->server->process->pconf, component); + return 0; +} + + +/** + * ap_set_context_info (request_rec *r, const char *prefix, + const char *document_root) Set context_prefix and context_document_root for a request. + * @param r The request + * @param prefix the URI prefix, without trailing slash + * @param document_root the corresponding directory on disk, without trailing + * slash + * @note If one of prefix of document_root is NULL, the corrsponding + * property will not be changed. + */ +static int lua_ap_set_context_info (lua_State *L) { + + request_rec *r; + const char* prefix; + const char* document_root; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + prefix = lua_tostring(L, 2); + luaL_checktype(L, 3, LUA_TSTRING); + document_root = lua_tostring(L, 3); + ap_set_context_info(r, prefix, document_root); + return 0; +} + + +/** + * ap_os_escape_path (apr_pool_t *p, const char *path, int partial) + * convert an OS path to a URL in an OS dependant way. + * @param p The pool to allocate from + * @param path The path to convert + * @param partial if set, assume that the path will be appended to something + * with a '/' in it (and thus does not prefix "./") + * @return The converted URL + */ +static int lua_ap_os_escape_path (lua_State *L) { + + char * returnValue; + request_rec *r; + const char* path; + int partial = 0; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + path = lua_tostring(L, 2); + if ( lua_isboolean( L, 3 ) ) partial = lua_toboolean( L, 3 ); + returnValue = ap_os_escape_path(r->pool, path, partial); + lua_pushstring(L, returnValue); + return 1; +} + + +/** + * ap_escape_logitem (apr_pool_t *p, const char *str) + * Escape a string for logging + * @param p The pool to allocate from + * @param str The string to escape + * @return The escaped string + */ +static int lua_ap_escape_logitem (lua_State *L) { + + char * returnValue; + request_rec *r; + const char* str; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + str = lua_tostring(L, 2); + returnValue = ap_escape_logitem(r->pool, str); + lua_pushstring(L, returnValue); + return 1; +} + +/** + * ap_strcmp_match (const char *str, const char *expected) + * Determine if a string matches a patterm containing the wildcards '?' or '*' + * @param str The string to check + * @param expected The pattern to match against + * @return 1 if the two strings match, 0 otherwise + */ +static int lua_ap_strcmp_match (lua_State *L) { + + int returnValue; + const char* str; + const char* expected; + int ignoreCase = 0; + luaL_checktype(L, 1, LUA_TSTRING); + str = lua_tostring(L, 1); + luaL_checktype(L, 2, LUA_TSTRING); + expected = lua_tostring(L, 2); + if ( lua_isboolean( L, 3 ) ) ignoreCase = lua_toboolean( L, 3 ); + if (!ignoreCase) returnValue = ap_strcmp_match(str, expected); + else returnValue = ap_strcasecmp_match(str, expected); + lua_pushboolean(L, (!returnValue)); /* Somehow, this doesn't match the docs */ + return 1; +} + + +/** + * ap_set_keepalive (request_rec *r) + * Set the keepalive status for this request + * @param r The current request + * @return 1 if keepalive can be set, 0 otherwise + */ +static int lua_ap_set_keepalive (lua_State *L) { + + int returnValue; + request_rec *r; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + returnValue = ap_set_keepalive(r); + lua_pushboolean(L, returnValue); + return 1; +} + +/** + * ap_make_etag (request_rec *r, int force_weak) + * Construct an entity tag from the resource information. If it's a real + * file, build in some of the file characteristics. + * @param r The current request + * @param force_weak Force the entity tag to be weak - it could be modified + * again in as short an interval. + * @return The entity tag + */ +static int lua_ap_make_etag (lua_State *L) { + + char * returnValue; + request_rec *r; + int force_weak; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TBOOLEAN); + force_weak = luaL_optint(L, 2, 0); + returnValue = ap_make_etag(r, force_weak); + lua_pushstring(L, returnValue); + return 1; +} + + + +/** + * ap_send_interim_response (request_rec *r, int send_headers) + * Send an interim (HTTP 1xx) response immediately. + * @param r The request + * @param send_headers Whether to send&clear headers in r->headers_out + */ +static int lua_ap_send_interim_response (lua_State *L) { + + request_rec *r; + int send_headers; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + if ( lua_isboolean( L, 2 ) ) send_headers = lua_toboolean( L, 2 ); + ap_send_interim_response(r, send_headers); + return 0; +} + + +/** + * ap_custom_response (request_rec *r, int status, const char *string) + * Install a custom response handler for a given status + * @param r The current request + * @param status The status for which the custom response should be used + * @param string The custom response. This can be a static string, a file + * or a URL + */ +static int lua_ap_custom_response (lua_State *L) { + + request_rec *r; + int status; + const char* string; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + luaL_checktype(L, 2, LUA_TNUMBER); + status = lua_tointeger(L, 2); + luaL_checktype(L, 3, LUA_TSTRING); + string = lua_tostring(L, 3); + ap_custom_response(r, status, string); + return 0; +} + + +/** + * ap_exists_config_define (const char *name) + * Check for a definition from the server command line + * @param name The define to check for + * @return 1 if defined, 0 otherwise + */ +static int lua_ap_exists_config_define (lua_State *L) { + + int returnValue; + const char* name; + luaL_checktype(L, 1, LUA_TSTRING); + name = lua_tostring(L, 1); + returnValue = ap_exists_config_define(name); + lua_pushinteger(L, returnValue); + return 1; +} + +static int lua_ap_get_server_name_for_url (lua_State *L) { + + const char* servername; + request_rec *r; + luaL_checktype(L, 1, LUA_TUSERDATA); + r = ap_lua_check_request_rec(L, 1); + servername = ap_get_server_name_for_url(r); + lua_pushstring(L, servername); + return 1; +} + + + +/** + * ap_state_query (int query_code) item starts a new field */ +static int lua_ap_state_query (lua_State *L) { + + int returnValue; + int query_code; + luaL_checktype(L, 1, LUA_TNUMBER); + query_code = lua_tointeger(L, 1); + returnValue = ap_state_query(query_code); + lua_pushinteger(L, returnValue); + return 1; +} + +static int lua_ap_sleep (lua_State *L) { + + int msec; + luaL_checktype(L, 1, LUA_TNUMBER); + msec = (lua_tonumber(L, 1) * 1000000); + apr_sleep(msec); + return 0; +} + +static const struct luaL_Reg httpd_functions [] = { + {"base64_encode", lua_apr_b64encode}, + {"base64_decode", lua_apr_b64decode}, + {"md5", lua_apr_md5}, + {"sha1", lua_apr_sha1}, + {"escape", lua_ap_escape}, + {"unescape", lua_ap_unescape}, + {"banner", lua_ap_banner}, + {"port", lua_ap_port}, + {"mpm_query", lua_ap_mpm_query}, + {"expr", lua_ap_expr}, + {"scoreboard_process", lua_ap_scoreboard_process}, + {"scoreboard_worker", lua_ap_scoreboard_worker}, + {"started", lua_ap_restarted}, + {"clock", lua_ap_clock}, + {"requestbody", lua_ap_requestbody}, + {"add_input_filter", lua_ap_add_input_filter}, + {"module_info", lua_ap_module_info}, + {"loaded_modules", lua_ap_loaded_modules}, + {"runtime_dir_relative", lua_ap_runtime_dir_relative}, + {"server_info", lua_ap_server_info}, + {"set_document_root", lua_ap_set_document_root}, + {"add_version_component" , lua_ap_add_version_component}, + {"set_context_info" , lua_ap_set_context_info}, + {"os_escape_path" , lua_ap_os_escape_path}, + {"escape_logitem" , lua_ap_escape_logitem}, + {"strcmp_match" , lua_ap_strcmp_match}, + {"set_keepalive" , lua_ap_set_keepalive}, + {"make_etag" , lua_ap_make_etag}, + {"send_interim_response" , lua_ap_send_interim_response}, + {"custom_response" , lua_ap_custom_response}, + {"exists_config_define" , lua_ap_exists_config_define}, + {"state_query" , lua_ap_state_query}, + {"stat" , lua_ap_stat}, + {"regex" , lua_ap_regex}, + {"sleep" , lua_ap_sleep}, + {"get_server_name_for_url" , lua_ap_get_server_name_for_url}, + {NULL, NULL} /* sentinel */ +}; + +AP_LUA_DECLARE(int) ap_lua_load_httpd_functions(lua_State *L) +{ + lua_getglobal(L, "apache2"); + luaL_register(L, NULL, httpd_functions); + +} diff --git a/modules/lua/lua_apr.h b/modules/lua/lua_apr.h index c66cdde9e8..b22b3aeab2 100644 --- a/modules/lua/lua_apr.h +++ b/modules/lua/lua_apr.h @@ -18,8 +18,20 @@ #ifndef _LUA_APR_H_ #define _LUA_APR_H_ +#include "scoreboard.h" +#include "http_main.h" +#include "ap_mpm.h" +#include "apr_md5.h" +#include "apr_sha1.h" +#include "apr_poll.h" +#include "apr.h" +#include "apr_tables.h" +#include "apr_base64.h" + + AP_LUA_DECLARE(int) ap_lua_init(lua_State *L, apr_pool_t * p); AP_LUA_DECLARE(apr_table_t*) ap_lua_check_apr_table(lua_State *L, int index); AP_LUA_DECLARE(void) ap_lua_push_apr_table(lua_State *L, apr_table_t *t); +AP_LUA_DECLARE(int) ap_lua_load_httpd_functions(lua_State *L); #endif /* !_LUA_APR_H_ */ diff --git a/modules/lua/mod_lua.c b/modules/lua/mod_lua.c index 7f7bc457e9..cd9a25f9ef 100644 --- a/modules/lua/mod_lua.c +++ b/modules/lua/mod_lua.c @@ -88,6 +88,7 @@ static void lua_open_callback(lua_State *L, apr_pool_t *p, void *ctx) ap_lua_init(L, p); ap_lua_load_apache2_lmodule(L); ap_lua_load_request_lmodule(L, p); + ap_lua_load_httpd_functions(L); ap_lua_load_config_lmodule(L); } -- 2.40.0