[tests] skip mod-secdownload HMAC-SHA1,HMAC-SHA256
[lighttpd.git] / src / mod_cml_lua.c
blob926f3ff6d10040fd0fe903bbbc114d75d12e8896
1 #include "first.h"
3 #include <lua.h>
4 #include <lualib.h>
5 #include <lauxlib.h>
7 #include <errno.h>
8 #include <time.h>
9 #include <string.h>
11 #include "mod_cml_funcs.h"
12 #include "mod_cml.h"
14 #include "base.h"
15 #include "chunk.h"
16 #include "log.h"
17 #include "http_header.h"
18 #include "response.h"
19 #include "stat_cache.h"
21 #define HASHLEN 16
22 typedef unsigned char HASH[HASHLEN];
23 #define HASHHEXLEN 32
24 typedef char HASHHEX[HASHHEXLEN+1];
26 static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
27 int curelem = lua_gettop(L);
28 int result;
30 lua_getglobal(L, varname);
32 if (lua_isstring(L, curelem)) {
33 buffer_copy_string(b, lua_tostring(L, curelem));
34 result = 0;
35 } else {
36 result = -1;
39 lua_pop(L, 1);
40 force_assert(curelem == lua_gettop(L));
41 return result;
44 static int lua_to_c_is_table(lua_State *L, const char *varname) {
45 int curelem = lua_gettop(L);
46 int result;
48 lua_getglobal(L, varname);
50 result = lua_istable(L, curelem) ? 1 : 0;
52 lua_pop(L, 1);
53 force_assert(curelem == lua_gettop(L));
54 return result;
57 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) {
58 lua_pushlstring(L, key, key_len);
59 lua_pushlstring(L, val, val_len);
60 lua_settable(L, tbl);
62 return 0;
65 static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
66 size_t is_key = 1;
67 size_t i, len, klen = 0;
68 char *key = NULL, *val = NULL;
70 if (buffer_string_is_empty(qrystr)) return 0;
71 key = qrystr->ptr;
73 /* we need the \0 */
74 len = buffer_string_length(qrystr);
75 for (i = 0; i <= len; i++) {
76 switch(qrystr->ptr[i]) {
77 case '=':
78 if (is_key) {
79 val = qrystr->ptr + i + 1;
80 klen = (size_t)(val - key - 1);
81 is_key = 0;
84 break;
85 case '&':
86 case '\0': /* fin symbol */
87 if (!is_key) {
88 /* we need at least a = since the last & */
89 c_to_lua_push(L, tbl,
90 key, klen,
91 val, (size_t)(qrystr->ptr + i - val));
94 key = qrystr->ptr + i + 1;
95 val = NULL;
96 is_key = 1;
97 break;
101 return 0;
104 int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
105 lua_State *L;
106 int ret = -1;
107 buffer *b;
109 b = buffer_init();
110 /* push the lua file to the interpreter and see what happends */
111 L = luaL_newstate();
112 luaL_openlibs(L);
114 /* register functions */
115 lua_register(L, "md5", f_crypto_md5);
116 lua_register(L, "file_mtime", f_file_mtime);
117 lua_register(L, "file_isreg", f_file_isreg);
118 lua_register(L, "file_isdir", f_file_isreg);
119 lua_register(L, "dir_files", f_dir_files);
121 #ifdef USE_MEMCACHED
122 lua_pushlightuserdata(L, p->conf.memc);
123 lua_pushcclosure(L, f_memcache_get_long, 1);
124 lua_setglobal(L, "memcache_get_long");
126 lua_pushlightuserdata(L, p->conf.memc);
127 lua_pushcclosure(L, f_memcache_get_string, 1);
128 lua_setglobal(L, "memcache_get_string");
130 lua_pushlightuserdata(L, p->conf.memc);
131 lua_pushcclosure(L, f_memcache_exists, 1);
132 lua_setglobal(L, "memcache_exists");
133 #endif
135 /* register CGI environment */
136 lua_newtable(L);
138 int header_tbl = lua_gettop(L);
140 c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
141 c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
142 c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
143 c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
144 if (!buffer_string_is_empty(con->request.pathinfo)) {
145 c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
148 c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
149 c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
151 lua_setglobal(L, "request");
153 /* register GET parameter */
154 lua_newtable(L);
155 cache_export_get_params(L, lua_gettop(L), con->uri.query);
156 lua_setglobal(L, "get");
158 /* 2 default constants */
159 lua_pushinteger(L, 0);
160 lua_setglobal(L, "CACHE_HIT");
162 lua_pushinteger(L, 1);
163 lua_setglobal(L, "CACHE_MISS");
165 /* load lua program */
166 ret = luaL_loadfile(L, fn->ptr);
167 if (0 != ret) {
168 log_error_write(srv, __FILE__, __LINE__, "sbsS",
169 "failed loading cml_lua script",
171 ":",
172 lua_tostring(L, -1));
173 goto error;
176 if (lua_pcall(L, 0, 1, 0)) {
177 log_error_write(srv, __FILE__, __LINE__, "sbsS",
178 "failed running cml_lua script",
180 ":",
181 lua_tostring(L, -1));
182 goto error;
185 /* get return value */
186 ret = (int)lua_tointeger(L, -1);
187 lua_pop(L, 1);
189 /* fetch the data from lua */
190 lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
192 if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
193 http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
196 if (ret == 0) {
197 /* up to now it is a cache-hit, check if all files exist */
199 int curelem;
200 time_t mtime = 0;
202 if (!lua_to_c_is_table(L, "output_include")) {
203 log_error_write(srv, __FILE__, __LINE__, "s",
204 "output_include is missing or not a table");
205 ret = -1;
207 goto error;
210 lua_getglobal(L, "output_include");
211 curelem = lua_gettop(L);
213 /* HOW-TO build a etag ?
214 * as we don't just have one file we have to take the stat()
215 * from all base files, merge them and build the etag from
216 * it later.
218 * The mtime of the content is the mtime of the freshest base file
220 * */
222 lua_pushnil(L); /* first key */
223 while (lua_next(L, curelem) != 0) {
224 /* key' is at index -2 and value' at index -1 */
226 if (lua_isstring(L, -1)) {
227 const char *s = lua_tostring(L, -1);
228 struct stat st;
229 int fd;
231 /* the file is relative, make it absolute */
232 if (s[0] != '/') {
233 buffer_copy_buffer(b, p->basedir);
234 buffer_append_string(b, lua_tostring(L, -1));
235 } else {
236 buffer_copy_string(b, lua_tostring(L, -1));
239 fd = stat_cache_open_rdonly_fstat(b, &st, con->conf.follow_symlink);
240 if (fd < 0) {
241 /* stat failed */
243 switch(errno) {
244 case ENOENT:
245 /* a file is missing, call the handler to generate it */
246 if (!buffer_string_is_empty(p->trigger_handler)) {
247 ret = 1; /* cache-miss */
249 log_error_write(srv, __FILE__, __LINE__, "s",
250 "a file is missing, calling handler");
252 break;
253 } else {
254 /* handler not set -> 500 */
255 ret = -1;
257 log_error_write(srv, __FILE__, __LINE__, "s",
258 "a file missing and no handler set");
260 break;
262 break;
263 default:
264 break;
266 } else {
267 chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size);
268 if (st.st_mtime > mtime) mtime = st.st_mtime;
270 } else {
271 /* not a string */
272 ret = -1;
273 log_error_write(srv, __FILE__, __LINE__, "s",
274 "not a string");
275 break;
278 lua_pop(L, 1); /* removes value'; keeps key' for next iteration */
281 lua_settop(L, curelem - 1);
283 if (ret == 0) {
284 buffer *vb = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"));
285 if (NULL == vb) { /* no Last-Modified specified */
286 char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
287 if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
288 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
289 http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
290 vb = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"));
291 force_assert(NULL != vb);
294 con->file_finished = 1;
296 if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, vb)) {
297 /* ok, the client already has our content,
298 * no need to send it again */
300 chunkqueue_reset(con->write_queue);
301 ret = 0; /* cache-hit */
303 } else {
304 chunkqueue_reset(con->write_queue);
308 if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) {
309 /* cache-miss */
310 buffer_copy_buffer(con->uri.path, p->baseurl);
311 buffer_append_string_buffer(con->uri.path, p->trigger_handler);
313 buffer_copy_buffer(con->physical.path, p->basedir);
314 buffer_append_string_buffer(con->physical.path, p->trigger_handler);
316 chunkqueue_reset(con->write_queue);
319 error:
320 lua_close(L);
322 buffer_free(b);
324 return ret /* cache-error */;