File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_cml_lua.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    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: 	force_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: 	force_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;
  213: 	int header_tbl = 0;
  214: 
  215: 	rm.done = 0;
  216: 	if (-1 == stream_open(&rm.st, fn)) {
  217: 		log_error_write(srv, __FILE__, __LINE__, "sbss",
  218: 				"opening lua cml file ", fn, "failed:", strerror(errno));
  219: 		return -1;
  220: 	}
  221: 
  222: 	b = buffer_init();
  223: 	/* push the lua file to the interpreter and see what happends */
  224: 	L = luaL_newstate();
  225: 	luaL_openlibs(L);
  226: 
  227: 	/* register functions */
  228: 	lua_register(L, "md5", f_crypto_md5);
  229: 	lua_register(L, "file_mtime", f_file_mtime);
  230: 	lua_register(L, "file_isreg", f_file_isreg);
  231: 	lua_register(L, "file_isdir", f_file_isreg);
  232: 	lua_register(L, "dir_files", f_dir_files);
  233: 
  234: #ifdef HAVE_MEMCACHE_H
  235: 	lua_pushliteral(L, "memcache_get_long");
  236: 	lua_pushlightuserdata(L, p->conf.mc);
  237: 	lua_pushcclosure(L, f_memcache_get_long, 1);
  238: 	lua_settable(L, LUA_GLOBALSINDEX);
  239: 
  240: 	lua_pushliteral(L, "memcache_get_string");
  241: 	lua_pushlightuserdata(L, p->conf.mc);
  242: 	lua_pushcclosure(L, f_memcache_get_string, 1);
  243: 	lua_settable(L, LUA_GLOBALSINDEX);
  244: 
  245: 	lua_pushliteral(L, "memcache_exists");
  246: 	lua_pushlightuserdata(L, p->conf.mc);
  247: 	lua_pushcclosure(L, f_memcache_exists, 1);
  248: 	lua_settable(L, LUA_GLOBALSINDEX);
  249: #endif
  250: 	/* register CGI environment */
  251: 	lua_pushliteral(L, "request");
  252: 	lua_newtable(L);
  253: 	lua_settable(L, LUA_GLOBALSINDEX);
  254: 
  255: 	lua_pushliteral(L, "request");
  256: 	header_tbl = lua_gettop(L);
  257: 	lua_gettable(L, LUA_GLOBALSINDEX);
  258: 
  259: 	c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
  260: 	c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
  261: 	c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
  262: 	c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
  263: 	if (!buffer_is_empty(con->request.pathinfo)) {
  264: 		c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
  265: 	}
  266: 
  267: 	c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
  268: 	c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
  269: 
  270: 	/* register GET parameter */
  271: 	lua_pushliteral(L, "get");
  272: 	lua_newtable(L);
  273: 	lua_settable(L, LUA_GLOBALSINDEX);
  274: 
  275: 	lua_pushliteral(L, "get");
  276: 	header_tbl = lua_gettop(L);
  277: 	lua_gettable(L, LUA_GLOBALSINDEX);
  278: 
  279: 	buffer_copy_string_buffer(b, con->uri.query);
  280: 	cache_export_get_params(L, header_tbl, b);
  281: 	buffer_reset(b);
  282: 
  283: 	/* 2 default constants */
  284: 	lua_pushliteral(L, "CACHE_HIT");
  285: 	lua_pushnumber(L, 0);
  286: 	lua_settable(L, LUA_GLOBALSINDEX);
  287: 
  288: 	lua_pushliteral(L, "CACHE_MISS");
  289: 	lua_pushnumber(L, 1);
  290: 	lua_settable(L, LUA_GLOBALSINDEX);
  291: 
  292: 	/* load lua program */
  293: 	if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) {
  294: 		log_error_write(srv, __FILE__, __LINE__, "s",
  295: 				lua_tostring(L,-1));
  296: 
  297: 		goto error;
  298: 	}
  299: 
  300: 	/* get return value */
  301: 	ret = (int)lua_tonumber(L, -1);
  302: 	lua_pop(L, 1);
  303: 
  304: 	/* fetch the data from lua */
  305: 	lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
  306: 
  307: 	if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
  308: 		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
  309: 	}
  310: 
  311: 	if (ret == 0) {
  312: 		/* up to now it is a cache-hit, check if all files exist */
  313: 
  314: 		int curelem;
  315: 		time_t mtime = 0;
  316: 
  317: 		if (!lua_to_c_is_table(L, "output_include")) {
  318: 			log_error_write(srv, __FILE__, __LINE__, "s",
  319: 				"output_include is missing or not a table");
  320: 			ret = -1;
  321: 
  322: 			goto error;
  323: 		}
  324: 
  325: 		lua_pushstring(L, "output_include");
  326: 
  327: 		curelem = lua_gettop(L);
  328: 		lua_gettable(L, LUA_GLOBALSINDEX);
  329: 
  330: 		/* HOW-TO build a etag ?
  331: 		 * as we don't just have one file we have to take the stat()
  332: 		 * from all base files, merge them and build the etag from
  333: 		 * it later.
  334: 		 *
  335: 		 * The mtime of the content is the mtime of the freshest base file
  336: 		 *
  337: 		 * */
  338: 
  339: 		lua_pushnil(L);  /* first key */
  340: 		while (lua_next(L, curelem) != 0) {
  341: 			stat_cache_entry *sce = NULL;
  342: 			/* key' is at index -2 and value' at index -1 */
  343: 
  344: 			if (lua_isstring(L, -1)) {
  345: 				const char *s = lua_tostring(L, -1);
  346: 
  347: 				/* the file is relative, make it absolute */
  348: 				if (s[0] != '/') {
  349: 					buffer_copy_string_buffer(b, p->basedir);
  350: 					buffer_append_string(b, lua_tostring(L, -1));
  351: 				} else {
  352: 					buffer_copy_string(b, lua_tostring(L, -1));
  353: 				}
  354: 
  355: 				if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
  356: 					/* stat failed */
  357: 
  358: 					switch(errno) {
  359: 					case ENOENT:
  360: 						/* a file is missing, call the handler to generate it */
  361: 						if (!buffer_is_empty(p->trigger_handler)) {
  362: 							ret = 1; /* cache-miss */
  363: 
  364: 							log_error_write(srv, __FILE__, __LINE__, "s",
  365: 									"a file is missing, calling handler");
  366: 
  367: 							break;
  368: 						} else {
  369: 							/* handler not set -> 500 */
  370: 							ret = -1;
  371: 
  372: 							log_error_write(srv, __FILE__, __LINE__, "s",
  373: 									"a file missing and no handler set");
  374: 
  375: 							break;
  376: 						}
  377: 						break;
  378: 					default:
  379: 						break;
  380: 					}
  381: 				} else {
  382: 					chunkqueue_append_file(con->write_queue, b, 0, sce->st.st_size);
  383: 					if (sce->st.st_mtime > mtime) mtime = sce->st.st_mtime;
  384: 				}
  385: 			} else {
  386: 				/* not a string */
  387: 				ret = -1;
  388: 				log_error_write(srv, __FILE__, __LINE__, "s",
  389: 						"not a string");
  390: 				break;
  391: 			}
  392: 
  393: 			lua_pop(L, 1);  /* removes value'; keeps key' for next iteration */
  394: 		}
  395: 
  396: 		lua_settop(L, curelem - 1);
  397: 
  398: 		if (ret == 0) {
  399: 			data_string *ds;
  400: 			char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
  401: 			buffer tbuf;
  402: 
  403: 			con->file_finished = 1;
  404: 
  405: 			ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
  406: 			if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
  407: 
  408: 			/* no Last-Modified specified */
  409: 			if (NULL == ds) {
  410: 
  411: 				strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
  412: 
  413: 				response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
  414: 
  415: 				tbuf.ptr = timebuf;
  416: 				tbuf.used = sizeof(timebuf);
  417: 				tbuf.size = sizeof(timebuf);
  418: 			} else {
  419: 				tbuf.ptr = ds->value->ptr;
  420: 				tbuf.used = ds->value->used;
  421: 				tbuf.size = ds->value->size;
  422: 			}
  423: 
  424: 			if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) {
  425: 				/* ok, the client already has our content,
  426: 				 * no need to send it again */
  427: 
  428: 				chunkqueue_reset(con->write_queue);
  429: 				ret = 0; /* cache-hit */
  430: 			}
  431: 		} else {
  432: 			chunkqueue_reset(con->write_queue);
  433: 		}
  434: 	}
  435: 
  436: 	if (ret == 1 && !buffer_is_empty(p->trigger_handler)) {
  437: 		/* cache-miss */
  438: 		buffer_copy_string_buffer(con->uri.path, p->baseurl);
  439: 		buffer_append_string_buffer(con->uri.path, p->trigger_handler);
  440: 
  441: 		buffer_copy_string_buffer(con->physical.path, p->basedir);
  442: 		buffer_append_string_buffer(con->physical.path, p->trigger_handler);
  443: 
  444: 		chunkqueue_reset(con->write_queue);
  445: 	}
  446: 
  447: error:
  448: 	lua_close(L);
  449: 
  450: 	stream_close(&rm.st);
  451: 	buffer_free(b);
  452: 
  453: 	return ret /* cache-error */;
  454: }
  455: #else
  456: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
  457: 	UNUSED(srv);
  458: 	UNUSED(con);
  459: 	UNUSED(p);
  460: 	UNUSED(fn);
  461: 	/* error */
  462: 	return -1;
  463: }
  464: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>