/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 "mod_wombat.h" #include "apr_lua.h" void rstack_dump(lua_State* L, request_rec* r, const char* msg) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Lua Stack Dump: [%s]", msg); int i; int top = lua_gettop(L); for (i = 1; i<= top; i++) { int t = lua_type(L, i); switch(t) { case LUA_TSTRING: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: '%s'", i, lua_tostring(L, i)); break; } case LUA_TUSERDATA: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: userdata", i); break; } case LUA_TLIGHTUSERDATA: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: lightuserdata", i); break; } case LUA_TNIL: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: NIL", i); break; } case LUA_TNONE: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: None", i); break; } case LUA_TBOOLEAN: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: %s", i, lua_toboolean(L, i) ? "true" : "false"); break; } case LUA_TNUMBER: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: %g", i, lua_tonumber(L, i)); break; } case LUA_TTABLE: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: ", i); break; } default: { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%d: unkown: [%s]", i, lua_typename(L, i)); break; } } } } /** * Verify that the thing at index is a request_rec wrapping * userdata thingamajig and return it if it is. if it is not * lua will enter its error handling routine. */ static request_rec* check_request_rec(lua_State* L, int index) { luaL_checkudata(L, index, "Apache2.Request"); request_rec* r = (request_rec*)lua_unboxpointer(L, index); return r; } /* ------------------ request methods -------------------- */ // helper callback for req_parseargs static int req_aprtable2luatable_cb(void *l, const char *key, const char *value) { lua_State* L = (lua_State*)l; // [table, table] // rstack_dump(L, RRR, "start of cb"); // L is [table, table] // build complex lua_getfield(L, -1, key); // [VALUE, table, table] // rstack_dump(L, RRR, "after getfield"); int t = lua_type(L, -1); switch(t) { case LUA_TNIL: case LUA_TNONE: { lua_pop(L, 1); // [table, table] lua_newtable(L); // [array, table, table] lua_pushnumber(L, 1); // [1, array, table, table] lua_pushstring(L, value); // [string, 1, array, table, table] lua_settable(L, -3); // [array, table, table] lua_setfield(L, -2, key); // [table, table] break; } case LUA_TTABLE: { // [array, table, table] int size = lua_objlen(L, -1); lua_pushnumber(L, size + 1); // [#, array, table, table] lua_pushstring(L, value); // [string, #, array, table, table] lua_settable(L, -3); // [array, table, table] lua_setfield(L, -2, key); // [table, table] break; } } // L is [table, table] // build simple lua_getfield(L, -2, key); // [VALUE, table, table] if (lua_isnoneornil(L, -1)) { // only set if not already set lua_pop(L, 1); // [table, table]] lua_pushstring(L, value); // [string, table, table] lua_setfield(L, -3, key); // [table, table] } else { lua_pop(L, 1); } return 1; } // r:parseargs() returning a lua table static int req_parseargs(lua_State* L) { request_rec* r = check_request_rec(L, 1); apreq_handle_t* h = apreq_handle_apache2(r); lua_newtable(L); lua_newtable(L); // [table, table] const apr_table_t* form_table; if (apreq_args(h, &form_table) == APR_SUCCESS) { apr_table_do(req_aprtable2luatable_cb, L, form_table, NULL); } return 2; // [table, table] } // wrap ap_rputs as r:puts(String) static int req_puts(lua_State* L) { request_rec* r = check_request_rec(L, 1); int argc = lua_gettop(L); int i; for (i=2;i<=argc;i++) { ap_rputs(luaL_checkstring(L, i), r); } return 0; } // wrap ap_rwrite as r:write(String) static int req_write(lua_State* L) { request_rec* r = check_request_rec(L, 1); size_t n; const char* buf = luaL_checklstring(L, 2, &n); ap_rwrite((void *)buf, n, r); return 0; } // r:parsebody() static int req_parsebody(lua_State* L) { request_rec* r = check_request_rec(L, 1); apreq_handle_t* h = apreq_handle_apache2(r); lua_newtable(L); lua_newtable(L); const apr_table_t* form_table; if (apreq_body(h, &form_table) == APR_SUCCESS) { apr_table_do(req_aprtable2luatable_cb, L, form_table, NULL); } return 2; } // r:addoutputfilter(name|function) static int req_add_output_filter(lua_State *L) { request_rec* r = check_request_rec(L, 1); const char *name = luaL_checkstring(L, 2); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "adding output filter %s", name); ap_add_output_filter(name, L, r, r->connection); return 0; } // helper function for the logging functions below static int req_log_at(lua_State* L, int level) { request_rec* r = check_request_rec(L, 1); lua_Debug dbg; lua_getstack(L, 1, &dbg); lua_getinfo(L, "Sl", &dbg); const char* msg = luaL_checkstring(L, 2); ap_log_rerror(dbg.source, dbg.currentline, level, 0, r, msg); return 0; } // r:debug(String) and friends which use apache logging static int req_emerg(lua_State* L) { req_log_at(L, APLOG_EMERG); return 0; } static int req_alert(lua_State* L) { req_log_at(L, APLOG_ALERT); return 0; } static int req_crit(lua_State* L) { req_log_at(L, APLOG_CRIT); return 0; } static int req_err(lua_State* L) { req_log_at(L, APLOG_ERR); return 0; } static int req_warn(lua_State* L) { req_log_at(L, APLOG_WARNING); return 0; } static int req_notice(lua_State* L) { req_log_at(L, APLOG_NOTICE); return 0; } static int req_info(lua_State* L) { req_log_at(L, APLOG_INFO); return 0; } static int req_debug(lua_State* L) { req_log_at(L, APLOG_DEBUG); return 0; } // handle r.status = 201 static int req_newindex(lua_State* L) { request_rec* r = lua_touserdata(L, lua_upvalueindex(1)); const char* key = luaL_checkstring(L, -2); if (0 == apr_strnatcmp("status", key)) { int code = luaL_checkinteger(L, -1); r->status = code; luaL_getmetatable(L, "Apache2.Request"); lua_pushinteger(L, code); lua_setfield(L, -2, "status"); lua_pop(L, 1); return 0; } if (0 == apr_strnatcmp("content_type", key)) { const char* value = luaL_checkstring(L, -1); r->content_type = apr_pstrdup(r->pool, value); luaL_getmetatable(L, "Apache2.Request"); lua_pushstring(L, value); lua_setfield(L, -2, "content_type"); lua_pop(L, 1); return 0; } if (0 == apr_strnatcmp("filename", key)) { const char* value = luaL_checkstring(L, -1); r->filename = apr_pstrdup(r->pool, value); luaL_getmetatable(L, "Apache2.Request"); lua_pushstring(L, value); lua_setfield(L, -2, "filename"); lua_pop(L, 1); return 0; } if (0 == apr_strnatcmp("uri", key)) { const char* value = luaL_checkstring(L, -1); r->uri = apr_pstrdup(r->pool, value); luaL_getmetatable(L, "Apache2.Request"); lua_pushstring(L, value); lua_setfield(L, -2, "uri"); lua_pop(L, 1); return 0; } lua_pushstring(L, apr_psprintf(r->pool, "Property [%s] may not be set on a request_rec", key)); lua_error(L); return 0; } static const struct luaL_Reg request_methods[] = { {"puts", req_puts}, {"write", req_write}, {"parseargs", req_parseargs}, {"parsebody", req_parsebody}, // Log methods {"debug", req_debug}, {"info", req_info}, {"notice", req_notice}, {"warn", req_warn}, {"err", req_err}, {"crit", req_crit}, {"alert", req_alert}, {"emerg", req_emerg}, {NULL, NULL} }; static const struct luaL_Reg connection_methods[] = { {NULL, NULL} }; static const struct luaL_Reg server_methods[] = { {NULL, NULL} }; void apw_load_request_lmodule(lua_State *L) { luaL_newmetatable(L, "Apache2.Request"); // [metatable] lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_register(L, NULL, request_methods); // [metatable] lua_pop(L, 2); luaL_newmetatable(L, "Apache2.Connection"); // [metatable] lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_register(L, NULL, connection_methods); // [metatable] lua_pop(L, 2); luaL_newmetatable(L, "Apache2.Server"); // [metatable] lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_register(L, NULL, server_methods); // [metatable] lua_pop(L, 2); } void apw_push_connection(lua_State* L, conn_rec* c) { lua_boxpointer(L, c); luaL_getmetatable(L, "Apache2.Connection"); lua_setmetatable(L, -2); luaL_getmetatable(L, "Apache2.Connection"); apw_push_apr_table(L, "notes", c->notes); lua_pushstring(L, c->remote_ip); lua_setfield(L, -2, "remote_ip"); lua_pop(L, 1); } void apw_push_server(lua_State* L, server_rec* s) { lua_boxpointer(L, s); luaL_getmetatable(L, "Apache2.Server"); lua_setmetatable(L, -2); luaL_getmetatable(L, "Apache2.Server"); lua_pushstring(L, s->server_hostname); lua_setfield(L, -2, "server_hostname"); lua_pop(L, 1); } void apw_push_request(lua_State* L, request_rec* r) { lua_boxpointer(L, r); luaL_getmetatable(L, "Apache2.Request"); lua_setmetatable(L, -2); luaL_getmetatable(L, "Apache2.Request"); lua_pushinteger(L, r->status); lua_setfield(L, -2, "status"); lua_pushstring(L, r->method); lua_setfield(L, -2, "method"); lua_pushstring(L, r->the_request); lua_setfield(L, -2, "the_request"); lua_pushstring(L, r->protocol); lua_setfield(L, -2, "protocol"); lua_pushstring(L, r->range); lua_setfield(L, -2, "range"); lua_pushstring(L, r->content_type); lua_setfield(L, -2, "content_type"); lua_pushstring(L, r->content_encoding); lua_setfield(L, -2, "content_encoding"); lua_pushstring(L, r->user); lua_setfield(L, -2, "user"); lua_pushstring(L, r->ap_auth_type); lua_setfield(L, -2, "ap_auth_type"); lua_pushstring(L, r->unparsed_uri); lua_setfield(L, -2, "unparsed_uri"); lua_pushstring(L, r->uri); lua_setfield(L, -2, "uri"); lua_pushstring(L, r->filename); lua_setfield(L, -2, "filename"); lua_pushstring(L, r->canonical_filename); lua_setfield(L, -2, "canonical_filename"); lua_pushstring(L, r->path_info); lua_setfield(L, -2, "path_info"); lua_pushstring(L, r->args); lua_setfield(L, -2, "args"); lua_pushstring(L, r->hostname); lua_setfield(L, -2, "hostname"); lua_pushboolean(L, r->assbackwards); lua_setfield(L, -2, "assbackwards"); lua_pushcfunction(L, &req_add_output_filter); lua_setfield(L, -2, "addoutputfilter"); apw_push_apr_table(L, "subprocess_env", r->subprocess_env); apw_push_apr_table(L, "headers_out", r->headers_out); apw_push_apr_table(L, "headers_in", r->headers_in); apw_push_apr_table(L, "notes", r->notes); apw_push_connection(L, r->connection); lua_setfield(L, -2, "connection"); apw_push_server(L, r->server); lua_setfield(L, -2, "server"); lua_pushlightuserdata(L, r); lua_pushcclosure(L, &req_newindex, 1); lua_setfield(L, -2, "__newindex"); lua_pop(L, 1); }