File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_magnet.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:35:00 2016 UTC (7 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_41p8, HEAD
lighttpd 1.4.41

    1: #include "first.h"
    2: 
    3: #include "base.h"
    4: #include "log.h"
    5: #include "buffer.h"
    6: #include "http_chunk.h"
    7: 
    8: #include "plugin.h"
    9: 
   10: #include "mod_magnet_cache.h"
   11: #include "response.h"
   12: #include "stat_cache.h"
   13: #include "status_counter.h"
   14: #include "etag.h"
   15: 
   16: #include <ctype.h>
   17: #include <stdlib.h>
   18: #include <string.h>
   19: #include <assert.h>
   20: #include <setjmp.h>
   21: 
   22: #ifdef HAVE_LUA_H
   23: #include <lua.h>
   24: #include <lauxlib.h>
   25: 
   26: #define LUA_RIDX_LIGHTTPD_SERVER     "lighty.srv"
   27: #define LUA_RIDX_LIGHTTPD_CONNECTION "lighty.con"
   28: 
   29: #define MAGNET_CONFIG_RAW_URL       "magnet.attract-raw-url-to"
   30: #define MAGNET_CONFIG_PHYSICAL_PATH "magnet.attract-physical-path-to"
   31: #define MAGNET_RESTART_REQUEST      99
   32: 
   33: /* plugin config for all request/connections */
   34: 
   35: static jmp_buf exceptionjmp;
   36: 
   37: typedef struct {
   38: 	array *url_raw;
   39: 	array *physical_path;
   40: } plugin_config;
   41: 
   42: typedef struct {
   43: 	PLUGIN_DATA;
   44: 
   45: 	script_cache *cache;
   46: 
   47: 	buffer *encode_buf;
   48: 
   49: 	plugin_config **config_storage;
   50: 
   51: 	plugin_config conf;
   52: } plugin_data;
   53: 
   54: /* init the plugin data */
   55: INIT_FUNC(mod_magnet_init) {
   56: 	plugin_data *p;
   57: 
   58: 	p = calloc(1, sizeof(*p));
   59: 
   60: 	p->cache = script_cache_init();
   61: 	p->encode_buf = buffer_init();
   62: 
   63: 	return p;
   64: }
   65: 
   66: /* detroy the plugin data */
   67: FREE_FUNC(mod_magnet_free) {
   68: 	plugin_data *p = p_d;
   69: 
   70: 	UNUSED(srv);
   71: 
   72: 	if (!p) return HANDLER_GO_ON;
   73: 
   74: 	if (p->config_storage) {
   75: 		size_t i;
   76: 
   77: 		for (i = 0; i < srv->config_context->used; i++) {
   78: 			plugin_config *s = p->config_storage[i];
   79: 
   80: 			if (NULL == s) continue;
   81: 
   82: 			array_free(s->url_raw);
   83: 			array_free(s->physical_path);
   84: 
   85: 			free(s);
   86: 		}
   87: 		free(p->config_storage);
   88: 	}
   89: 
   90: 	script_cache_free(p->cache);
   91: 	buffer_free(p->encode_buf);
   92: 
   93: 	free(p);
   94: 
   95: 	return HANDLER_GO_ON;
   96: }
   97: 
   98: /* handle plugin config and check values */
   99: 
  100: SETDEFAULTS_FUNC(mod_magnet_set_defaults) {
  101: 	plugin_data *p = p_d;
  102: 	size_t i = 0;
  103: 
  104: 	config_values_t cv[] = {
  105: 		{ MAGNET_CONFIG_RAW_URL,       NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
  106: 		{ MAGNET_CONFIG_PHYSICAL_PATH, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
  107: 		{ NULL,                           NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
  108: 	};
  109: 
  110: 	if (!p) return HANDLER_ERROR;
  111: 
  112: 	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
  113: 
  114: 	for (i = 0; i < srv->config_context->used; i++) {
  115: 		data_config const* config = (data_config const*)srv->config_context->data[i];
  116: 		plugin_config *s;
  117: 
  118: 		s = calloc(1, sizeof(plugin_config));
  119: 		s->url_raw  = array_init();
  120: 		s->physical_path = array_init();
  121: 
  122: 		cv[0].destination = s->url_raw;
  123: 		cv[1].destination = s->physical_path;
  124: 
  125: 		p->config_storage[i] = s;
  126: 
  127: 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
  128: 			return HANDLER_ERROR;
  129: 		}
  130: 	}
  131: 
  132: 	return HANDLER_GO_ON;
  133: }
  134: 
  135: #define PATCH(x) \
  136: 	p->conf.x = s->x;
  137: static int mod_magnet_patch_connection(server *srv, connection *con, plugin_data *p) {
  138: 	size_t i, j;
  139: 	plugin_config *s = p->config_storage[0];
  140: 
  141: 	PATCH(url_raw);
  142: 	PATCH(physical_path);
  143: 
  144: 	/* skip the first, the global context */
  145: 	for (i = 1; i < srv->config_context->used; i++) {
  146: 		data_config *dc = (data_config *)srv->config_context->data[i];
  147: 		s = p->config_storage[i];
  148: 
  149: 		/* condition didn't match */
  150: 		if (!config_check_cond(srv, con, dc)) continue;
  151: 
  152: 		/* merge config */
  153: 		for (j = 0; j < dc->value->used; j++) {
  154: 			data_unset *du = dc->value->data[j];
  155: 
  156: 			if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_RAW_URL))) {
  157: 				PATCH(url_raw);
  158: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_PHYSICAL_PATH))) {
  159: 				PATCH(physical_path);
  160: 			}
  161: 		}
  162: 	}
  163: 
  164: 	return 0;
  165: }
  166: #undef PATCH
  167: 
  168: #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
  169: /* lua5.1 backward compat definition */
  170: static void lua_pushglobaltable(lua_State *L) { /* (-0, +1, -) */
  171: 	lua_pushvalue(L, LUA_GLOBALSINDEX);
  172: }
  173: #endif
  174: 
  175: static void magnet_setfenv_mainfn(lua_State *L, int funcIndex) { /* (-1, 0, -) */
  176: #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502
  177: 	/* set "_ENV" upvalue, which should be the first upvalue of a "main" lua
  178: 	 * function if it uses any global names
  179: 	 */
  180: 
  181: 	const char* first_upvalue_name = lua_getupvalue(L, funcIndex, 1);
  182: 	if (NULL == first_upvalue_name) return; /* doesn't have any upvalues */
  183: 	lua_pop(L, 1); /* only need the name of the upvalue, not the value */
  184: 
  185: 	if (0 != strcmp(first_upvalue_name, "_ENV")) return;
  186: 
  187: 	if (NULL == lua_setupvalue(L, funcIndex, 1)) {
  188: 		/* pop value if lua_setupvalue didn't set the (not existing) upvalue */
  189: 		lua_pop(L, 1);
  190: 	}
  191: #else
  192: 	lua_setfenv(L, funcIndex);
  193: #endif
  194: }
  195: 
  196: #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
  197: /* lua 5.2 already supports __pairs */
  198: 
  199: /* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details.
  200:  * Override the default pairs() function to allow us to use a __pairs metakey
  201:  */
  202: static int magnet_pairs(lua_State *L) {
  203: 	luaL_checkany(L, 1); /* "self" */
  204: 
  205: 	if (luaL_getmetafield(L, 1, "__pairs")) {
  206: 		/* call __pairs(self) */
  207: 		lua_pushvalue(L, 1);
  208: 		lua_call(L, 1, 3);
  209: 	} else {
  210: 		/* call <original-pairs-method>(self) */
  211: 		lua_pushvalue(L, lua_upvalueindex(1));
  212: 		lua_pushvalue(L, 1);
  213: 		lua_call(L, 1, 3);
  214: 	}
  215: 	return 3;
  216: }
  217: #endif
  218: 
  219: /* Define a function that will iterate over an array* (in upval 1) using current position (upval 2) */
  220: static int magnet_array_next(lua_State *L) {
  221: 	data_unset *du;
  222: 	data_string *ds;
  223: 	data_integer *di;
  224: 
  225: 	size_t pos = lua_tointeger(L, lua_upvalueindex(1));
  226: 	array *a = lua_touserdata(L, lua_upvalueindex(2));
  227: 
  228: 	lua_settop(L, 0);
  229: 
  230: 	if (pos >= a->used) return 0;
  231: 	if (NULL != (du = a->data[pos])) {
  232: 		lua_pushlstring(L, CONST_BUF_LEN(du->key));
  233: 		switch (du->type) {
  234: 			case TYPE_STRING:
  235: 				ds = (data_string *)du;
  236: 				if (!buffer_is_empty(ds->value)) {
  237: 					lua_pushlstring(L, CONST_BUF_LEN(ds->value));
  238: 				} else {
  239: 					lua_pushnil(L);
  240: 				}
  241: 				break;
  242: 			case TYPE_COUNT:
  243: 			case TYPE_INTEGER:
  244: 				di = (data_integer *)du;
  245: 				lua_pushinteger(L, di->value);
  246: 				break;
  247: 			default:
  248: 				lua_pushnil(L);
  249: 				break;
  250: 		}
  251: 
  252: 		/* Update our positional upval to reflect our new current position */
  253: 		pos++;
  254: 		lua_pushinteger(L, pos);
  255: 		lua_replace(L, lua_upvalueindex(1));
  256: 
  257: 		/* Returning 2 items on the stack (key, value) */
  258: 		return 2;
  259: 	}
  260: 	return 0;
  261: }
  262: 
  263: /* Create the closure necessary to iterate over the array *a with the above function */
  264: static int magnet_array_pairs(lua_State *L, array *a) {
  265: 	lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */
  266: 	lua_pushlightuserdata(L, a); /* Push our array *a into upval 2 */
  267: 	lua_pushcclosure(L, magnet_array_next, 2); /* Push our new closure with 2 upvals */
  268: 	return 1;
  269: }
  270: 
  271: static server* magnet_get_server(lua_State *L) {
  272: 	server *srv;
  273: 
  274: 	lua_getfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_SERVER);
  275: 	srv = lua_touserdata(L, -1);
  276: 	lua_pop(L, 1);
  277: 
  278: 	return srv;
  279: }
  280: 
  281: static connection* magnet_get_connection(lua_State *L) {
  282: 	connection *con;
  283: 
  284: 	lua_getfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_CONNECTION);
  285: 	con = lua_touserdata(L, -1);
  286: 	lua_pop(L, 1);
  287: 
  288: 	return con;
  289: }
  290: 
  291: typedef struct {
  292: 	const char *ptr;
  293: 	size_t len;
  294: } const_buffer;
  295: 
  296: static const_buffer magnet_checkconstbuffer(lua_State *L, int index) {
  297: 	const_buffer cb;
  298: 	cb.ptr = luaL_checklstring(L, index, &cb.len);
  299: 	return cb;
  300: }
  301: 
  302: static buffer* magnet_checkbuffer(lua_State *L, int index) {
  303: 	const_buffer cb = magnet_checkconstbuffer(L, index);
  304: 	buffer *b = buffer_init();
  305: 	buffer_copy_string_len(b, cb.ptr, cb.len);
  306: 	return b;
  307: }
  308: 
  309: static int magnet_print(lua_State *L) {
  310: 	buffer *b = magnet_checkbuffer(L, 1);
  311: 
  312: 	log_error_write(magnet_get_server(L), __FILE__, __LINE__, "sB",
  313: 		"(lua-print)",
  314: 		b);
  315: 
  316: 	buffer_free(b);
  317: 
  318: 	return 0;
  319: }
  320: 
  321: static int magnet_stat(lua_State *L) {
  322: 	buffer *sb = magnet_checkbuffer(L, 1);
  323: 	server *srv = magnet_get_server(L);
  324: 	connection *con = magnet_get_connection(L);
  325: 	stat_cache_entry *sce = NULL;
  326: 	handler_t res;
  327: 
  328: 	res = stat_cache_get_entry(srv, con, sb, &sce);
  329: 	buffer_free(sb);
  330: 
  331: 	if (HANDLER_GO_ON != res) {
  332: 		lua_pushnil(L);
  333: 		return 1;
  334: 	}
  335: 
  336: 	lua_newtable(L); // return value
  337: 
  338: 	lua_pushboolean(L, S_ISREG(sce->st.st_mode));
  339: 	lua_setfield(L, -2, "is_file");
  340: 
  341: 	lua_pushboolean(L, S_ISDIR(sce->st.st_mode));
  342: 	lua_setfield(L, -2, "is_dir");
  343: 
  344: 	lua_pushboolean(L, S_ISCHR(sce->st.st_mode));
  345: 	lua_setfield(L, -2, "is_char");
  346: 
  347: 	lua_pushboolean(L, S_ISBLK(sce->st.st_mode));
  348: 	lua_setfield(L, -2, "is_block");
  349: 
  350: 	lua_pushboolean(L, S_ISSOCK(sce->st.st_mode));
  351: 	lua_setfield(L, -2, "is_socket");
  352: 
  353: 	lua_pushboolean(L, S_ISLNK(sce->st.st_mode));
  354: 	lua_setfield(L, -2, "is_link");
  355: 
  356: 	lua_pushboolean(L, S_ISFIFO(sce->st.st_mode));
  357: 	lua_setfield(L, -2, "is_fifo");
  358: 
  359: 	lua_pushinteger(L, sce->st.st_mtime);
  360: 	lua_setfield(L, -2, "st_mtime");
  361: 
  362: 	lua_pushinteger(L, sce->st.st_ctime);
  363: 	lua_setfield(L, -2, "st_ctime");
  364: 
  365: 	lua_pushinteger(L, sce->st.st_atime);
  366: 	lua_setfield(L, -2, "st_atime");
  367: 
  368: 	lua_pushinteger(L, sce->st.st_uid);
  369: 	lua_setfield(L, -2, "st_uid");
  370: 
  371: 	lua_pushinteger(L, sce->st.st_gid);
  372: 	lua_setfield(L, -2, "st_gid");
  373: 
  374: 	lua_pushinteger(L, sce->st.st_size);
  375: 	lua_setfield(L, -2, "st_size");
  376: 
  377: 	lua_pushinteger(L, sce->st.st_ino);
  378: 	lua_setfield(L, -2, "st_ino");
  379: 
  380: 	if (!buffer_string_is_empty(sce->etag)) {
  381: 		/* we have to mutate the etag */
  382: 		buffer *b = buffer_init();
  383: 		etag_mutate(b, sce->etag);
  384: 
  385: 		lua_pushlstring(L, CONST_BUF_LEN(b));
  386: 		buffer_free(b);
  387: 	} else {
  388: 		lua_pushnil(L);
  389: 	}
  390: 	lua_setfield(L, -2, "etag");
  391: 
  392: 	if (!buffer_string_is_empty(sce->content_type)) {
  393: 		lua_pushlstring(L, CONST_BUF_LEN(sce->content_type));
  394: 	} else {
  395: 		lua_pushnil(L);
  396: 	}
  397: 	lua_setfield(L, -2, "content-type");
  398: 
  399: 	return 1;
  400: }
  401: 
  402: 
  403: static int magnet_atpanic(lua_State *L) {
  404: 	buffer *b = magnet_checkbuffer(L, 1);
  405: 
  406: 	log_error_write(magnet_get_server(L), __FILE__, __LINE__, "sB",
  407: 		"(lua-atpanic)",
  408: 		b);
  409: 
  410: 	buffer_free(b);
  411: 
  412: 	longjmp(exceptionjmp, 1);
  413: }
  414: 
  415: static int magnet_reqhdr_get(lua_State *L) {
  416: 	connection *con = magnet_get_connection(L);
  417: 	data_string *ds;
  418: 
  419: 	/* __index: param 1 is the (empty) table the value was not found in */
  420: 	const char *key = luaL_checkstring(L, 2);
  421: 
  422: 	if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key))) {
  423: 		if (!buffer_is_empty(ds->value)) {
  424: 			lua_pushlstring(L, CONST_BUF_LEN(ds->value));
  425: 		} else {
  426: 			lua_pushnil(L);
  427: 		}
  428: 	} else {
  429: 		lua_pushnil(L);
  430: 	}
  431: 	return 1;
  432: }
  433: 
  434: static int magnet_reqhdr_pairs(lua_State *L) {
  435: 	connection *con = magnet_get_connection(L);
  436: 
  437: 	return magnet_array_pairs(L, con->request.headers);
  438: }
  439: 
  440: static int magnet_status_get(lua_State *L) {
  441: 	data_integer *di;
  442: 	server *srv = magnet_get_server(L);
  443: 
  444: 	/* __index: param 1 is the (empty) table the value was not found in */
  445: 	const_buffer key = magnet_checkconstbuffer(L, 2);
  446: 
  447: 	di = status_counter_get_counter(srv, key.ptr, key.len);
  448: 
  449: 	lua_pushinteger(L, (lua_Integer)di->value);
  450: 
  451: 	return 1;
  452: }
  453: 
  454: static int magnet_status_set(lua_State *L) {
  455: 	server *srv = magnet_get_server(L);
  456: 
  457: 	/* __newindex: param 1 is the (empty) table the value is supposed to be set in */
  458: 	const_buffer key = magnet_checkconstbuffer(L, 2);
  459: 	int counter = (int) luaL_checkinteger(L, 3);
  460: 
  461: 	status_counter_set(srv, key.ptr, key.len, counter);
  462: 
  463: 	return 0;
  464: }
  465: 
  466: static int magnet_status_pairs(lua_State *L) {
  467: 	server *srv = magnet_get_server(L);
  468: 
  469: 	return magnet_array_pairs(L, srv->status);
  470: }
  471: 
  472: typedef struct {
  473: 	const char *name;
  474: 	enum {
  475: 		MAGNET_ENV_UNSET,
  476: 
  477: 		MAGNET_ENV_PHYICAL_PATH,
  478: 		MAGNET_ENV_PHYICAL_REL_PATH,
  479: 		MAGNET_ENV_PHYICAL_DOC_ROOT,
  480: 		MAGNET_ENV_PHYICAL_BASEDIR,
  481: 
  482: 		MAGNET_ENV_URI_PATH,
  483: 		MAGNET_ENV_URI_PATH_RAW,
  484: 		MAGNET_ENV_URI_SCHEME,
  485: 		MAGNET_ENV_URI_AUTHORITY,
  486: 		MAGNET_ENV_URI_QUERY,
  487: 
  488: 		MAGNET_ENV_REQUEST_METHOD,
  489: 		MAGNET_ENV_REQUEST_URI,
  490: 		MAGNET_ENV_REQUEST_ORIG_URI,
  491: 		MAGNET_ENV_REQUEST_PATH_INFO,
  492: 		MAGNET_ENV_REQUEST_REMOTE_IP,
  493: 		MAGNET_ENV_REQUEST_PROTOCOL
  494: 	} type;
  495: } magnet_env_t;
  496: 
  497: static const magnet_env_t magnet_env[] = {
  498: 	{ "physical.path", MAGNET_ENV_PHYICAL_PATH },
  499: 	{ "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH },
  500: 	{ "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT },
  501: 	{ "physical.basedir", MAGNET_ENV_PHYICAL_BASEDIR },
  502: 
  503: 	{ "uri.path", MAGNET_ENV_URI_PATH },
  504: 	{ "uri.path-raw", MAGNET_ENV_URI_PATH_RAW },
  505: 	{ "uri.scheme", MAGNET_ENV_URI_SCHEME },
  506: 	{ "uri.authority", MAGNET_ENV_URI_AUTHORITY },
  507: 	{ "uri.query", MAGNET_ENV_URI_QUERY },
  508: 
  509: 	{ "request.method", MAGNET_ENV_REQUEST_METHOD },
  510: 	{ "request.uri", MAGNET_ENV_REQUEST_URI },
  511: 	{ "request.orig-uri", MAGNET_ENV_REQUEST_ORIG_URI },
  512: 	{ "request.path-info", MAGNET_ENV_REQUEST_PATH_INFO },
  513: 	{ "request.remote-ip", MAGNET_ENV_REQUEST_REMOTE_IP },
  514: 	{ "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL },
  515: 
  516: 	{ NULL, MAGNET_ENV_UNSET }
  517: };
  518: 
  519: static buffer *magnet_env_get_buffer_by_id(server *srv, connection *con, int id) {
  520: 	buffer *dest = NULL;
  521: 
  522: 	UNUSED(srv);
  523: 
  524: 	/**
  525: 	 * map all internal variables to lua
  526: 	 *
  527: 	 */
  528: 
  529: 	switch (id) {
  530: 	case MAGNET_ENV_PHYICAL_PATH: dest = con->physical.path; break;
  531: 	case MAGNET_ENV_PHYICAL_REL_PATH: dest = con->physical.rel_path; break;
  532: 	case MAGNET_ENV_PHYICAL_DOC_ROOT: dest = con->physical.doc_root; break;
  533: 	case MAGNET_ENV_PHYICAL_BASEDIR: dest = con->physical.basedir; break;
  534: 
  535: 	case MAGNET_ENV_URI_PATH: dest = con->uri.path; break;
  536: 	case MAGNET_ENV_URI_PATH_RAW: dest = con->uri.path_raw; break;
  537: 	case MAGNET_ENV_URI_SCHEME: dest = con->uri.scheme; break;
  538: 	case MAGNET_ENV_URI_AUTHORITY: dest = con->uri.authority; break;
  539: 	case MAGNET_ENV_URI_QUERY: dest = con->uri.query; break;
  540: 
  541: 	case MAGNET_ENV_REQUEST_METHOD:
  542: 		buffer_copy_string(srv->tmp_buf, get_http_method_name(con->request.http_method));
  543: 		dest = srv->tmp_buf;
  544: 		break;
  545: 	case MAGNET_ENV_REQUEST_URI:      dest = con->request.uri; break;
  546: 	case MAGNET_ENV_REQUEST_ORIG_URI: dest = con->request.orig_uri; break;
  547: 	case MAGNET_ENV_REQUEST_PATH_INFO: dest = con->request.pathinfo; break;
  548: 	case MAGNET_ENV_REQUEST_REMOTE_IP: dest = con->dst_addr_buf; break;
  549: 	case MAGNET_ENV_REQUEST_PROTOCOL:
  550: 		buffer_copy_string(srv->tmp_buf, get_http_version_name(con->request.http_version));
  551: 		dest = srv->tmp_buf;
  552: 		break;
  553: 
  554: 	case MAGNET_ENV_UNSET: break;
  555: 	}
  556: 
  557: 	return dest;
  558: }
  559: 
  560: static buffer *magnet_env_get_buffer(server *srv, connection *con, const char *key) {
  561: 	size_t i;
  562: 
  563: 	for (i = 0; magnet_env[i].name; i++) {
  564: 		if (0 == strcmp(key, magnet_env[i].name)) break;
  565: 	}
  566: 
  567: 	return magnet_env_get_buffer_by_id(srv, con, magnet_env[i].type);
  568: }
  569: 
  570: static int magnet_env_get(lua_State *L) {
  571: 	server *srv = magnet_get_server(L);
  572: 	connection *con = magnet_get_connection(L);
  573: 
  574: 	/* __index: param 1 is the (empty) table the value was not found in */
  575: 	const char *key = luaL_checkstring(L, 2);
  576: 	buffer *dest = NULL;
  577: 
  578: 	dest = magnet_env_get_buffer(srv, con, key);
  579: 
  580: 	if (!buffer_is_empty(dest)) {
  581: 		lua_pushlstring(L, CONST_BUF_LEN(dest));
  582: 	} else {
  583: 		lua_pushnil(L);
  584: 	}
  585: 
  586: 	return 1;
  587: }
  588: 
  589: static int magnet_env_set(lua_State *L) {
  590: 	server *srv = magnet_get_server(L);
  591: 	connection *con = magnet_get_connection(L);
  592: 
  593: 	/* __newindex: param 1 is the (empty) table the value is supposed to be set in */
  594: 	const char *key = luaL_checkstring(L, 2);
  595: 	buffer *dest = NULL;
  596: 
  597: 	luaL_checkany(L, 3); /* nil or a string */
  598: 
  599: 	if (NULL != (dest = magnet_env_get_buffer(srv, con, key))) {
  600: 		if (lua_isnil(L, 3)) {
  601: 			buffer_reset(dest);
  602: 		} else {
  603: 			const_buffer val = magnet_checkconstbuffer(L, 3);
  604: 			buffer_copy_string_len(dest, val.ptr, val.len);
  605: 		}
  606: 	} else {
  607: 		/* couldn't save */
  608: 
  609: 		return luaL_error(L, "couldn't store '%s' in lighty.env[]", key);
  610: 	}
  611: 
  612: 	return 0;
  613: }
  614: 
  615: static int magnet_env_next(lua_State *L) {
  616: 	server *srv = magnet_get_server(L);
  617: 	connection *con = magnet_get_connection(L);
  618: 	const int pos = lua_tointeger(L, lua_upvalueindex(1));
  619: 
  620: 	buffer *dest;
  621: 
  622: 	/* ignore previous key: use upvalue for current pos */
  623: 	lua_settop(L, 0);
  624: 
  625: 	if (NULL == magnet_env[pos].name) return 0; /* end of list */
  626: 	/* Update our positional upval to reflect our new current position */
  627: 	lua_pushinteger(L, pos + 1);
  628: 	lua_replace(L, lua_upvalueindex(1));
  629: 
  630: 	/* key to return */
  631: 	lua_pushstring(L, magnet_env[pos].name);
  632: 
  633: 	/* get value */
  634: 	dest = magnet_env_get_buffer_by_id(srv, con, magnet_env[pos].type);
  635: 	if (!buffer_is_empty(dest)) {
  636: 		lua_pushlstring(L, CONST_BUF_LEN(dest));
  637: 	} else {
  638: 		lua_pushnil(L);
  639: 	}
  640: 
  641: 	/* return 2 items on the stack (key, value) */
  642: 	return 2;
  643: }
  644: 
  645: static int magnet_env_pairs(lua_State *L) {
  646: 	lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */
  647: 	lua_pushcclosure(L, magnet_env_next, 1); /* Push our new closure with 1 upvals */
  648: 	return 1;
  649: }
  650: 
  651: static int magnet_cgi_get(lua_State *L) {
  652: 	connection *con = magnet_get_connection(L);
  653: 	data_string *ds;
  654: 
  655: 	/* __index: param 1 is the (empty) table the value was not found in */
  656: 	const char *key = luaL_checkstring(L, 2);
  657: 
  658: 	ds = (data_string *)array_get_element(con->environment, key);
  659: 	if (NULL != ds && !buffer_is_empty(ds->value))
  660: 		lua_pushlstring(L, CONST_BUF_LEN(ds->value));
  661: 	else
  662: 		lua_pushnil(L);
  663: 
  664: 	return 1;
  665: }
  666: 
  667: static int magnet_cgi_set(lua_State *L) {
  668: 	connection *con = magnet_get_connection(L);
  669: 
  670: 	/* __newindex: param 1 is the (empty) table the value is supposed to be set in */
  671: 	const_buffer key = magnet_checkconstbuffer(L, 2);
  672: 	const_buffer val = magnet_checkconstbuffer(L, 2);
  673: 
  674: 	array_set_key_value(con->environment, key.ptr, key.len, val.ptr, val.len);
  675: 
  676: 	return 0;
  677: }
  678: 
  679: static int magnet_cgi_pairs(lua_State *L) {
  680: 	connection *con = magnet_get_connection(L);
  681: 
  682: 	return magnet_array_pairs(L, con->environment);
  683: }
  684: 
  685: 
  686: static int magnet_copy_response_header(server *srv, connection *con, lua_State *L, int lighty_table_ndx) {
  687: 	force_assert(lua_istable(L, lighty_table_ndx));
  688: 
  689: 	lua_getfield(L, lighty_table_ndx, "header"); /* lighty.header */
  690: 	if (lua_istable(L, -1)) {
  691: 		/* header is found, and is a table */
  692: 
  693: 		lua_pushnil(L);
  694: 		while (lua_next(L, -2) != 0) {
  695: 			if (lua_isstring(L, -1) && lua_isstring(L, -2)) {
  696: 				const_buffer key = magnet_checkconstbuffer(L, -2);
  697: 				const_buffer val = magnet_checkconstbuffer(L, -1);
  698: 
  699: 				response_header_overwrite(srv, con, key.ptr, key.len, val.ptr, val.len);
  700: 			}
  701: 
  702: 			lua_pop(L, 1);
  703: 		}
  704: 	}
  705: 	lua_pop(L, 1); /* pop lighty.header */
  706: 
  707: 	return 0;
  708: }
  709: 
  710: /**
  711:  * walk through the content array
  712:  *
  713:  * content = { "<pre>", { file = "/content" } , "</pre>" }
  714:  *
  715:  * header["Content-Type"] = "text/html"
  716:  *
  717:  * return 200
  718:  */
  719: static int magnet_attach_content(server *srv, connection *con, lua_State *L, int lighty_table_ndx) {
  720: 	force_assert(lua_istable(L, lighty_table_ndx));
  721: 
  722: 	lua_getfield(L, lighty_table_ndx, "content"); /* lighty.content */
  723: 	if (lua_istable(L, -1)) {
  724: 		int i;
  725: 		/* content is found, and is a table */
  726: 
  727: 		for (i = 1; ; i++) {
  728: 			lua_rawgeti(L, -1, i);
  729: 
  730: 			/* -1 is the value and should be the value ... aka a table */
  731: 			if (lua_isstring(L, -1)) {
  732: 				const_buffer data = magnet_checkconstbuffer(L, -1);
  733: 
  734: 				chunkqueue_append_mem(con->write_queue, data.ptr, data.len);
  735: 			} else if (lua_istable(L, -1)) {
  736: 				lua_getfield(L, -1, "filename");
  737: 				lua_getfield(L, -2, "length"); /* (0-based) end of range (not actually "length") */
  738: 				lua_getfield(L, -3, "offset"); /* (0-based) start of range */
  739: 
  740: 				if (lua_isstring(L, -3)) { /* filename has to be a string */
  741: 					buffer *fn = magnet_checkbuffer(L, -3);
  742: 					off_t off = (off_t) luaL_optinteger(L, -1, 0);
  743: 					off_t len = (off_t) luaL_optinteger(L, -2, -1); /*(-1 to http_chunk_append_file_range() uses file size minus offset)*/
  744: 					if (off < 0) {
  745: 						buffer_free(fn);
  746: 						return luaL_error(L, "offset for '%s' is negative", lua_tostring(L, -3));
  747: 					}
  748: 
  749: 					if (len >= off) {
  750: 						len -= off;
  751: 					} else if (-1 != len) {
  752: 						buffer_free(fn);
  753: 						return luaL_error(L, "offset > length for '%s'", lua_tostring(L, -3));
  754: 					}
  755: 
  756: 					if (0 != len && 0 != http_chunk_append_file_range(srv, con, fn, off, len)) {
  757: 						buffer_free(fn);
  758: 						return luaL_error(L, "error opening file content '%s' at offset %lld", lua_tostring(L, -3), (long long)off);
  759: 					}
  760: 
  761: 					buffer_free(fn);
  762: 				} else {
  763: 					return luaL_error(L, "content[%d] is a table and requires the field \"filename\"", i);
  764: 				}
  765: 
  766: 				lua_pop(L, 3);
  767: 			} else if (lua_isnil(L, -1)) {
  768: 				/* end of list */
  769: 
  770: 				lua_pop(L, 1);
  771: 
  772: 				break;
  773: 			} else {
  774: 				return luaL_error(L, "content[%d] is neither a string nor a table: ", i);
  775: 			}
  776: 
  777: 			lua_pop(L, 1); /* pop the content[...] entry value */
  778: 		}
  779: 	} else {
  780: 		return luaL_error(L, "lighty.content has to be a table");
  781: 	}
  782: 	lua_pop(L, 1); /* pop lighty.content */
  783: 
  784: 	return 0;
  785: }
  786: 
  787: static int traceback(lua_State *L) {
  788: 	if (!lua_isstring(L, 1))  /* 'message' not a string? */
  789: 		return 1;  /* keep it intact */
  790: 	lua_getglobal(L, "debug");
  791: 	if (!lua_istable(L, -1)) {
  792: 		lua_pop(L, 1);
  793: 		return 1;
  794: 	}
  795: 	lua_getfield(L, -1, "traceback");
  796: 	if (!lua_isfunction(L, -1)) {
  797: 		lua_pop(L, 2);
  798: 		return 1;
  799: 	}
  800: 	lua_pushvalue(L, 1);  /* pass error message */
  801: 	lua_pushinteger(L, 2);  /* skip this function and traceback */
  802: 	lua_call(L, 2, 1);  /* call debug.traceback */
  803: 	return 1;
  804: }
  805: 
  806: /* push traceback function before calling lua_pcall after narg arguments
  807:  * have been pushed (inserts it before the arguments). returns index for
  808:  * traceback function ("msgh" in lua_pcall)
  809:  */
  810: static int push_traceback(lua_State *L, int narg) {
  811: 	int base = lua_gettop(L) - narg;  /* function index */
  812: 	lua_pushcfunction(L, traceback);
  813: 	lua_insert(L, base);
  814: 	return base;
  815: }
  816: 
  817: static handler_t magnet_attract(server *srv, connection *con, plugin_data *p, buffer *name) {
  818: 	lua_State *L;
  819: 	int lua_return_value;
  820: 	const int func_ndx = 1;
  821: 	const int lighty_table_ndx = 2;
  822: 
  823: 	/* get the script-context */
  824: 	L = script_cache_get_script(srv, con, p->cache, name);
  825: 
  826: 	if (lua_isstring(L, -1)) {
  827: 		log_error_write(srv, __FILE__, __LINE__,
  828: 				"sbss",
  829: 				"loading script",
  830: 				name,
  831: 				"failed:",
  832: 				lua_tostring(L, -1));
  833: 
  834: 		lua_pop(L, 1);
  835: 
  836: 		force_assert(lua_gettop(L) == 0); /* only the error should have been on the stack */
  837: 
  838: 		con->http_status = 500;
  839: 		con->mode = DIRECT;
  840: 
  841: 		return HANDLER_FINISHED;
  842: 	}
  843: 
  844: 	force_assert(lua_gettop(L) == 1);
  845: 	force_assert(lua_isfunction(L, func_ndx));
  846: 
  847: 	lua_pushlightuserdata(L, srv);
  848: 	lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_SERVER);
  849: 
  850: 	lua_pushlightuserdata(L, con);
  851: 	lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_CONNECTION);
  852: 
  853: 	lua_atpanic(L, magnet_atpanic);
  854: 
  855: 	/**
  856: 	 * we want to create empty environment for our script
  857: 	 *
  858: 	 * setmetatable({}, {__index = _G})
  859: 	 *
  860: 	 * if a function symbol is not defined in our env, __index will lookup
  861: 	 * in the global env.
  862: 	 *
  863: 	 * all variables created in the script-env will be thrown
  864: 	 * away at the end of the script run.
  865: 	 */
  866: 	lua_newtable(L); /* my empty environment aka {}              (sp += 1) */
  867: 
  868: 	/* we have to overwrite the print function */
  869: 	lua_pushcfunction(L, magnet_print);                       /* (sp += 1) */
  870: 	lua_setfield(L, -2, "print"); /* -1 is the env we want to set(sp -= 1) */
  871: 
  872: 	/**
  873: 	 * lighty.request[] (ro) has the HTTP-request headers
  874: 	 * lighty.env[] (rw) has various url/physical file paths and
  875: 	 *                   request meta data; might contain nil values
  876: 	 * lighty.req_env[] (ro) has the cgi environment
  877: 	 * lighty.status[] (ro) has the status counters
  878: 	 * lighty.content[] (rw) is a table of string/file
  879: 	 * lighty.header[] (rw) is a array to set response headers
  880: 	 */
  881: 
  882: 	lua_newtable(L); /* lighty.*                                 (sp += 1) */
  883: 
  884: 	lua_newtable(L); /*  {}                                      (sp += 1) */
  885: 	lua_newtable(L); /* the meta-table for the request-table     (sp += 1) */
  886: 	lua_pushcfunction(L, magnet_reqhdr_get);                  /* (sp += 1) */
  887: 	lua_setfield(L, -2, "__index");                           /* (sp -= 1) */
  888: 	lua_pushcfunction(L, magnet_reqhdr_pairs);                /* (sp += 1) */
  889: 	lua_setfield(L, -2, "__pairs");                           /* (sp -= 1) */
  890: 	lua_setmetatable(L, -2); /* tie the metatable to request     (sp -= 1) */
  891: 	lua_setfield(L, -2, "request"); /* content = {}              (sp -= 1) */
  892: 
  893: 	lua_newtable(L); /*  {}                                      (sp += 1) */
  894: 	lua_newtable(L); /* the meta-table for the env-table         (sp += 1) */
  895: 	lua_pushcfunction(L, magnet_env_get);                     /* (sp += 1) */
  896: 	lua_setfield(L, -2, "__index");                           /* (sp -= 1) */
  897: 	lua_pushcfunction(L, magnet_env_set);                     /* (sp += 1) */
  898: 	lua_setfield(L, -2, "__newindex");                        /* (sp -= 1) */
  899: 	lua_pushcfunction(L, magnet_env_pairs);                   /* (sp += 1) */
  900: 	lua_setfield(L, -2, "__pairs");                           /* (sp -= 1) */
  901: 	lua_setmetatable(L, -2); /* tie the metatable to env         (sp -= 1) */
  902: 	lua_setfield(L, -2, "env"); /* content = {}                  (sp -= 1) */
  903: 
  904: 	lua_newtable(L); /*  {}                                      (sp += 1) */
  905: 	lua_newtable(L); /* the meta-table for the req_env-table     (sp += 1) */
  906: 	lua_pushcfunction(L, magnet_cgi_get);                     /* (sp += 1) */
  907: 	lua_setfield(L, -2, "__index");                           /* (sp -= 1) */
  908: 	lua_pushcfunction(L, magnet_cgi_set);                     /* (sp += 1) */
  909: 	lua_setfield(L, -2, "__newindex");                        /* (sp -= 1) */
  910: 	lua_pushcfunction(L, magnet_cgi_pairs);                   /* (sp += 1) */
  911: 	lua_setfield(L, -2, "__pairs");                           /* (sp -= 1) */
  912: 	lua_setmetatable(L, -2); /* tie the metatable to req_env     (sp -= 1) */
  913: 	lua_setfield(L, -2, "req_env"); /* content = {}              (sp -= 1) */
  914: 
  915: 	lua_newtable(L); /*  {}                                      (sp += 1) */
  916: 	lua_newtable(L); /* the meta-table for the status-table      (sp += 1) */
  917: 	lua_pushcfunction(L, magnet_status_get);                  /* (sp += 1) */
  918: 	lua_setfield(L, -2, "__index");                           /* (sp -= 1) */
  919: 	lua_pushcfunction(L, magnet_status_set);                  /* (sp += 1) */
  920: 	lua_setfield(L, -2, "__newindex");                        /* (sp -= 1) */
  921: 	lua_pushcfunction(L, magnet_status_pairs);                /* (sp += 1) */
  922: 	lua_setfield(L, -2, "__pairs");                           /* (sp -= 1) */
  923: 	lua_setmetatable(L, -2); /* tie the metatable to statzs      (sp -= 1) */
  924: 	lua_setfield(L, -2, "status"); /* content = {}               (sp -= 1) */
  925: 
  926: 	/* add empty 'content' and 'header' tables */
  927: 	lua_newtable(L); /*  {}                                      (sp += 1) */
  928: 	lua_setfield(L, -2, "content"); /* content = {}              (sp -= 1) */
  929: 
  930: 	lua_newtable(L); /*  {}                                      (sp += 1) */
  931: 	lua_setfield(L, -2, "header"); /* header = {}                (sp -= 1) */
  932: 
  933: 	lua_pushinteger(L, MAGNET_RESTART_REQUEST);
  934: 	lua_setfield(L, -2, "RESTART_REQUEST");
  935: 
  936: 	lua_pushcfunction(L, magnet_stat);                        /* (sp += 1) */
  937: 	lua_setfield(L, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */
  938: 
  939: 	/* insert lighty table at index 2 */
  940: 	lua_pushvalue(L, -1);
  941: 	lua_insert(L, lighty_table_ndx);
  942: 
  943: 	lua_setfield(L, -2, "lighty"); /* lighty.*                   (sp -= 1) */
  944: 
  945: #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
  946: 	/* override the default pairs() function to our __pairs capable version;
  947: 	 * not needed for lua 5.2+
  948: 	 */
  949: 	lua_getglobal(L, "pairs"); /* push original pairs()          (sp += 1) */
  950: 	lua_pushcclosure(L, magnet_pairs, 1);
  951: 	lua_setfield(L, -2, "pairs");                             /* (sp -= 1) */
  952: #endif
  953: 
  954: 	lua_newtable(L); /* the meta-table for the new env           (sp += 1) */
  955: 	lua_pushglobaltable(L);                                   /* (sp += 1) */
  956: 	lua_setfield(L, -2, "__index"); /* { __index = _G }          (sp -= 1) */
  957: 	lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */
  958: 
  959: 	magnet_setfenv_mainfn(L, 1);                              /* (sp -= 1) */
  960: 
  961: 	/* pcall will destroy the func value, duplicate it */     /* (sp += 1) */
  962: 	lua_pushvalue(L, func_ndx);
  963: 	{
  964: 		int errfunc = push_traceback(L, 0);
  965: 		int ret = lua_pcall(L, 0, 1, errfunc);
  966: 		lua_remove(L, errfunc);
  967: 
  968: 		/* reset environment */
  969: 		lua_pushglobaltable(L);                               /* (sp += 1) */
  970: 		magnet_setfenv_mainfn(L, 1);                          /* (sp -= 1) */
  971: 
  972: 		if (0 != ret) {
  973: 			log_error_write(srv, __FILE__, __LINE__,
  974: 				"ss",
  975: 				"lua_pcall():",
  976: 				lua_tostring(L, -1));
  977: 			lua_pop(L, 2); /* remove the error-msg and the lighty table at index 2 */
  978: 
  979: 			force_assert(lua_gettop(L) == 1); /* only the function should be on the stack */
  980: 
  981: 			con->http_status = 500;
  982: 			con->mode = DIRECT;
  983: 
  984: 			return HANDLER_FINISHED;
  985: 		}
  986: 	}
  987: 
  988: 	/* we should have the function, the lighty table and the return value on the stack */
  989: 	force_assert(lua_gettop(L) == 3);
  990: 
  991: 	lua_return_value = (int) luaL_optinteger(L, -1, -1);
  992: 	lua_pop(L, 1); /* pop return value */
  993: 
  994: 	magnet_copy_response_header(srv, con, L, lighty_table_ndx);
  995: 
  996: 	{
  997: 		handler_t result = HANDLER_GO_ON;
  998: 
  999: 		if (lua_return_value > 99) {
 1000: 			con->http_status = lua_return_value;
 1001: 			con->file_finished = 1;
 1002: 
 1003: 			/* try { ...*/
 1004: 			if (0 == setjmp(exceptionjmp)) {
 1005: 				magnet_attach_content(srv, con, L, lighty_table_ndx);
 1006: 				if (!chunkqueue_is_empty(con->write_queue)) {
 1007: 					con->mode = p->id;
 1008: 				}
 1009: 			} else {
 1010: 				lua_settop(L, 2); /* remove all but function and lighty table */
 1011: 				/* } catch () { */
 1012: 				con->http_status = 500;
 1013: 				con->mode = DIRECT;
 1014: 			}
 1015: 
 1016: 			result = HANDLER_FINISHED;
 1017: 		} else if (MAGNET_RESTART_REQUEST == lua_return_value) {
 1018: 			result = HANDLER_COMEBACK;
 1019: 		}
 1020: 
 1021: 		lua_pop(L, 1); /* pop the lighty table */
 1022: 		force_assert(lua_gettop(L) == 1); /* only the function should remain on the stack */
 1023: 
 1024: 		return result;
 1025: 	}
 1026: }
 1027: 
 1028: static handler_t magnet_attract_array(server *srv, connection *con, plugin_data *p, array *files) {
 1029: 	size_t i;
 1030: 	handler_t ret = HANDLER_GO_ON;
 1031: 
 1032: 	/* no filename set */
 1033: 	if (files->used == 0) return HANDLER_GO_ON;
 1034: 
 1035: 	/**
 1036: 	 * execute all files and jump out on the first !HANDLER_GO_ON
 1037: 	 */
 1038: 	for (i = 0; i < files->used && ret == HANDLER_GO_ON; i++) {
 1039: 		data_string *ds = (data_string *)files->data[i];
 1040: 
 1041: 		if (buffer_string_is_empty(ds->value)) continue;
 1042: 
 1043: 		ret = magnet_attract(srv, con, p, ds->value);
 1044: 	}
 1045: 
 1046: 	if (con->error_handler_saved_status) {
 1047: 		/* retrieve (possibly modified) REDIRECT_STATUS and store as number */
 1048: 		unsigned long x;
 1049: 		data_string * const ds = (data_string *)array_get_element(con->environment, "REDIRECT_STATUS");
 1050: 		if (ds && (x = strtoul(ds->value->ptr, NULL, 10)) < 1000)
 1051: 			/*(simplified validity check x < 1000)*/
 1052: 			con->error_handler_saved_status =
 1053: 			  con->error_handler_saved_status > 0 ? (int)x : -(int)x;
 1054: 	}
 1055: 
 1056: 	return ret;
 1057: }
 1058: 
 1059: URIHANDLER_FUNC(mod_magnet_uri_handler) {
 1060: 	plugin_data *p = p_d;
 1061: 
 1062: 	mod_magnet_patch_connection(srv, con, p);
 1063: 
 1064: 	return magnet_attract_array(srv, con, p, p->conf.url_raw);
 1065: }
 1066: 
 1067: URIHANDLER_FUNC(mod_magnet_physical) {
 1068: 	plugin_data *p = p_d;
 1069: 
 1070: 	mod_magnet_patch_connection(srv, con, p);
 1071: 
 1072: 	return magnet_attract_array(srv, con, p, p->conf.physical_path);
 1073: }
 1074: 
 1075: 
 1076: /* this function is called at dlopen() time and inits the callbacks */
 1077: 
 1078: int mod_magnet_plugin_init(plugin *p);
 1079: int mod_magnet_plugin_init(plugin *p) {
 1080: 	p->version     = LIGHTTPD_VERSION_ID;
 1081: 	p->name        = buffer_init_string("magnet");
 1082: 
 1083: 	p->init        = mod_magnet_init;
 1084: 	p->handle_uri_clean  = mod_magnet_uri_handler;
 1085: 	p->handle_physical   = mod_magnet_physical;
 1086: 	p->set_defaults  = mod_magnet_set_defaults;
 1087: 	p->cleanup     = mod_magnet_free;
 1088: 
 1089: 	p->data        = NULL;
 1090: 
 1091: 	return 0;
 1092: }
 1093: 
 1094: #else
 1095: 
 1096: #pragma message("lua is required, but was not found")
 1097: 
 1098: int mod_magnet_plugin_init(plugin *p);
 1099: int mod_magnet_plugin_init(plugin *p) {
 1100: 	UNUSED(p);
 1101: 	return -1;
 1102: }
 1103: #endif

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