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

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