Annotation of embedaddon/lighttpd/src/mod_cml_lua.c, revision 1.1
1.1 ! misho 1: #include "mod_cml.h"
! 2: #include "mod_cml_funcs.h"
! 3: #include "log.h"
! 4: #include "stream.h"
! 5:
! 6: #include "stat_cache.h"
! 7:
! 8: #include <assert.h>
! 9: #include <stdio.h>
! 10: #include <errno.h>
! 11: #include <time.h>
! 12: #include <string.h>
! 13:
! 14: #define HASHLEN 16
! 15: typedef unsigned char HASH[HASHLEN];
! 16: #define HASHHEXLEN 32
! 17: typedef char HASHHEX[HASHHEXLEN+1];
! 18: #ifdef USE_OPENSSL
! 19: #define IN const
! 20: #else
! 21: #define IN
! 22: #endif
! 23: #define OUT
! 24:
! 25: #ifdef HAVE_LUA_H
! 26:
! 27: #include <lua.h>
! 28: #include <lualib.h>
! 29: #include <lauxlib.h>
! 30:
! 31: typedef struct {
! 32: stream st;
! 33: int done;
! 34: } readme;
! 35:
! 36: static const char * load_file(lua_State *L, void *data, size_t *size) {
! 37: readme *rm = data;
! 38:
! 39: UNUSED(L);
! 40:
! 41: if (rm->done) return 0;
! 42:
! 43: *size = rm->st.size;
! 44: rm->done = 1;
! 45: return rm->st.start;
! 46: }
! 47:
! 48: static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
! 49: int curelem;
! 50:
! 51: lua_pushstring(L, varname);
! 52:
! 53: curelem = lua_gettop(L);
! 54: lua_gettable(L, LUA_GLOBALSINDEX);
! 55:
! 56: /* it should be a table */
! 57: if (!lua_isstring(L, curelem)) {
! 58: lua_settop(L, curelem - 1);
! 59:
! 60: return -1;
! 61: }
! 62:
! 63: buffer_copy_string(b, lua_tostring(L, curelem));
! 64:
! 65: lua_pop(L, 1);
! 66:
! 67: assert(curelem - 1 == lua_gettop(L));
! 68:
! 69: return 0;
! 70: }
! 71:
! 72: static int lua_to_c_is_table(lua_State *L, const char *varname) {
! 73: int curelem;
! 74:
! 75: lua_pushstring(L, varname);
! 76:
! 77: curelem = lua_gettop(L);
! 78: lua_gettable(L, LUA_GLOBALSINDEX);
! 79:
! 80: /* it should be a table */
! 81: if (!lua_istable(L, curelem)) {
! 82: lua_settop(L, curelem - 1);
! 83:
! 84: return 0;
! 85: }
! 86:
! 87: lua_settop(L, curelem - 1);
! 88:
! 89: assert(curelem - 1 == lua_gettop(L));
! 90:
! 91: return 1;
! 92: }
! 93:
! 94: 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) {
! 95: lua_pushlstring(L, key, key_len);
! 96: lua_pushlstring(L, val, val_len);
! 97: lua_settable(L, tbl);
! 98:
! 99: return 0;
! 100: }
! 101:
! 102:
! 103: static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
! 104: size_t is_key = 1;
! 105: size_t i;
! 106: char *key = NULL, *val = NULL;
! 107:
! 108: key = qrystr->ptr;
! 109:
! 110: /* we need the \0 */
! 111: for (i = 0; i < qrystr->used; i++) {
! 112: switch(qrystr->ptr[i]) {
! 113: case '=':
! 114: if (is_key) {
! 115: val = qrystr->ptr + i + 1;
! 116:
! 117: qrystr->ptr[i] = '\0';
! 118:
! 119: is_key = 0;
! 120: }
! 121:
! 122: break;
! 123: case '&':
! 124: case '\0': /* fin symbol */
! 125: if (!is_key) {
! 126: /* we need at least a = since the last & */
! 127:
! 128: /* terminate the value */
! 129: qrystr->ptr[i] = '\0';
! 130:
! 131: c_to_lua_push(L, tbl,
! 132: key, strlen(key),
! 133: val, strlen(val));
! 134: }
! 135:
! 136: key = qrystr->ptr + i + 1;
! 137: val = NULL;
! 138: is_key = 1;
! 139: break;
! 140: }
! 141: }
! 142:
! 143: return 0;
! 144: }
! 145: #if 0
! 146: int cache_export_cookie_params(server *srv, connection *con, plugin_data *p) {
! 147: data_unset *d;
! 148:
! 149: UNUSED(srv);
! 150:
! 151: if (NULL != (d = array_get_element(con->request.headers, "Cookie"))) {
! 152: data_string *ds = (data_string *)d;
! 153: size_t key = 0, value = 0;
! 154: size_t is_key = 1, is_sid = 0;
! 155: size_t i;
! 156:
! 157: /* found COOKIE */
! 158: if (!DATA_IS_STRING(d)) return -1;
! 159: if (ds->value->used == 0) return -1;
! 160:
! 161: if (ds->value->ptr[0] == '\0' ||
! 162: ds->value->ptr[0] == '=' ||
! 163: ds->value->ptr[0] == ';') return -1;
! 164:
! 165: buffer_reset(p->session_id);
! 166: for (i = 0; i < ds->value->used; i++) {
! 167: switch(ds->value->ptr[i]) {
! 168: case '=':
! 169: if (is_key) {
! 170: if (0 == strncmp(ds->value->ptr + key, "PHPSESSID", i - key)) {
! 171: /* found PHP-session-id-key */
! 172: is_sid = 1;
! 173: }
! 174: value = i + 1;
! 175:
! 176: is_key = 0;
! 177: }
! 178:
! 179: break;
! 180: case ';':
! 181: if (is_sid) {
! 182: buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
! 183: }
! 184:
! 185: is_sid = 0;
! 186: key = i + 1;
! 187: value = 0;
! 188: is_key = 1;
! 189: break;
! 190: case ' ':
! 191: if (is_key == 1 && key == i) key = i + 1;
! 192: if (is_key == 0 && value == i) value = i + 1;
! 193: break;
! 194: case '\0':
! 195: if (is_sid) {
! 196: buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
! 197: }
! 198: /* fin */
! 199: break;
! 200: }
! 201: }
! 202: }
! 203:
! 204: return 0;
! 205: }
! 206: #endif
! 207:
! 208: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
! 209: lua_State *L;
! 210: readme rm;
! 211: int ret = -1;
! 212: buffer *b = buffer_init();
! 213: int header_tbl = 0;
! 214:
! 215: rm.done = 0;
! 216: stream_open(&rm.st, fn);
! 217:
! 218: /* push the lua file to the interpreter and see what happends */
! 219: L = luaL_newstate();
! 220: luaL_openlibs(L);
! 221:
! 222: /* register functions */
! 223: lua_register(L, "md5", f_crypto_md5);
! 224: lua_register(L, "file_mtime", f_file_mtime);
! 225: lua_register(L, "file_isreg", f_file_isreg);
! 226: lua_register(L, "file_isdir", f_file_isreg);
! 227: lua_register(L, "dir_files", f_dir_files);
! 228:
! 229: #ifdef HAVE_MEMCACHE_H
! 230: lua_pushliteral(L, "memcache_get_long");
! 231: lua_pushlightuserdata(L, p->conf.mc);
! 232: lua_pushcclosure(L, f_memcache_get_long, 1);
! 233: lua_settable(L, LUA_GLOBALSINDEX);
! 234:
! 235: lua_pushliteral(L, "memcache_get_string");
! 236: lua_pushlightuserdata(L, p->conf.mc);
! 237: lua_pushcclosure(L, f_memcache_get_string, 1);
! 238: lua_settable(L, LUA_GLOBALSINDEX);
! 239:
! 240: lua_pushliteral(L, "memcache_exists");
! 241: lua_pushlightuserdata(L, p->conf.mc);
! 242: lua_pushcclosure(L, f_memcache_exists, 1);
! 243: lua_settable(L, LUA_GLOBALSINDEX);
! 244: #endif
! 245: /* register CGI environment */
! 246: lua_pushliteral(L, "request");
! 247: lua_newtable(L);
! 248: lua_settable(L, LUA_GLOBALSINDEX);
! 249:
! 250: lua_pushliteral(L, "request");
! 251: header_tbl = lua_gettop(L);
! 252: lua_gettable(L, LUA_GLOBALSINDEX);
! 253:
! 254: c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
! 255: c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
! 256: c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
! 257: c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
! 258: if (!buffer_is_empty(con->request.pathinfo)) {
! 259: c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
! 260: }
! 261:
! 262: c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
! 263: c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
! 264:
! 265: /* register GET parameter */
! 266: lua_pushliteral(L, "get");
! 267: lua_newtable(L);
! 268: lua_settable(L, LUA_GLOBALSINDEX);
! 269:
! 270: lua_pushliteral(L, "get");
! 271: header_tbl = lua_gettop(L);
! 272: lua_gettable(L, LUA_GLOBALSINDEX);
! 273:
! 274: buffer_copy_string_buffer(b, con->uri.query);
! 275: cache_export_get_params(L, header_tbl, b);
! 276: buffer_reset(b);
! 277:
! 278: /* 2 default constants */
! 279: lua_pushliteral(L, "CACHE_HIT");
! 280: lua_pushnumber(L, 0);
! 281: lua_settable(L, LUA_GLOBALSINDEX);
! 282:
! 283: lua_pushliteral(L, "CACHE_MISS");
! 284: lua_pushnumber(L, 1);
! 285: lua_settable(L, LUA_GLOBALSINDEX);
! 286:
! 287: /* load lua program */
! 288: if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) {
! 289: log_error_write(srv, __FILE__, __LINE__, "s",
! 290: lua_tostring(L,-1));
! 291:
! 292: goto error;
! 293: }
! 294:
! 295: /* get return value */
! 296: ret = (int)lua_tonumber(L, -1);
! 297: lua_pop(L, 1);
! 298:
! 299: /* fetch the data from lua */
! 300: lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
! 301:
! 302: if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
! 303: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
! 304: }
! 305:
! 306: if (ret == 0) {
! 307: /* up to now it is a cache-hit, check if all files exist */
! 308:
! 309: int curelem;
! 310: time_t mtime = 0;
! 311:
! 312: if (!lua_to_c_is_table(L, "output_include")) {
! 313: log_error_write(srv, __FILE__, __LINE__, "s",
! 314: "output_include is missing or not a table");
! 315: ret = -1;
! 316:
! 317: goto error;
! 318: }
! 319:
! 320: lua_pushstring(L, "output_include");
! 321:
! 322: curelem = lua_gettop(L);
! 323: lua_gettable(L, LUA_GLOBALSINDEX);
! 324:
! 325: /* HOW-TO build a etag ?
! 326: * as we don't just have one file we have to take the stat()
! 327: * from all base files, merge them and build the etag from
! 328: * it later.
! 329: *
! 330: * The mtime of the content is the mtime of the freshest base file
! 331: *
! 332: * */
! 333:
! 334: lua_pushnil(L); /* first key */
! 335: while (lua_next(L, curelem) != 0) {
! 336: stat_cache_entry *sce = NULL;
! 337: /* key' is at index -2 and value' at index -1 */
! 338:
! 339: if (lua_isstring(L, -1)) {
! 340: const char *s = lua_tostring(L, -1);
! 341:
! 342: /* the file is relative, make it absolute */
! 343: if (s[0] != '/') {
! 344: buffer_copy_string_buffer(b, p->basedir);
! 345: buffer_append_string(b, lua_tostring(L, -1));
! 346: } else {
! 347: buffer_copy_string(b, lua_tostring(L, -1));
! 348: }
! 349:
! 350: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
! 351: /* stat failed */
! 352:
! 353: switch(errno) {
! 354: case ENOENT:
! 355: /* a file is missing, call the handler to generate it */
! 356: if (!buffer_is_empty(p->trigger_handler)) {
! 357: ret = 1; /* cache-miss */
! 358:
! 359: log_error_write(srv, __FILE__, __LINE__, "s",
! 360: "a file is missing, calling handler");
! 361:
! 362: break;
! 363: } else {
! 364: /* handler not set -> 500 */
! 365: ret = -1;
! 366:
! 367: log_error_write(srv, __FILE__, __LINE__, "s",
! 368: "a file missing and no handler set");
! 369:
! 370: break;
! 371: }
! 372: break;
! 373: default:
! 374: break;
! 375: }
! 376: } else {
! 377: chunkqueue_append_file(con->write_queue, b, 0, sce->st.st_size);
! 378: if (sce->st.st_mtime > mtime) mtime = sce->st.st_mtime;
! 379: }
! 380: } else {
! 381: /* not a string */
! 382: ret = -1;
! 383: log_error_write(srv, __FILE__, __LINE__, "s",
! 384: "not a string");
! 385: break;
! 386: }
! 387:
! 388: lua_pop(L, 1); /* removes value'; keeps key' for next iteration */
! 389: }
! 390:
! 391: lua_settop(L, curelem - 1);
! 392:
! 393: if (ret == 0) {
! 394: data_string *ds;
! 395: char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
! 396: buffer tbuf;
! 397:
! 398: con->file_finished = 1;
! 399:
! 400: ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
! 401:
! 402: /* no Last-Modified specified */
! 403: if ((mtime) && (NULL == ds)) {
! 404:
! 405: strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
! 406:
! 407: response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
! 408:
! 409:
! 410: tbuf.ptr = timebuf;
! 411: tbuf.used = sizeof(timebuf);
! 412: tbuf.size = sizeof(timebuf);
! 413: } else if (ds) {
! 414: tbuf.ptr = ds->value->ptr;
! 415: tbuf.used = ds->value->used;
! 416: tbuf.size = ds->value->size;
! 417: } else {
! 418: tbuf.size = 0;
! 419: tbuf.used = 0;
! 420: tbuf.ptr = NULL;
! 421: }
! 422:
! 423: if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) {
! 424: /* ok, the client already has our content,
! 425: * no need to send it again */
! 426:
! 427: chunkqueue_reset(con->write_queue);
! 428: ret = 0; /* cache-hit */
! 429: }
! 430: } else {
! 431: chunkqueue_reset(con->write_queue);
! 432: }
! 433: }
! 434:
! 435: if (ret == 1 && !buffer_is_empty(p->trigger_handler)) {
! 436: /* cache-miss */
! 437: buffer_copy_string_buffer(con->uri.path, p->baseurl);
! 438: buffer_append_string_buffer(con->uri.path, p->trigger_handler);
! 439:
! 440: buffer_copy_string_buffer(con->physical.path, p->basedir);
! 441: buffer_append_string_buffer(con->physical.path, p->trigger_handler);
! 442:
! 443: chunkqueue_reset(con->write_queue);
! 444: }
! 445:
! 446: error:
! 447: lua_close(L);
! 448:
! 449: stream_close(&rm.st);
! 450: buffer_free(b);
! 451:
! 452: return ret /* cache-error */;
! 453: }
! 454: #else
! 455: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
! 456: UNUSED(srv);
! 457: UNUSED(con);
! 458: UNUSED(p);
! 459: UNUSED(fn);
! 460: /* error */
! 461: return -1;
! 462: }
! 463: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>