summaryrefslogtreecommitdiff
path: root/data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c
diff options
context:
space:
mode:
Diffstat (limited to 'data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c')
-rw-r--r--data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c325
1 files changed, 325 insertions, 0 deletions
diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c b/data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c
new file mode 100644
index 000000000..41b554363
--- /dev/null
+++ b/data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c
@@ -0,0 +1,325 @@
+#include "first.h"
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+
+#include "mod_cml_funcs.h"
+#include "mod_cml.h"
+
+#include "base.h"
+#include "chunk.h"
+#include "log.h"
+#include "http_header.h"
+#include "response.h"
+#include "stat_cache.h"
+
+#define HASHLEN 16
+typedef unsigned char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+
+static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
+ int curelem = lua_gettop(L);
+ int result;
+
+ lua_getglobal(L, varname);
+
+ if (lua_isstring(L, curelem)) {
+ buffer_copy_string(b, lua_tostring(L, curelem));
+ result = 0;
+ } else {
+ result = -1;
+ }
+
+ lua_pop(L, 1);
+ force_assert(curelem == lua_gettop(L));
+ return result;
+}
+
+static int lua_to_c_is_table(lua_State *L, const char *varname) {
+ int curelem = lua_gettop(L);
+ int result;
+
+ lua_getglobal(L, varname);
+
+ result = lua_istable(L, curelem) ? 1 : 0;
+
+ lua_pop(L, 1);
+ force_assert(curelem == lua_gettop(L));
+ return result;
+}
+
+static int c_to_lua_push(lua_State *L, int tbl, const char *key, size_t key_len, const char *val, size_t val_len) {
+ lua_pushlstring(L, key, key_len);
+ lua_pushlstring(L, val, val_len);
+ lua_settable(L, tbl);
+
+ return 0;
+}
+
+static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
+ size_t is_key = 1;
+ size_t i, len, klen = 0;
+ char *key = NULL, *val = NULL;
+
+ if (buffer_string_is_empty(qrystr)) return 0;
+ key = qrystr->ptr;
+
+ /* we need the \0 */
+ len = buffer_string_length(qrystr);
+ for (i = 0; i <= len; i++) {
+ switch(qrystr->ptr[i]) {
+ case '=':
+ if (is_key) {
+ val = qrystr->ptr + i + 1;
+ klen = (size_t)(val - key - 1);
+ is_key = 0;
+ }
+
+ break;
+ case '&':
+ case '\0': /* fin symbol */
+ if (!is_key) {
+ /* we need at least a = since the last & */
+ c_to_lua_push(L, tbl,
+ key, klen,
+ val, (size_t)(qrystr->ptr + i - val));
+ }
+
+ key = qrystr->ptr + i + 1;
+ val = NULL;
+ is_key = 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
+ lua_State *L;
+ int ret = -1;
+ buffer *b;
+
+ b = buffer_init();
+ /* push the lua file to the interpreter and see what happends */
+ L = luaL_newstate();
+ luaL_openlibs(L);
+
+ /* register functions */
+ lua_register(L, "md5", f_crypto_md5);
+ lua_register(L, "file_mtime", f_file_mtime);
+ lua_register(L, "file_isreg", f_file_isreg);
+ lua_register(L, "file_isdir", f_file_isreg);
+ lua_register(L, "dir_files", f_dir_files);
+
+#ifdef USE_MEMCACHED
+ lua_pushlightuserdata(L, p->conf.memc);
+ lua_pushcclosure(L, f_memcache_get_long, 1);
+ lua_setglobal(L, "memcache_get_long");
+
+ lua_pushlightuserdata(L, p->conf.memc);
+ lua_pushcclosure(L, f_memcache_get_string, 1);
+ lua_setglobal(L, "memcache_get_string");
+
+ lua_pushlightuserdata(L, p->conf.memc);
+ lua_pushcclosure(L, f_memcache_exists, 1);
+ lua_setglobal(L, "memcache_exists");
+#endif
+
+ /* register CGI environment */
+ lua_newtable(L);
+ {
+ int header_tbl = lua_gettop(L);
+
+ c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
+ c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
+ c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
+ c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
+ if (!buffer_string_is_empty(con->request.pathinfo)) {
+ c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
+ }
+
+ c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
+ c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
+ }
+ lua_setglobal(L, "request");
+
+ /* register GET parameter */
+ lua_newtable(L);
+ cache_export_get_params(L, lua_gettop(L), con->uri.query);
+ lua_setglobal(L, "get");
+
+ /* 2 default constants */
+ lua_pushinteger(L, 0);
+ lua_setglobal(L, "CACHE_HIT");
+
+ lua_pushinteger(L, 1);
+ lua_setglobal(L, "CACHE_MISS");
+
+ /* load lua program */
+ ret = luaL_loadfile(L, fn->ptr);
+ if (0 != ret) {
+ log_error_write(srv, __FILE__, __LINE__, "sbsS",
+ "failed loading cml_lua script",
+ fn,
+ ":",
+ lua_tostring(L, -1));
+ goto error;
+ }
+
+ if (lua_pcall(L, 0, 1, 0)) {
+ log_error_write(srv, __FILE__, __LINE__, "sbsS",
+ "failed running cml_lua script",
+ fn,
+ ":",
+ lua_tostring(L, -1));
+ goto error;
+ }
+
+ /* get return value */
+ ret = (int)lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ /* fetch the data from lua */
+ lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
+
+ if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
+ http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
+ }
+
+ if (ret == 0) {
+ /* up to now it is a cache-hit, check if all files exist */
+
+ int curelem;
+ time_t mtime = 0;
+
+ if (!lua_to_c_is_table(L, "output_include")) {
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "output_include is missing or not a table");
+ ret = -1;
+
+ goto error;
+ }
+
+ lua_getglobal(L, "output_include");
+ curelem = lua_gettop(L);
+
+ /* HOW-TO build a etag ?
+ * as we don't just have one file we have to take the stat()
+ * from all base files, merge them and build the etag from
+ * it later.
+ *
+ * The mtime of the content is the mtime of the freshest base file
+ *
+ * */
+
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, curelem) != 0) {
+ /* key' is at index -2 and value' at index -1 */
+
+ if (lua_isstring(L, -1)) {
+ const char *s = lua_tostring(L, -1);
+ struct stat st;
+ int fd;
+
+ /* the file is relative, make it absolute */
+ if (s[0] != '/') {
+ buffer_copy_buffer(b, p->basedir);
+ buffer_append_string(b, lua_tostring(L, -1));
+ } else {
+ buffer_copy_string(b, lua_tostring(L, -1));
+ }
+
+ fd = stat_cache_open_rdonly_fstat(srv, con, b, &st);
+ if (fd < 0) {
+ /* stat failed */
+
+ switch(errno) {
+ case ENOENT:
+ /* a file is missing, call the handler to generate it */
+ if (!buffer_string_is_empty(p->trigger_handler)) {
+ ret = 1; /* cache-miss */
+
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "a file is missing, calling handler");
+
+ break;
+ } else {
+ /* handler not set -> 500 */
+ ret = -1;
+
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "a file missing and no handler set");
+
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size);
+ if (st.st_mtime > mtime) mtime = st.st_mtime;
+ }
+ } else {
+ /* not a string */
+ ret = -1;
+ log_error_write(srv, __FILE__, __LINE__, "s",
+ "not a string");
+ break;
+ }
+
+ lua_pop(L, 1); /* removes value'; keeps key' for next iteration */
+ }
+
+ lua_settop(L, curelem - 1);
+
+ if (ret == 0) {
+ buffer *vb = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"));
+ if (NULL == vb) { /* no Last-Modified specified */
+ char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
+ if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
+ strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
+ http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
+ vb = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"));
+ force_assert(NULL != vb);
+ }
+
+ con->file_finished = 1;
+
+ if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, vb)) {
+ /* ok, the client already has our content,
+ * no need to send it again */
+
+ chunkqueue_reset(con->write_queue);
+ ret = 0; /* cache-hit */
+ }
+ } else {
+ chunkqueue_reset(con->write_queue);
+ }
+ }
+
+ if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) {
+ /* cache-miss */
+ buffer_copy_buffer(con->uri.path, p->baseurl);
+ buffer_append_string_buffer(con->uri.path, p->trigger_handler);
+
+ buffer_copy_buffer(con->physical.path, p->basedir);
+ buffer_append_string_buffer(con->physical.path, p->trigger_handler);
+
+ chunkqueue_reset(con->write_queue);
+ }
+
+error:
+ lua_close(L);
+
+ buffer_free(b);
+
+ return ret /* cache-error */;
+}