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