[mod_cgi] skip local-redir handling if to self (fixes #2779, #2108)
[lighttpd.git] / src / mod_cml_lua.c
blobe9f5c8b8730b51a6b16d9882f735c57dddee78a1
1 #include "first.h"
3 #include <lua.h>
4 #include <lualib.h>
5 #include <lauxlib.h>
7 #include <assert.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <time.h>
11 #include <string.h>
13 #include "mod_cml_funcs.h"
14 #include "mod_cml.h"
16 #include "log.h"
17 #include "stat_cache.h"
19 #define HASHLEN 16
20 typedef unsigned char HASH[HASHLEN];
21 #define HASHHEXLEN 32
22 typedef char HASHHEX[HASHHEXLEN+1];
24 static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
25 int curelem = lua_gettop(L);
26 int result;
28 lua_getglobal(L, varname);
30 if (lua_isstring(L, curelem)) {
31 buffer_copy_string(b, lua_tostring(L, curelem));
32 result = 0;
33 } else {
34 result = -1;
37 lua_pop(L, 1);
38 force_assert(curelem == lua_gettop(L));
39 return result;
42 static int lua_to_c_is_table(lua_State *L, const char *varname) {
43 int curelem = lua_gettop(L);
44 int result;
46 lua_getglobal(L, varname);
48 result = lua_istable(L, curelem) ? 1 : 0;
50 lua_pop(L, 1);
51 force_assert(curelem == lua_gettop(L));
52 return result;
55 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) {
56 lua_pushlstring(L, key, key_len);
57 lua_pushlstring(L, val, val_len);
58 lua_settable(L, tbl);
60 return 0;
63 static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
64 size_t is_key = 1;
65 size_t i, len;
66 char *key = NULL, *val = NULL;
68 key = qrystr->ptr;
70 /* we need the \0 */
71 len = buffer_string_length(qrystr);
72 for (i = 0; i <= len; i++) {
73 switch(qrystr->ptr[i]) {
74 case '=':
75 if (is_key) {
76 val = qrystr->ptr + i + 1;
78 qrystr->ptr[i] = '\0';
80 is_key = 0;
83 break;
84 case '&':
85 case '\0': /* fin symbol */
86 if (!is_key) {
87 /* we need at least a = since the last & */
89 /* terminate the value */
90 qrystr->ptr[i] = '\0';
92 c_to_lua_push(L, tbl,
93 key, strlen(key),
94 val, strlen(val));
97 key = qrystr->ptr + i + 1;
98 val = NULL;
99 is_key = 1;
100 break;
104 return 0;
107 int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
108 lua_State *L;
109 int ret = -1;
110 buffer *b;
112 b = buffer_init();
113 /* push the lua file to the interpreter and see what happends */
114 L = luaL_newstate();
115 luaL_openlibs(L);
117 /* register functions */
118 lua_register(L, "md5", f_crypto_md5);
119 lua_register(L, "file_mtime", f_file_mtime);
120 lua_register(L, "file_isreg", f_file_isreg);
121 lua_register(L, "file_isdir", f_file_isreg);
122 lua_register(L, "dir_files", f_dir_files);
124 #ifdef USE_MEMCACHED
125 lua_pushlightuserdata(L, p->conf.memc);
126 lua_pushcclosure(L, f_memcache_get_long, 1);
127 lua_setglobal(L, "memcache_get_long");
129 lua_pushlightuserdata(L, p->conf.memc);
130 lua_pushcclosure(L, f_memcache_get_string, 1);
131 lua_setglobal(L, "memcache_get_string");
133 lua_pushlightuserdata(L, p->conf.memc);
134 lua_pushcclosure(L, f_memcache_exists, 1);
135 lua_setglobal(L, "memcache_exists");
136 #endif
138 /* register CGI environment */
139 lua_newtable(L);
141 int header_tbl = lua_gettop(L);
143 c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
144 c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
145 c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
146 c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
147 if (!buffer_string_is_empty(con->request.pathinfo)) {
148 c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
151 c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
152 c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
154 lua_setglobal(L, "request");
156 /* register GET parameter */
157 lua_newtable(L);
159 int get_tbl = lua_gettop(L);
161 buffer_copy_buffer(b, con->uri.query);
162 cache_export_get_params(L, get_tbl, b);
163 buffer_reset(b);
165 lua_setglobal(L, "get");
167 /* 2 default constants */
168 lua_pushinteger(L, 0);
169 lua_setglobal(L, "CACHE_HIT");
171 lua_pushinteger(L, 1);
172 lua_setglobal(L, "CACHE_MISS");
174 /* load lua program */
175 ret = luaL_loadfile(L, fn->ptr);
176 if (0 != ret) {
177 log_error_write(srv, __FILE__, __LINE__, "sbsS",
178 "failed loading cml_lua script",
180 ":",
181 lua_tostring(L, -1));
182 goto error;
185 if (lua_pcall(L, 0, 1, 0)) {
186 log_error_write(srv, __FILE__, __LINE__, "sbsS",
187 "failed running cml_lua script",
189 ":",
190 lua_tostring(L, -1));
191 goto error;
194 /* get return value */
195 ret = (int)lua_tointeger(L, -1);
196 lua_pop(L, 1);
198 /* fetch the data from lua */
199 lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
201 if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
202 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
205 if (ret == 0) {
206 /* up to now it is a cache-hit, check if all files exist */
208 int curelem;
209 time_t mtime = 0;
211 if (!lua_to_c_is_table(L, "output_include")) {
212 log_error_write(srv, __FILE__, __LINE__, "s",
213 "output_include is missing or not a table");
214 ret = -1;
216 goto error;
219 lua_getglobal(L, "output_include");
220 curelem = lua_gettop(L);
222 /* HOW-TO build a etag ?
223 * as we don't just have one file we have to take the stat()
224 * from all base files, merge them and build the etag from
225 * it later.
227 * The mtime of the content is the mtime of the freshest base file
229 * */
231 lua_pushnil(L); /* first key */
232 while (lua_next(L, curelem) != 0) {
233 /* key' is at index -2 and value' at index -1 */
235 if (lua_isstring(L, -1)) {
236 const char *s = lua_tostring(L, -1);
237 struct stat st;
238 int fd;
240 /* the file is relative, make it absolute */
241 if (s[0] != '/') {
242 buffer_copy_buffer(b, p->basedir);
243 buffer_append_string(b, lua_tostring(L, -1));
244 } else {
245 buffer_copy_string(b, lua_tostring(L, -1));
248 fd = stat_cache_open_rdonly_fstat(srv, con, b, &st);
249 if (fd < 0) {
250 /* stat failed */
252 switch(errno) {
253 case ENOENT:
254 /* a file is missing, call the handler to generate it */
255 if (!buffer_string_is_empty(p->trigger_handler)) {
256 ret = 1; /* cache-miss */
258 log_error_write(srv, __FILE__, __LINE__, "s",
259 "a file is missing, calling handler");
261 break;
262 } else {
263 /* handler not set -> 500 */
264 ret = -1;
266 log_error_write(srv, __FILE__, __LINE__, "s",
267 "a file missing and no handler set");
269 break;
271 break;
272 default:
273 break;
275 } else {
276 chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size);
277 if (st.st_mtime > mtime) mtime = st.st_mtime;
279 } else {
280 /* not a string */
281 ret = -1;
282 log_error_write(srv, __FILE__, __LINE__, "s",
283 "not a string");
284 break;
287 lua_pop(L, 1); /* removes value'; keeps key' for next iteration */
290 lua_settop(L, curelem - 1);
292 if (ret == 0) {
293 data_string *ds;
294 char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
296 con->file_finished = 1;
298 ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
299 if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
301 /* no Last-Modified specified */
302 if (NULL == ds) {
304 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
306 response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
307 ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
308 force_assert(NULL != ds);
311 if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, ds->value)) {
312 /* ok, the client already has our content,
313 * no need to send it again */
315 chunkqueue_reset(con->write_queue);
316 ret = 0; /* cache-hit */
318 } else {
319 chunkqueue_reset(con->write_queue);
323 if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) {
324 /* cache-miss */
325 buffer_copy_buffer(con->uri.path, p->baseurl);
326 buffer_append_string_buffer(con->uri.path, p->trigger_handler);
328 buffer_copy_buffer(con->physical.path, p->basedir);
329 buffer_append_string_buffer(con->physical.path, p->trigger_handler);
331 chunkqueue_reset(con->write_queue);
334 error:
335 lua_close(L);
337 buffer_free(b);
339 return ret /* cache-error */;