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 */
INIT_FUNC(mod_magnet_init)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 */
FREE_FUNC(mod_magnet_free)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 
SETDEFAULTS_FUNC(mod_magnet_set_defaults)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(specific_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;
mod_magnet_patch_connection(server * srv,connection * con,plugin_data * p)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 */
magnet_pairs(lua_State * L)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) */
magnet_array_next(lua_State * L)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 */
magnet_array_pairs(lua_State * L,array * a)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 
magnet_print(lua_State * L)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 
magnet_stat(lua_State * L)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 
magnet_atpanic(lua_State * L)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 
magnet_reqhdr_get(lua_State * L)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 
magnet_reqhdr_pairs(lua_State * L)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 
magnet_status_get(lua_State * L)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 
magnet_status_set(lua_State * L)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 
magnet_status_pairs(lua_State * L)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 
450 		MAGNET_ENV_URI_PATH,
451 		MAGNET_ENV_URI_PATH_RAW,
452 		MAGNET_ENV_URI_SCHEME,
453 		MAGNET_ENV_URI_AUTHORITY,
454 		MAGNET_ENV_URI_QUERY,
455 
456 		MAGNET_ENV_REQUEST_METHOD,
457 		MAGNET_ENV_REQUEST_URI,
458 		MAGNET_ENV_REQUEST_ORIG_URI,
459 		MAGNET_ENV_REQUEST_PATH_INFO,
460 		MAGNET_ENV_REQUEST_REMOTE_IP,
461 		MAGNET_ENV_REQUEST_PROTOCOL
462 	} type;
463 } magnet_env_t;
464 
465 static const magnet_env_t magnet_env[] = {
466 	{ "physical.path", MAGNET_ENV_PHYICAL_PATH },
467 	{ "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH },
468 	{ "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT },
469 
470 	{ "uri.path", MAGNET_ENV_URI_PATH },
471 	{ "uri.path-raw", MAGNET_ENV_URI_PATH_RAW },
472 	{ "uri.scheme", MAGNET_ENV_URI_SCHEME },
473 	{ "uri.authority", MAGNET_ENV_URI_AUTHORITY },
474 	{ "uri.query", MAGNET_ENV_URI_QUERY },
475 
476 	{ "request.method", MAGNET_ENV_REQUEST_METHOD },
477 	{ "request.uri", MAGNET_ENV_REQUEST_URI },
478 	{ "request.orig-uri", MAGNET_ENV_REQUEST_ORIG_URI },
479 	{ "request.path-info", MAGNET_ENV_REQUEST_PATH_INFO },
480 	{ "request.remote-ip", MAGNET_ENV_REQUEST_REMOTE_IP },
481 	{ "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL },
482 
483 	{ NULL, MAGNET_ENV_UNSET }
484 };
485 
magnet_env_get_buffer_by_id(server * srv,connection * con,int id)486 static buffer *magnet_env_get_buffer_by_id(server *srv, connection *con, int id) {
487 	buffer *dest = NULL;
488 
489 	UNUSED(srv);
490 
491 	/**
492 	 * map all internal variables to lua
493 	 *
494 	 */
495 
496 	switch (id) {
497 	case MAGNET_ENV_PHYICAL_PATH: dest = con->physical.path; break;
498 	case MAGNET_ENV_PHYICAL_REL_PATH: dest = con->physical.rel_path; break;
499 	case MAGNET_ENV_PHYICAL_DOC_ROOT: dest = con->physical.doc_root; break;
500 
501 	case MAGNET_ENV_URI_PATH: dest = con->uri.path; break;
502 	case MAGNET_ENV_URI_PATH_RAW: dest = con->uri.path_raw; break;
503 	case MAGNET_ENV_URI_SCHEME: dest = con->uri.scheme; break;
504 	case MAGNET_ENV_URI_AUTHORITY: dest = con->uri.authority; break;
505 	case MAGNET_ENV_URI_QUERY: dest = con->uri.query; break;
506 
507 	case MAGNET_ENV_REQUEST_METHOD:
508 		buffer_copy_string(srv->tmp_buf, get_http_method_name(con->request.http_method));
509 		dest = srv->tmp_buf;
510 		break;
511 	case MAGNET_ENV_REQUEST_URI:      dest = con->request.uri; break;
512 	case MAGNET_ENV_REQUEST_ORIG_URI: dest = con->request.orig_uri; break;
513 	case MAGNET_ENV_REQUEST_PATH_INFO: dest = con->request.pathinfo; break;
514 	case MAGNET_ENV_REQUEST_REMOTE_IP: dest = con->dst_addr_buf; break;
515 	case MAGNET_ENV_REQUEST_PROTOCOL:
516 		buffer_copy_string(srv->tmp_buf, get_http_version_name(con->request.http_version));
517 		dest = srv->tmp_buf;
518 		break;
519 
520 	case MAGNET_ENV_UNSET: break;
521 	}
522 
523 	return dest;
524 }
525 
magnet_env_get_buffer(server * srv,connection * con,const char * key)526 static buffer *magnet_env_get_buffer(server *srv, connection *con, const char *key) {
527 	size_t i;
528 
529 	for (i = 0; magnet_env[i].name; i++) {
530 		if (0 == strcmp(key, magnet_env[i].name)) break;
531 	}
532 
533 	return magnet_env_get_buffer_by_id(srv, con, magnet_env[i].type);
534 }
535 
magnet_env_get(lua_State * L)536 static int magnet_env_get(lua_State *L) {
537 	server *srv;
538 	connection *con;
539 
540 	const char *key = luaL_checkstring(L, 2);
541 	buffer *dest = NULL;
542 
543 	lua_pushstring(L, "lighty.srv");
544 	lua_gettable(L, LUA_REGISTRYINDEX);
545 	srv = lua_touserdata(L, -1);
546 	lua_pop(L, 1);
547 
548 	lua_pushstring(L, "lighty.con");
549 	lua_gettable(L, LUA_REGISTRYINDEX);
550 	con = lua_touserdata(L, -1);
551 	lua_pop(L, 1);
552 
553 	dest = magnet_env_get_buffer(srv, con, key);
554 
555 	if (dest && dest->used) {
556 		lua_pushlstring(L, dest->ptr, dest->used - 1);
557 	} else {
558 		lua_pushnil(L);
559 	}
560 
561 	return 1;
562 }
563 
magnet_env_set(lua_State * L)564 static int magnet_env_set(lua_State *L) {
565 	server *srv;
566 	connection *con;
567 
568 	const char *key = luaL_checkstring(L, 2);
569 	const char *val = luaL_checkstring(L, 3);
570 	buffer *dest = NULL;
571 
572 	lua_pushstring(L, "lighty.srv");
573 	lua_gettable(L, LUA_REGISTRYINDEX);
574 	srv = lua_touserdata(L, -1);
575 	lua_pop(L, 1);
576 
577 	lua_pushstring(L, "lighty.con");
578 	lua_gettable(L, LUA_REGISTRYINDEX);
579 	con = lua_touserdata(L, -1);
580 	lua_pop(L, 1);
581 
582 	if (NULL != (dest = magnet_env_get_buffer(srv, con, key))) {
583 		buffer_copy_string(dest, val);
584 	} else {
585 		/* couldn't save */
586 
587 		return luaL_error(L, "couldn't store '%s' in lighty.env[]", key);
588 	}
589 
590 	return 0;
591 }
592 
magnet_env_next(lua_State * L)593 static int magnet_env_next(lua_State *L) {
594 	server *srv;
595 	connection *con;
596 	int pos = lua_tointeger(L, lua_upvalueindex(1));
597 
598 	buffer *dest;
599 
600 	lua_pushstring(L, "lighty.srv");
601 	lua_gettable(L, LUA_REGISTRYINDEX);
602 	srv = lua_touserdata(L, -1);
603 	lua_pop(L, 1);
604 
605 	lua_pushstring(L, "lighty.con");
606 	lua_gettable(L, LUA_REGISTRYINDEX);
607 	con = lua_touserdata(L, -1);
608 	lua_pop(L, 1);
609 
610 	lua_settop(L, 0);
611 
612 	if (NULL == magnet_env[pos].name) return 0; /* end of list */
613 
614 	lua_pushstring(L, magnet_env[pos].name);
615 
616 	dest = magnet_env_get_buffer_by_id(srv, con, magnet_env[pos].type);
617 	if (dest && dest->used) {
618 		lua_pushlstring(L, dest->ptr, dest->used - 1);
619 	} else {
620 		lua_pushnil(L);
621 	}
622 
623 	/* Update our positional upval to reflect our new current position */
624 	pos++;
625 	lua_pushinteger(L, pos);
626 	lua_replace(L, lua_upvalueindex(1));
627 
628 	/* Returning 2 items on the stack (key, value) */
629 	return 2;
630 }
631 
magnet_env_pairs(lua_State * L)632 static int magnet_env_pairs(lua_State *L) {
633 	lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */
634 	lua_pushcclosure(L, magnet_env_next, 1); /* Push our new closure with 1 upvals */
635 	return 1;
636 }
637 
magnet_cgi_get(lua_State * L)638 static int magnet_cgi_get(lua_State *L) {
639 	connection *con;
640 	data_string *ds;
641 
642 	const char *key = luaL_checkstring(L, 2);
643 
644 	lua_pushstring(L, "lighty.con");
645 	lua_gettable(L, LUA_REGISTRYINDEX);
646 	con = lua_touserdata(L, -1);
647 	lua_pop(L, 1);
648 
649 	if (NULL != (ds = (data_string *)array_get_element(con->environment, key)) && ds->value->used)
650 		lua_pushlstring(L, CONST_BUF_LEN(ds->value));
651 	else
652 		lua_pushnil(L);
653 
654 	return 1;
655 }
656 
magnet_cgi_set(lua_State * L)657 static int magnet_cgi_set(lua_State *L) {
658 	connection *con;
659 
660 	const char *key = luaL_checkstring(L, 2);
661 	const char *val = luaL_checkstring(L, 3);
662 
663 	lua_pushstring(L, "lighty.con");
664 	lua_gettable(L, LUA_REGISTRYINDEX);
665 	con = lua_touserdata(L, -1);
666 	lua_pop(L, 1);
667 
668 	array_set_key_value(con->environment, key, strlen(key), val, strlen(val));
669 
670 	return 0;
671 }
672 
magnet_cgi_pairs(lua_State * L)673 static int magnet_cgi_pairs(lua_State *L) {
674 	connection *con;
675 
676 	lua_pushstring(L, "lighty.con");
677 	lua_gettable(L, LUA_REGISTRYINDEX);
678 	con = lua_touserdata(L, -1);
679 	lua_pop(L, 1);
680 
681 	return magnet_array_pairs(L, con->environment);
682 }
683 
684 
magnet_copy_response_header(server * srv,connection * con,plugin_data * p,lua_State * L)685 static int magnet_copy_response_header(server *srv, connection *con, plugin_data *p, lua_State *L) {
686 	UNUSED(p);
687 	/**
688 	 * get the environment of the function
689 	 */
690 
691 	lua_getfenv(L, -1); /* -1 is the function */
692 
693 	/* lighty.header */
694 
695 	lua_getfield(L, -1, "lighty"); /* lighty.* from the env  */
696 	assert(lua_istable(L, -1));
697 
698 	lua_getfield(L, -1, "header"); /* lighty.header */
699 	if (lua_istable(L, -1)) {
700 		/* header is found, and is a table */
701 
702 		lua_pushnil(L);
703 		while (lua_next(L, -2) != 0) {
704 			if (lua_isstring(L, -1) && lua_isstring(L, -2)) {
705 				const char *key, *val;
706 				size_t key_len, val_len;
707 
708 				key = lua_tolstring(L, -2, &key_len);
709 				val = lua_tolstring(L, -1, &val_len);
710 
711 				response_header_overwrite(srv, con, key, key_len, val, val_len);
712 			}
713 
714 			lua_pop(L, 1);
715 		}
716 	}
717 
718 	lua_pop(L, 1); /* pop the header-table */
719 	lua_pop(L, 1); /* pop the lighty-env */
720 	lua_pop(L, 1); /* pop the function env */
721 
722 	return 0;
723 }
724 
725 /**
726  * walk through the content array
727  *
728  * content = { "<pre>", { file = "/content" } , "</pre>" }
729  *
730  * header["Content-Type"] = "text/html"
731  *
732  * return 200
733  */
magnet_attach_content(server * srv,connection * con,plugin_data * p,lua_State * L)734 static int magnet_attach_content(server *srv, connection *con, plugin_data *p, lua_State *L) {
735 	UNUSED(p);
736 	/**
737 	 * get the environment of the function
738 	 */
739 
740 	assert(lua_isfunction(L, -1));
741 	lua_getfenv(L, -1); /* -1 is the function */
742 
743 	lua_getfield(L, -1, "lighty"); /* lighty.* from the env  */
744 	assert(lua_istable(L, -1));
745 
746 	lua_getfield(L, -1, "content"); /* lighty.content */
747 	if (lua_istable(L, -1)) {
748 		int i;
749 		/* header is found, and is a table */
750 
751 		for (i = 1; ; i++) {
752 			lua_rawgeti(L, -1, i);
753 
754 			/* -1 is the value and should be the value ... aka a table */
755 			if (lua_isstring(L, -1)) {
756 				size_t s_len = 0;
757 				const char *s = lua_tolstring(L, -1, &s_len);
758 
759 				chunkqueue_append_mem(con->write_queue, s, s_len + 1);
760 			} else if (lua_istable(L, -1)) {
761 				lua_getfield(L, -1, "filename");
762 				lua_getfield(L, -2, "length");
763 				lua_getfield(L, -3, "offset");
764 
765 				if (lua_isstring(L, -3)) { /* filename has to be a string */
766 					buffer *fn = buffer_init();
767 					stat_cache_entry *sce;
768 
769 					buffer_copy_string(fn, lua_tostring(L, -3));
770 
771 					if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, fn, &sce)) {
772 						off_t off = 0;
773 						off_t len = 0;
774 
775 						if (lua_isnumber(L, -1)) {
776 							off = lua_tonumber(L, -1);
777 						}
778 
779 						if (lua_isnumber(L, -2)) {
780 							len = lua_tonumber(L, -2);
781 						} else {
782 							len = sce->st.st_size;
783 						}
784 
785 						if (off < 0) {
786 							return luaL_error(L, "offset for '%s' is negative", fn->ptr);
787 						}
788 
789 						if (len < off) {
790 							return luaL_error(L, "offset > length for '%s'", fn->ptr);
791 						}
792 
793 						chunkqueue_append_file(con->write_queue, fn, off, len - off);
794 					}
795 
796 					buffer_free(fn);
797 				} else {
798 					lua_pop(L, 3 + 2); /* correct the stack */
799 
800 					return luaL_error(L, "content[%d] is a table and requires the field \"filename\"", i);
801 				}
802 
803 				lua_pop(L, 3);
804 			} else if (lua_isnil(L, -1)) {
805 				/* oops, end of list */
806 
807 				lua_pop(L, 1);
808 
809 				break;
810 			} else {
811 				lua_pop(L, 4);
812 
813 				return luaL_error(L, "content[%d] is neither a string nor a table: ", i);
814 			}
815 
816 			lua_pop(L, 1); /* pop the content[...] table */
817 		}
818 	} else {
819 		return luaL_error(L, "lighty.content has to be a table");
820 	}
821 	lua_pop(L, 1); /* pop the header-table */
822 	lua_pop(L, 1); /* pop the lighty-table */
823 	lua_pop(L, 1); /* php the function env */
824 
825 	return 0;
826 }
827 
traceback(lua_State * L)828 static int traceback (lua_State *L) {
829 	if (!lua_isstring(L, 1))  /* 'message' not a string? */
830 		return 1;  /* keep it intact */
831 	lua_getfield(L, LUA_GLOBALSINDEX, "debug");
832 	if (!lua_istable(L, -1)) {
833 		lua_pop(L, 1);
834 		return 1;
835 	}
836 	lua_getfield(L, -1, "traceback");
837 	if (!lua_isfunction(L, -1)) {
838 		lua_pop(L, 2);
839 		return 1;
840 	}
841 	lua_pushvalue(L, 1);  /* pass error message */
842 	lua_pushinteger(L, 2);  /* skip this function and traceback */
843 	lua_call(L, 2, 1);  /* call debug.traceback */
844 	return 1;
845 }
846 
push_traceback(lua_State * L,int narg)847 static int push_traceback(lua_State *L, int narg) {
848 	int base = lua_gettop(L) - narg;  /* function index */
849 	lua_pushcfunction(L, traceback);
850 	lua_insert(L, base);
851 	return base;
852 }
853 
magnet_attract(server * srv,connection * con,plugin_data * p,buffer * name)854 static handler_t magnet_attract(server *srv, connection *con, plugin_data *p, buffer *name) {
855 	lua_State *L;
856 	int lua_return_value = -1;
857 	int errfunc;
858 	/* get the script-context */
859 
860 
861 	L = script_cache_get_script(srv, con, p->cache, name);
862 
863 	if (lua_isstring(L, -1)) {
864 		log_error_write(srv, __FILE__, __LINE__,
865 				"sbss",
866 				"loading script",
867 				name,
868 				"failed:",
869 				lua_tostring(L, -1));
870 
871 		lua_pop(L, 1);
872 
873 		assert(lua_gettop(L) == 0); /* only the function should be on the stack */
874 
875 		con->http_status = 500;
876 		con->mode = DIRECT;
877 
878 		return HANDLER_FINISHED;
879 	}
880 
881 	lua_pushstring(L, "lighty.srv");
882 	lua_pushlightuserdata(L, srv);
883 	lua_settable(L, LUA_REGISTRYINDEX); /* registery[<id>] = srv */
884 
885 	lua_pushstring(L, "lighty.con");
886 	lua_pushlightuserdata(L, con);
887 	lua_settable(L, LUA_REGISTRYINDEX); /* registery[<id>] = con */
888 
889 	lua_atpanic(L, magnet_atpanic);
890 
891 	/**
892 	 * we want to create empty environment for our script
893 	 *
894 	 * setmetatable({}, {__index = _G})
895 	 *
896 	 * if a function, symbol is not defined in our env, __index will lookup
897 	 * in the global env.
898 	 *
899 	 * all variables created in the script-env will be thrown
900 	 * away at the end of the script run.
901 	 */
902 	lua_newtable(L); /* my empty environment aka {}              (sp += 1) */
903 
904 	/* we have to overwrite the print function */
905 	lua_pushcfunction(L, magnet_print);                       /* (sp += 1) */
906 	lua_setfield(L, -2, "print"); /* -1 is the env we want to set(sp -= 1) */
907 
908 	/**
909 	 * lighty.request[] has the HTTP-request headers
910 	 * lighty.content[] is a table of string/file
911 	 * lighty.header[] is a array to set response headers
912 	 */
913 
914 	lua_newtable(L); /* lighty.*                                 (sp += 1) */
915 
916 	lua_newtable(L); /*  {}                                      (sp += 1) */
917 	lua_newtable(L); /* the meta-table for the request-table     (sp += 1) */
918 	lua_pushcfunction(L, magnet_reqhdr_get);                  /* (sp += 1) */
919 	lua_setfield(L, -2, "__index");                           /* (sp -= 1) */
920 	lua_pushcfunction(L, magnet_reqhdr_pairs);                /* (sp += 1) */
921 	lua_setfield(L, -2, "__pairs");                           /* (sp -= 1) */
922 	lua_setmetatable(L, -2); /* tie the metatable to request     (sp -= 1) */
923 	lua_setfield(L, -2, "request"); /* content = {}              (sp -= 1) */
924 
925 	lua_newtable(L); /*  {}                                      (sp += 1) */
926 	lua_newtable(L); /* the meta-table for the request-table     (sp += 1) */
927 	lua_pushcfunction(L, magnet_env_get);                     /* (sp += 1) */
928 	lua_setfield(L, -2, "__index");                           /* (sp -= 1) */
929 	lua_pushcfunction(L, magnet_env_set);                     /* (sp += 1) */
930 	lua_setfield(L, -2, "__newindex");                        /* (sp -= 1) */
931 	lua_pushcfunction(L, magnet_env_pairs);                   /* (sp += 1) */
932 	lua_setfield(L, -2, "__pairs");                           /* (sp -= 1) */
933 	lua_setmetatable(L, -2); /* tie the metatable to request     (sp -= 1) */
934 	lua_setfield(L, -2, "env"); /* content = {}                  (sp -= 1) */
935 
936 	lua_newtable(L); /*  {}                                      (sp += 1) */
937 	lua_newtable(L); /* the meta-table for the request-table     (sp += 1) */
938 	lua_pushcfunction(L, magnet_cgi_get);                     /* (sp += 1) */
939 	lua_setfield(L, -2, "__index");                           /* (sp -= 1) */
940 	lua_pushcfunction(L, magnet_cgi_set);                     /* (sp += 1) */
941 	lua_setfield(L, -2, "__newindex");                        /* (sp -= 1) */
942 	lua_pushcfunction(L, magnet_cgi_pairs);                   /* (sp += 1) */
943 	lua_setfield(L, -2, "__pairs");                           /* (sp -= 1) */
944 	lua_setmetatable(L, -2); /* tie the metatable to req_env     (sp -= 1) */
945 	lua_setfield(L, -2, "req_env"); /* content = {}              (sp -= 1) */
946 
947 	lua_newtable(L); /*  {}                                      (sp += 1) */
948 	lua_newtable(L); /* the meta-table for the request-table     (sp += 1) */
949 	lua_pushcfunction(L, magnet_status_get);                  /* (sp += 1) */
950 	lua_setfield(L, -2, "__index");                           /* (sp -= 1) */
951 	lua_pushcfunction(L, magnet_status_set);                  /* (sp += 1) */
952 	lua_setfield(L, -2, "__newindex");                        /* (sp -= 1) */
953 	lua_pushcfunction(L, magnet_status_pairs);                /* (sp += 1) */
954 	lua_setfield(L, -2, "__pairs");                           /* (sp -= 1) */
955 	lua_setmetatable(L, -2); /* tie the metatable to request     (sp -= 1) */
956 	lua_setfield(L, -2, "status"); /* content = {}               (sp -= 1) */
957 
958 	/* add empty 'content' and 'header' tables */
959 	lua_newtable(L); /*  {}                                      (sp += 1) */
960 	lua_setfield(L, -2, "content"); /* content = {}              (sp -= 1) */
961 
962 	lua_newtable(L); /*  {}                                      (sp += 1) */
963 	lua_setfield(L, -2, "header"); /* header = {}                (sp -= 1) */
964 
965 	lua_pushinteger(L, MAGNET_RESTART_REQUEST);
966 	lua_setfield(L, -2, "RESTART_REQUEST");
967 
968 	lua_pushcfunction(L, magnet_stat);                        /* (sp += 1) */
969 	lua_setfield(L, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */
970 
971 	lua_setfield(L, -2, "lighty"); /* lighty.*                   (sp -= 1) */
972 
973 	/* override the default pairs() function to our __pairs capable version */
974 	lua_getglobal(L, "pairs"); /* push original pairs()          (sp += 1) */
975 	lua_pushcclosure(L, magnet_pairs, 1);
976 	lua_setfield(L, -2, "pairs");                             /* (sp -= 1) */
977 
978 	lua_newtable(L); /* the meta-table for the new env           (sp += 1) */
979 	lua_pushvalue(L, LUA_GLOBALSINDEX);                       /* (sp += 1) */
980 	lua_setfield(L, -2, "__index"); /* { __index = _G }          (sp -= 1) */
981 	lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */
982 
983 
984 	lua_setfenv(L, -2); /* on the stack should be a modified env (sp -= 1) */
985 
986 	errfunc = push_traceback(L, 0);
987 	if (lua_pcall(L, 0, 1, errfunc)) {
988 		lua_remove(L, errfunc);
989 		log_error_write(srv, __FILE__, __LINE__,
990 			"ss",
991 			"lua_pcall():",
992 			lua_tostring(L, -1));
993 		lua_pop(L, 1); /* remove the error-msg and the function copy from the stack */
994 
995 		assert(lua_gettop(L) == 1); /* only the function should be on the stack */
996 
997 		con->http_status = 500;
998 		con->mode = DIRECT;
999 
1000 		return HANDLER_FINISHED;
1001 	}
1002 	lua_remove(L, errfunc);
1003 
1004 	/* we should have the function-copy and the return value on the stack */
1005 	assert(lua_gettop(L) == 2);
1006 
1007 	if (lua_isnumber(L, -1)) {
1008 		/* if the ret-value is a number, take it */
1009 		lua_return_value = (int)lua_tonumber(L, -1);
1010 	}
1011 	lua_pop(L, 1); /* pop the ret-value */
1012 
1013 	magnet_copy_response_header(srv, con, p, L);
1014 
1015 	if (lua_return_value > 99) {
1016 		con->http_status = lua_return_value;
1017 		con->file_finished = 1;
1018 
1019 		/* try { ...*/
1020 		if (0 == setjmp(exceptionjmp)) {
1021 			magnet_attach_content(srv, con, p, L);
1022 			if (!chunkqueue_is_empty(con->write_queue)) {
1023 				con->mode = p->id;
1024 			}
1025 		} else {
1026 			/* } catch () { */
1027 			con->http_status = 500;
1028 			con->mode = DIRECT;
1029 		}
1030 
1031 		assert(lua_gettop(L) == 1); /* only the function should be on the stack */
1032 
1033 		/* we are finished */
1034 		return HANDLER_FINISHED;
1035 	} else if (MAGNET_RESTART_REQUEST == lua_return_value) {
1036 		assert(lua_gettop(L) == 1); /* only the function should be on the stack */
1037 
1038 		return HANDLER_COMEBACK;
1039 	} else {
1040 		assert(lua_gettop(L) == 1); /* only the function should be on the stack */
1041 
1042 		return HANDLER_GO_ON;
1043 	}
1044 }
1045 
magnet_attract_array(server * srv,connection * con,plugin_data * p,array * files)1046 static handler_t magnet_attract_array(server *srv, connection *con, plugin_data *p, array *files) {
1047 	size_t i;
1048 
1049 	/* no filename set */
1050 	if (files->used == 0) return HANDLER_GO_ON;
1051 
1052 	/**
1053 	 * execute all files and jump out on the first !HANDLER_GO_ON
1054 	 */
1055 	for (i = 0; i < files->used; i++) {
1056 		data_string *ds = (data_string *)files->data[i];
1057 		handler_t ret;
1058 
1059 		if (buffer_is_empty(ds->value)) continue;
1060 
1061 		ret = magnet_attract(srv, con, p, ds->value);
1062 
1063 		if (ret != HANDLER_GO_ON) return ret;
1064 	}
1065 
1066 	return HANDLER_GO_ON;
1067 }
1068 
URIHANDLER_FUNC(mod_magnet_uri_handler)1069 URIHANDLER_FUNC(mod_magnet_uri_handler) {
1070 	plugin_data *p = p_d;
1071 
1072 	mod_magnet_patch_connection(srv, con, p);
1073 
1074 	return magnet_attract_array(srv, con, p, p->conf.url_raw);
1075 }
1076 
URIHANDLER_FUNC(mod_magnet_physical)1077 URIHANDLER_FUNC(mod_magnet_physical) {
1078 	plugin_data *p = p_d;
1079 
1080 	mod_magnet_patch_connection(srv, con, p);
1081 
1082 	return magnet_attract_array(srv, con, p, p->conf.physical_path);
1083 }
1084 
1085 
1086 /* this function is called at dlopen() time and inits the callbacks */
1087 
1088 int mod_magnet_plugin_init(plugin *p);
mod_magnet_plugin_init(plugin * p)1089 int mod_magnet_plugin_init(plugin *p) {
1090 	p->version     = LIGHTTPD_VERSION_ID;
1091 	p->name        = buffer_init_string("magnet");
1092 
1093 	p->init        = mod_magnet_init;
1094 	p->handle_uri_clean  = mod_magnet_uri_handler;
1095 	p->handle_physical   = mod_magnet_physical;
1096 	p->set_defaults  = mod_magnet_set_defaults;
1097 	p->cleanup     = mod_magnet_free;
1098 
1099 	p->data        = NULL;
1100 
1101 	return 0;
1102 }
1103 
1104 #else
1105 int mod_magnet_plugin_init(plugin *p);
mod_magnet_plugin_init(plugin * p)1106 int mod_magnet_plugin_init(plugin *p) {
1107 	UNUSED(p);
1108 	return -1;
1109 }
1110 #endif
1111