1 #include "first.h" 2 3 #include "sys-crypto-md.h" 4 #include "algo_hmac.h" 5 #include "base.h" 6 #include "base64.h" 7 #include "burl.h" 8 #include "log.h" 9 #include "buffer.h" 10 #include "chunk.h" 11 #include "ck.h" 12 #include "http_chunk.h" 13 #include "http_etag.h" 14 #include "http_header.h" 15 #include "rand.h" 16 #include "response.h" /* http_response_send_1xx() */ 17 18 #include "plugin.h" 19 20 #include "mod_magnet_cache.h" 21 #include "sock_addr.h" 22 #include "stat_cache.h" 23 #include "status_counter.h" 24 25 #include <dirent.h> 26 #include <stdlib.h> 27 #include <string.h> 28 /*#include <setjmp.h>*//*(not currently used)*/ 29 30 #include <lua.h> 31 #include <lauxlib.h> 32 33 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 34 #define LUA_RIDX_LIGHTTPD_REQUEST "lighty.request" 35 #endif 36 37 #define MAGNET_RESTART_REQUEST 99 38 39 /* plugin config for all request/connections */ 40 41 /*static jmp_buf exceptionjmp;*//*(not currently used)*/ 42 43 typedef struct { 44 script * const *url_raw; 45 script * const *physical_path; 46 script * const *response_start; 47 int stage; 48 } plugin_config; 49 50 typedef struct { 51 PLUGIN_DATA; 52 plugin_config defaults; 53 plugin_config conf; 54 55 script_cache cache; 56 } plugin_data; 57 58 INIT_FUNC(mod_magnet_init) { 59 return calloc(1, sizeof(plugin_data)); 60 } 61 62 FREE_FUNC(mod_magnet_free) { 63 plugin_data * const p = p_d; 64 script_cache_free_data(&p->cache); 65 if (NULL == p->cvlist) return; 66 /* (init i to 0 if global context; to 1 to skip empty global context) */ 67 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 68 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 69 for (; -1 != cpv->k_id; ++cpv) { 70 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 71 switch (cpv->k_id) { 72 case 0: /* magnet.attract-raw-url-to */ 73 case 1: /* magnet.attract-physical-path-to */ 74 case 2: /* magnet.attract-response-start-to */ 75 free(cpv->v.v); 76 break; 77 default: 78 break; 79 } 80 } 81 } 82 } 83 84 static void mod_magnet_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 85 if (cpv->vtype != T_CONFIG_LOCAL) 86 return; 87 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 88 case 0: /* magnet.attract-raw-url-to */ 89 pconf->url_raw = cpv->v.v; 90 break; 91 case 1: /* magnet.attract-physical-path-to */ 92 pconf->physical_path = cpv->v.v; 93 break; 94 case 2: /* magnet.attract-response-start-to */ 95 pconf->response_start = cpv->v.v; 96 break; 97 default:/* should not happen */ 98 return; 99 } 100 } 101 102 static void mod_magnet_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 103 do { 104 mod_magnet_merge_config_cpv(pconf, cpv); 105 } while ((++cpv)->k_id != -1); 106 } 107 108 static void mod_magnet_patch_config(request_st * const r, plugin_data * const p) { 109 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 110 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 111 for (int i = 1, used = p->nconfig; i < used; ++i) { 112 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 113 mod_magnet_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 114 } 115 } 116 117 SETDEFAULTS_FUNC(mod_magnet_set_defaults) { 118 static const config_plugin_keys_t cpk[] = { 119 { CONST_STR_LEN("magnet.attract-raw-url-to"), 120 T_CONFIG_ARRAY_VLIST, 121 T_CONFIG_SCOPE_CONNECTION } 122 ,{ CONST_STR_LEN("magnet.attract-physical-path-to"), 123 T_CONFIG_ARRAY_VLIST, 124 T_CONFIG_SCOPE_CONNECTION } 125 ,{ CONST_STR_LEN("magnet.attract-response-start-to"), 126 T_CONFIG_ARRAY_VLIST, 127 T_CONFIG_SCOPE_CONNECTION } 128 ,{ NULL, 0, 129 T_CONFIG_UNSET, 130 T_CONFIG_SCOPE_UNSET } 131 }; 132 133 plugin_data * const p = p_d; 134 if (!config_plugin_values_init(srv, p, cpk, "mod_magnet")) 135 return HANDLER_ERROR; 136 137 /* process and validate config directives 138 * (init i to 0 if global context; to 1 to skip empty global context) */ 139 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 140 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 141 for (; -1 != cpv->k_id; ++cpv) { 142 switch (cpv->k_id) { 143 case 0: /* magnet.attract-raw-url-to */ 144 case 1: /* magnet.attract-physical-path-to */ 145 case 2: /* magnet.attract-response-start-to */ 146 if (0 == cpv->v.a->used) { 147 cpv->v.v = NULL; 148 cpv->vtype = T_CONFIG_LOCAL; 149 } 150 else { 151 script ** const a = 152 malloc(sizeof(script *)*(cpv->v.a->used+1)); 153 force_assert(a); 154 for (uint32_t j = 0; j < cpv->v.a->used; ++j) { 155 data_string *ds = (data_string *)cpv->v.a->data[j]; 156 if (buffer_is_blank(&ds->value)) { 157 log_error(srv->errh, __FILE__, __LINE__, 158 "unexpected (blank) value for %s; " 159 "expected list of \"scriptpath\"", cpk[cpv->k_id].k); 160 free(a); 161 return HANDLER_ERROR; 162 } 163 a[j] = script_cache_get_script(&p->cache, &ds->value); 164 } 165 a[cpv->v.a->used] = NULL; 166 cpv->v.v = a; 167 cpv->vtype = T_CONFIG_LOCAL; 168 } 169 break; 170 default:/* should not happen */ 171 break; 172 } 173 } 174 } 175 176 /* initialize p->defaults from global config context */ 177 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 178 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 179 if (-1 != cpv->k_id) 180 mod_magnet_merge_config(&p->defaults, cpv); 181 } 182 183 return HANDLER_GO_ON; 184 } 185 186 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 187 #define lua_getfield_and_type(L,idx,k) \ 188 (lua_getfield((L),(idx),(k)), lua_type((L),-1)) 189 #define lua_getglobal_and_type(L,name) \ 190 (lua_getglobal((L),(name)), lua_type((L),-1)) 191 #else 192 #define lua_getfield_and_type(L,idx,k) \ 193 lua_getfield((L),(idx),(k)) 194 #define lua_getglobal_and_type(L,name) \ 195 lua_getglobal((L),(name)) 196 #endif 197 198 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 199 static lua_Integer 200 lua_tointegerx (lua_State * const L, int idx, int *isnum) 201 { 202 /*(caller should check for LUA_TNIL if using a default value is desired)*/ 203 /*(note: return 0 for floating point not convertible to integer)*/ 204 *isnum = lua_isnumber(L, idx); 205 return *isnum ? lua_tointeger(L, idx) : 0; 206 } 207 #endif 208 209 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 210 /* lua5.1 backward compat definition */ 211 static void lua_pushglobaltable(lua_State *L) { /* (-0, +1, -) */ 212 lua_pushvalue(L, LUA_GLOBALSINDEX); 213 } 214 #endif 215 216 static void magnet_setfenv_mainfn(lua_State *L, int funcIndex) { /* (-1, 0, -) */ 217 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502 218 /* set "_ENV" upvalue, which should be the first upvalue of a "main" lua 219 * function if it uses any global names 220 */ 221 222 const char* first_upvalue_name = lua_getupvalue(L, funcIndex, 1); 223 if (NULL == first_upvalue_name) return; /* doesn't have any upvalues */ 224 lua_pop(L, 1); /* only need the name of the upvalue, not the value */ 225 226 if (0 != strcmp(first_upvalue_name, "_ENV")) return; 227 228 if (NULL == lua_setupvalue(L, funcIndex, 1)) { 229 /* pop value if lua_setupvalue didn't set the (not existing) upvalue */ 230 lua_pop(L, 1); 231 } 232 #else 233 lua_setfenv(L, funcIndex); 234 #endif 235 } 236 237 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 238 /* lua 5.1 deprecated luaL_getn() for lua_objlen() */ 239 /* lua 5.2 renamed lua_objlen() to lua_rawlen() */ 240 #define lua_rawlen lua_objlen 241 /* lua 5.2 deprecated luaL_register() for luaL_setfuncs() 242 * (this define is valid only when 0 == nup) */ 243 #define luaL_setfuncs(L, l, nup) luaL_register((L), NULL, (l)) 244 #endif 245 246 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 247 /* lua 5.2 already supports __pairs */ 248 249 /* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details. 250 * Override the default pairs() function to allow us to use a __pairs metakey 251 */ 252 static int magnet_pairs(lua_State *L) { 253 luaL_checkany(L, 1); /* "self" */ 254 255 if (luaL_getmetafield(L, 1, "__pairs")) { 256 /* call __pairs(self) */ 257 lua_pushvalue(L, 1); 258 lua_call(L, 1, 3); 259 } else { 260 /* call <original-pairs-method>(self) */ 261 lua_pushvalue(L, lua_upvalueindex(1)); 262 lua_pushvalue(L, 1); 263 lua_call(L, 1, 3); 264 } 265 return 3; 266 } 267 #endif 268 269 270 /* XXX: mystery why dir walk (readdir) is not already part of lua io liolib.c */ 271 272 #ifndef _D_EXACT_NAMLEN 273 #ifdef _DIRENT_HAVE_D_NAMLEN 274 #define _D_EXACT_NAMLEN(d) ((d)->d_namlen) 275 #else 276 #define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name)) 277 #endif 278 #endif 279 280 static int magnet_readdir_iter(lua_State *L) { 281 DIR ** const d = (DIR **)lua_touserdata(L, lua_upvalueindex(1)); 282 if (NULL == *d) return 0; 283 284 /* readdir() and skip over "." and ".." */ 285 struct dirent *de; 286 const char *n; 287 do { 288 de = readdir(*d); 289 } while (de && (n = de->d_name)[0] == '.' 290 && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))); 291 292 if (de) { 293 lua_pushlstring(L, de->d_name, _D_EXACT_NAMLEN(de)); 294 return 1; 295 } 296 else { /* EOF */ 297 closedir(*d); 298 *d = NULL; 299 return 0; 300 } 301 } 302 303 static int magnet_readdir_gc(lua_State *L) { 304 /*DIR ** const d = ((DIR **)luaL_checkudata(L, 1, "lighty.DIR"));*/ 305 DIR ** const d = lua_touserdata(L, 1); 306 if (*d) closedir(*d); 307 return 0; 308 } 309 310 static void magnet_readdir_metatable(lua_State * const L) { 311 if (luaL_newmetatable(L, "lighty.DIR")) { /* (sp += 1) */ 312 lua_pushcclosure(L, magnet_readdir_gc, 0); /* (sp += 1) */ 313 lua_setfield(L, -2, "__gc"); /* (sp -= 1) */ 314 lua_pushboolean(L, 0); /* (sp += 1) */ 315 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 316 } 317 } 318 319 static int magnet_readdir(lua_State *L) { 320 const char * const s = luaL_checkstring(L, 1); 321 DIR * const d = opendir(s); 322 if (d) { 323 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 504 324 DIR ** const dp = (DIR **)lua_newuserdata(L, sizeof(DIR *)); 325 #else 326 DIR ** const dp = (DIR **)lua_newuserdatauv(L, sizeof(DIR *), 0); 327 #endif 328 *dp = d; 329 magnet_readdir_metatable(L); 330 lua_setmetatable(L, -2); 331 lua_pushcclosure(L, magnet_readdir_iter, 1); 332 } 333 else 334 lua_pushnil(L); 335 return 1; 336 } 337 338 339 static int magnet_newindex_readonly(lua_State *L) { 340 lua_pushliteral(L, "lua table is read-only"); 341 return lua_error(L); 342 } 343 344 static void magnet_push_buffer(lua_State *L, const buffer *b) { 345 if (b && !buffer_is_unset(b)) 346 lua_pushlstring(L, BUF_PTR_LEN(b)); 347 else 348 lua_pushnil(L); 349 } 350 351 #if 0 352 static int magnet_array_get_element(lua_State *L, const array *a) { 353 /* __index: param 1 is the (empty) table the value was not found in */ 354 size_t klen; 355 const char * const k = luaL_checklstring(L, 2, &klen); 356 const data_string * const ds = (const data_string *) 357 array_get_element_klen(a, k, klen); 358 magnet_push_buffer(L, NULL != ds ? &ds->value : NULL); 359 return 1; 360 } 361 #endif 362 363 /* Define a function that will iterate over an array* (in upval 1) using current position (upval 2) */ 364 static int magnet_array_next(lua_State *L) { 365 data_unset *du; 366 data_string *ds; 367 data_integer *di; 368 369 size_t pos = lua_tointeger(L, lua_upvalueindex(1)); 370 array *a = lua_touserdata(L, lua_upvalueindex(2)); 371 372 lua_settop(L, 0); 373 374 if (pos >= a->used) return 0; 375 if (NULL != (du = a->data[pos])) { 376 lua_pushlstring(L, BUF_PTR_LEN(&du->key)); 377 switch (du->type) { 378 case TYPE_STRING: 379 ds = (data_string *)du; 380 magnet_push_buffer(L, &ds->value); 381 break; 382 case TYPE_INTEGER: 383 di = (data_integer *)du; 384 lua_pushinteger(L, di->value); 385 break; 386 default: 387 lua_pushnil(L); 388 break; 389 } 390 391 /* Update our positional upval to reflect our new current position */ 392 pos++; 393 lua_pushinteger(L, pos); 394 lua_replace(L, lua_upvalueindex(1)); 395 396 /* Returning 2 items on the stack (key, value) */ 397 return 2; 398 } 399 return 0; 400 } 401 402 /* Create the closure necessary to iterate over the array *a with the above function */ 403 static int magnet_array_pairs(lua_State *L, array *a) { 404 lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */ 405 lua_pushlightuserdata(L, a); /* Push our array *a into upval 2 */ 406 lua_pushcclosure(L, magnet_array_next, 2); /* Push our new closure with 2 upvals */ 407 return 1; 408 } 409 410 static request_st * magnet_get_request(lua_State *L) { 411 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 412 lua_getfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_REQUEST); 413 request_st * const r = lua_touserdata(L, -1); 414 lua_pop(L, 1); 415 return r; 416 #else 417 return *(request_st **)lua_getextraspace(L); 418 #endif 419 } 420 421 static buffer * magnet_tmpbuf_acquire(lua_State *L) 422 { 423 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 424 UNUSED(L); 425 return chunk_buffer_acquire(); 426 #else 427 request_st * const r = magnet_get_request(L); 428 buffer * const tb = r->tmp_buf; 429 buffer_clear(tb); 430 return tb; 431 #endif 432 } 433 434 static void magnet_tmpbuf_release(buffer *b) 435 { 436 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 437 chunk_buffer_release(b); 438 #else 439 UNUSED(b); 440 #endif 441 } 442 443 typedef struct { 444 const char *ptr; 445 size_t len; 446 } const_buffer; 447 448 static const_buffer magnet_checkconstbuffer(lua_State *L, int idx) { 449 const_buffer cb; 450 if (!lua_isnoneornil(L, idx)) 451 cb.ptr = luaL_checklstring(L, idx, &cb.len); 452 else { 453 cb.ptr = NULL; 454 cb.len = 0; 455 } 456 return cb; 457 } 458 459 static const buffer* magnet_checkbuffer(lua_State *L, int idx, buffer *b) { 460 const_buffer cb = magnet_checkconstbuffer(L, idx); 461 /* assign result into (buffer *), and return (const buffer *) 462 * (note: caller must not free result) */ 463 *(const char **)&b->ptr = cb.ptr ? cb.ptr : ""; 464 b->used = cb.len+1; 465 b->size = 0; 466 return b; 467 } 468 469 static int magnet_print(lua_State *L) { 470 const_buffer cb = magnet_checkconstbuffer(L, 1); 471 request_st * const r = magnet_get_request(L); 472 log_error(r->conf.errh, __FILE__, __LINE__, "(lua-print) %s", cb.ptr); 473 return 0; 474 } 475 476 477 static int magnet_stat_field(lua_State *L) { 478 if (lua_gettop(L) != 2) 479 return 0; /*(should not happen; __index method in protected metatable)*/ 480 481 stat_cache_entry * const sce = *(stat_cache_entry **)lua_touserdata(L, -2); 482 const_buffer k = magnet_checkconstbuffer(L, -1); 483 switch (k.len ? k.ptr[0] : 0) { 484 case 'c': { /* content-type */ 485 if (0 != strcmp(k.ptr, "content-type")) break; 486 request_st * const r = magnet_get_request(L); 487 const buffer *content_type = stat_cache_content_type_get(sce, r); 488 if (content_type && !buffer_is_blank(content_type)) 489 lua_pushlstring(L, BUF_PTR_LEN(content_type)); 490 else 491 lua_pushnil(L); 492 return 1; 493 } 494 case 'e': { /* etag */ 495 if (0 != strcmp(k.ptr, "etag")) break; 496 request_st * const r = magnet_get_request(L); 497 const buffer *etag = stat_cache_etag_get(sce, r->conf.etag_flags); 498 if (etag && !buffer_is_blank(etag)) 499 lua_pushlstring(L, BUF_PTR_LEN(etag)); 500 else 501 lua_pushnil(L); 502 return 1; 503 } 504 case 'h': { /* http-response-send-file */ 505 if (0 != strcmp(k.ptr, "http-response-send-file")) break; 506 request_st * const r = magnet_get_request(L); 507 http_response_send_file(r, &sce->name, sce); 508 lua_pushinteger(L, r->http_status); 509 return 1; 510 } 511 case 'i': /* is_* */ 512 if (k.len < 4) break; 513 switch (k.ptr[3]) { 514 case 'b': /* is_block */ 515 if (0 == strcmp(k.ptr, "is_block")) { 516 lua_pushboolean(L, S_ISBLK(sce->st.st_mode)); 517 return 1; 518 } 519 break; 520 case 'c': /* is_char */ 521 if (0 == strcmp(k.ptr, "is_char")) { 522 lua_pushboolean(L, S_ISCHR(sce->st.st_mode)); 523 return 1; 524 } 525 break; 526 case 'd': /* is_dir */ 527 if (0 == strcmp(k.ptr, "is_dir")) { 528 lua_pushboolean(L, S_ISDIR(sce->st.st_mode)); 529 return 1; 530 } 531 break; 532 case 'f': /* is_file is_fifo */ 533 if (0 == strcmp(k.ptr, "is_file")) { 534 lua_pushboolean(L, S_ISREG(sce->st.st_mode)); 535 return 1; 536 } 537 if (0 == strcmp(k.ptr, "is_fifo")) { 538 lua_pushboolean(L, S_ISFIFO(sce->st.st_mode)); 539 return 1; 540 } 541 break; 542 case 'l': /* is_link */ 543 if (0 == strcmp(k.ptr, "is_link")) { 544 lua_pushboolean(L, S_ISLNK(sce->st.st_mode)); 545 return 1; 546 } 547 break; 548 case 's': /* is_socket */ 549 if (0 == strcmp(k.ptr, "is_socket")) { 550 lua_pushboolean(L, S_ISSOCK(sce->st.st_mode)); 551 return 1; 552 } 553 break; 554 default: 555 break; 556 } 557 break; 558 case 's': /* st_* */ 559 if (k.len < 4) break; 560 switch (k.ptr[3]) { 561 case 'a': /* st_atime */ 562 if (0 == strcmp(k.ptr, "st_atime")) { 563 lua_pushinteger(L, TIME64_CAST(sce->st.st_atime)); 564 return 1; 565 } 566 break; 567 case 'c': /* st_ctime */ 568 if (0 == strcmp(k.ptr, "st_ctime")) { 569 lua_pushinteger(L, TIME64_CAST(sce->st.st_ctime)); 570 return 1; 571 } 572 break; 573 case 'i': /* st_ino */ 574 if (0 == strcmp(k.ptr, "st_ino")) { 575 lua_pushinteger(L, sce->st.st_ino); 576 return 1; 577 } 578 break; 579 case 'm': /* st_mtime st_mode */ 580 if (0 == strcmp(k.ptr, "st_mtime")) { 581 lua_pushinteger(L, TIME64_CAST(sce->st.st_mtime)); 582 return 1; 583 } 584 if (0 == strcmp(k.ptr, "st_mode")) { 585 lua_pushinteger(L, sce->st.st_mode); 586 return 1; 587 } 588 break; 589 case 'g': /* st_gid */ 590 if (0 == strcmp(k.ptr, "st_gid")) { 591 lua_pushinteger(L, sce->st.st_gid); 592 return 1; 593 } 594 break; 595 case 's': /* st_size */ 596 if (0 == strcmp(k.ptr, "st_size")) { 597 lua_pushinteger(L, sce->st.st_size); 598 return 1; 599 } 600 break; 601 case 'u': /* st_uid */ 602 if (0 == strcmp(k.ptr, "st_uid")) { 603 lua_pushinteger(L, sce->st.st_uid); 604 return 1; 605 } 606 break; 607 default: 608 break; 609 } 610 break; 611 default: 612 break; 613 } 614 615 lua_pushliteral(L, "stat[\"field\"] invalid: "); 616 lua_pushvalue(L, -2); /* field */ 617 lua_concat(L, 2); 618 lua_error(L); 619 return 0; 620 } 621 622 623 __attribute_cold__ 624 static int magnet_stat_pairs_noimpl_iter(lua_State *L) { 625 request_st * const r = magnet_get_request(L); 626 log_error(r->conf.errh, __FILE__, __LINE__, 627 "(lua) pairs() not implemented on lighty.stat object; " 628 "returning empty iter"); 629 return 0; 630 } 631 632 633 __attribute_cold__ 634 static int magnet_stat_pairs_noimpl(lua_State *L) { 635 lua_pushcclosure(L, magnet_stat_pairs_noimpl_iter, 0); 636 return 1; 637 } 638 639 640 static void magnet_stat_metatable(lua_State *L) { 641 if (luaL_newmetatable(L, "lighty.stat")) { /* (sp += 1) */ 642 lua_pushcfunction(L, magnet_stat_field); /* (sp += 1) */ 643 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 644 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 645 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 646 lua_pushcfunction(L, magnet_stat_pairs_noimpl); /* (sp += 1) */ 647 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 648 lua_pushboolean(L, 0); /* (sp += 1) */ 649 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 650 } 651 } 652 653 654 static int magnet_stat(lua_State *L) { 655 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/ 656 const buffer * const sb = magnet_checkbuffer(L, 1, &stor); 657 stat_cache_entry * const sce = (!buffer_is_blank(sb)) 658 ? stat_cache_get_entry(sb) 659 : NULL; 660 if (NULL == sce) { 661 lua_pushnil(L); 662 return 1; 663 } 664 665 /* note: caching sce valid only for procedural script which does not yield; 666 * (sce might not be valid if script yields and is later resumed) 667 * (script must not cache sce in persistent global state for later use) 668 * (If we did want sce to be persistent, then could increment sce refcnt, 669 * and set up __gc metatable method to decrement sce refcnt) */ 670 stat_cache_entry ** const udata =(struct stat_cache_entry**)/* (sp += 1) */ 671 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 504 672 lua_newuserdata(L, sizeof(stat_cache_entry *)); 673 #else 674 lua_newuserdatauv(L, sizeof(stat_cache_entry *), 0); 675 #endif 676 *udata = sce; 677 678 magnet_stat_metatable(L); /* (sp += 1) */ 679 lua_setmetatable(L, -2); /* (sp -= 1) */ 680 return 1; 681 } 682 683 684 static int magnet_time(lua_State *L) { 685 lua_pushinteger(L, (lua_Integer)log_epoch_secs); 686 return 1; 687 } 688 689 690 static int magnet_rand(lua_State *L) { 691 lua_pushinteger(L, (lua_Integer)li_rand_pseudo()); 692 return 1; 693 } 694 695 696 static int magnet_md_once(lua_State *L) { 697 if (lua_gettop(L) != 2) { 698 lua_pushliteral(L, 699 "lighty.c.md(algo, data): incorrect number of arguments"); 700 return lua_error(L); 701 } 702 const_buffer algo = magnet_checkconstbuffer(L, -2); 703 const_buffer msg = magnet_checkconstbuffer(L, -1); 704 uint8_t digest[MD_DIGEST_LENGTH_MAX]; 705 uint32_t dlen = 0; 706 switch (algo.len) { 707 #ifdef USE_LIB_CRYPTO 708 case 6: 709 #ifdef USE_LIB_CRYPTO_SHA512 710 if (0 == memcmp(algo.ptr, "sha512", 6)) { 711 SHA512_once(digest, msg.ptr, msg.len); 712 dlen = SHA512_DIGEST_LENGTH; 713 break; 714 } 715 #endif 716 #ifdef USE_LIB_CRYPTO_SHA256 717 if (0 == memcmp(algo.ptr, "sha256", 6)) { 718 SHA256_once(digest, msg.ptr, msg.len); 719 dlen = SHA256_DIGEST_LENGTH; 720 break; 721 } 722 #endif 723 break; 724 case 4: 725 #ifdef USE_LIB_CRYPTO_SHA1 726 if (0 == memcmp(algo.ptr, "sha1", 4)) { 727 SHA1_once(digest, msg.ptr, msg.len); 728 dlen = SHA1_DIGEST_LENGTH; 729 break; 730 } 731 #endif 732 break; 733 #endif 734 case 3: 735 if (0 == memcmp(algo.ptr, "md5", 3)) { 736 MD5_once(digest, msg.ptr, msg.len); 737 dlen = MD5_DIGEST_LENGTH; 738 break; 739 } 740 break; 741 default: 742 break; 743 } 744 745 if (dlen) { 746 char dighex[MD_DIGEST_LENGTH_MAX*2+1]; 747 li_tohex_uc(dighex, sizeof(dighex), (char *)digest, dlen); 748 lua_pushlstring(L, dighex, dlen*2); 749 } 750 else 751 lua_pushnil(L); 752 753 return 1; 754 } 755 756 static int magnet_hmac_once(lua_State *L) { 757 if (lua_gettop(L) != 3) { 758 lua_pushliteral(L, 759 "lighty.c.hmac(algo, secret, data): incorrect number of arguments"); 760 return lua_error(L); 761 } 762 const_buffer algo = magnet_checkconstbuffer(L, -3); 763 const_buffer secret = magnet_checkconstbuffer(L, -2); 764 const_buffer msg = magnet_checkconstbuffer(L, -1); 765 const uint8_t * const msgptr = (uint8_t *)msg.ptr; 766 uint8_t digest[MD_DIGEST_LENGTH_MAX]; 767 uint32_t dlen = 0; 768 int rc = 0; 769 switch (algo.len) { 770 #ifdef USE_LIB_CRYPTO 771 case 6: 772 #ifdef USE_LIB_CRYPTO_SHA512 773 if (0 == memcmp(algo.ptr, "sha512", 6)) { 774 rc = li_hmac_sha512(digest,secret.ptr,secret.len,msgptr,msg.len); 775 dlen = SHA512_DIGEST_LENGTH; 776 break; 777 } 778 #endif 779 #ifdef USE_LIB_CRYPTO_SHA256 780 if (0 == memcmp(algo.ptr, "sha256", 6)) { 781 rc = li_hmac_sha256(digest,secret.ptr,secret.len,msgptr,msg.len); 782 dlen = SHA256_DIGEST_LENGTH; 783 break; 784 } 785 #endif 786 break; 787 case 4: 788 #ifdef USE_LIB_CRYPTO_SHA1 789 if (0 == memcmp(algo.ptr, "sha1", 4)) { 790 rc = li_hmac_sha1(digest,secret.ptr,secret.len,msgptr,msg.len); 791 dlen = SHA1_DIGEST_LENGTH; 792 break; 793 } 794 #endif 795 break; 796 #endif 797 case 3: 798 if (0 == memcmp(algo.ptr, "md5", 3)) { 799 rc = li_hmac_md5(digest,secret.ptr,secret.len,msgptr,msg.len); 800 dlen = MD5_DIGEST_LENGTH; 801 break; 802 } 803 break; 804 default: 805 break; 806 } 807 808 if (rc) { 809 char dighex[MD_DIGEST_LENGTH_MAX*2+1]; 810 li_tohex_uc(dighex, sizeof(dighex), (char *)digest, dlen); 811 lua_pushlstring(L, dighex, dlen*2); 812 } 813 else 814 lua_pushnil(L); 815 816 return 1; 817 } 818 819 static int magnet_digest_eq(lua_State *L) { 820 if (lua_gettop(L) != 2) { 821 lua_pushliteral(L, 822 "lighty.c.digest_eq(d1, d2): incorrect number of arguments"); 823 return lua_error(L); 824 } 825 const_buffer d1 = magnet_checkconstbuffer(L, -2); 826 const_buffer d2 = magnet_checkconstbuffer(L, -1); 827 /* convert hex to binary: validate hex and eliminate hex case comparison */ 828 uint8_t b1[MD_DIGEST_LENGTH_MAX]; 829 uint8_t b2[MD_DIGEST_LENGTH_MAX]; 830 int rc = (d1.len == d2.len) 831 && 0 == li_hex2bin(b1, sizeof(b1), d1.ptr, d1.len) 832 && 0 == li_hex2bin(b2, sizeof(b2), d2.ptr, d2.len) 833 && ck_memeq_const_time_fixed_len(b1, b2, d2.len >> 1); 834 lua_pushboolean(L, rc); 835 return 1; 836 } 837 838 static int magnet_secret_eq(lua_State *L) { 839 if (lua_gettop(L) != 2) { 840 lua_pushliteral(L, 841 "lighty.c.secret_eq(d1, d2): incorrect number of arguments"); 842 return lua_error(L); 843 } 844 const_buffer d1 = magnet_checkconstbuffer(L, -2); 845 const_buffer d2 = magnet_checkconstbuffer(L, -1); 846 lua_pushboolean(L, ck_memeq_const_time(d1.ptr, d1.len, d2.ptr, d2.len)); 847 return 1; 848 } 849 850 static int magnet_b64dec(lua_State *L, base64_charset dict) { 851 if (lua_isnoneornil(L, -1)) { 852 lua_pushlstring(L, "", 0); 853 return 1; 854 } 855 const_buffer s = magnet_checkconstbuffer(L, -1); 856 if (0 == s.len) { 857 lua_pushvalue(L, -1); 858 return 1; 859 } 860 buffer * const b = magnet_tmpbuf_acquire(L); 861 if (buffer_append_base64_decode(b, s.ptr, s.len, dict)) 862 lua_pushlstring(L, BUF_PTR_LEN(b)); 863 else 864 lua_pushnil(L); 865 magnet_tmpbuf_release(b); 866 return 1; 867 } 868 869 static int magnet_b64enc(lua_State *L, base64_charset dict) { 870 if (lua_isnoneornil(L, -1)) { 871 lua_pushlstring(L, "", 0); 872 return 1; 873 } 874 const_buffer s = magnet_checkconstbuffer(L, -1); 875 if (0 == s.len) { 876 lua_pushvalue(L, -1); 877 return 1; 878 } 879 buffer * const b = magnet_tmpbuf_acquire(L); 880 buffer_append_base64_encode_no_padding(b, (uint8_t *)s.ptr, s.len, dict); 881 lua_pushlstring(L, BUF_PTR_LEN(b)); 882 magnet_tmpbuf_release(b); 883 return 1; 884 } 885 886 static int magnet_b64urldec(lua_State *L) { 887 return magnet_b64dec(L, BASE64_URL); 888 } 889 890 static int magnet_b64urlenc(lua_State *L) { 891 return magnet_b64enc(L, BASE64_URL); 892 } 893 894 static int magnet_b64stddec(lua_State *L) { 895 return magnet_b64dec(L, BASE64_STANDARD); 896 } 897 898 static int magnet_b64stdenc(lua_State *L) { 899 return magnet_b64enc(L, BASE64_STANDARD); 900 } 901 902 static int magnet_hexdec(lua_State *L) { 903 if (lua_isnoneornil(L, -1)) { 904 lua_pushlstring(L, "", 0); 905 return 1; 906 } 907 const_buffer s = magnet_checkconstbuffer(L, -1); 908 if (0 == s.len) { 909 lua_pushvalue(L, -1); 910 return 1; 911 } 912 buffer * const b = magnet_tmpbuf_acquire(L); 913 uint8_t * const p = (uint8_t *)buffer_extend(b, s.len >> 1); 914 int rc = li_hex2bin(p, s.len >> 1, s.ptr, s.len); 915 if (0 == rc) 916 lua_pushlstring(L, BUF_PTR_LEN(b)); 917 magnet_tmpbuf_release(b); 918 return rc+1; /* 1 on success (pushed string); 0 on failure (no value) */ 919 } 920 921 static int magnet_hexenc(lua_State *L) { 922 if (lua_isnoneornil(L, -1)) { 923 lua_pushlstring(L, "", 0); 924 return 1; 925 } 926 const_buffer s = magnet_checkconstbuffer(L, -1); 927 if (0 == s.len) { 928 lua_pushvalue(L, -1); 929 return 1; 930 } 931 buffer * const b = magnet_tmpbuf_acquire(L); 932 buffer_append_string_encoded_hex_uc(b, s.ptr, s.len); 933 lua_pushlstring(L, BUF_PTR_LEN(b)); 934 magnet_tmpbuf_release(b); 935 return 1; /* uppercase hex string; use lua s = s:lower() to lowercase */ 936 } 937 938 static int magnet_quoteddec(lua_State *L) { 939 if (lua_isnoneornil(L, -1)) { 940 lua_pushlstring(L, "", 0); 941 return 1; 942 } 943 const_buffer s = magnet_checkconstbuffer(L, -1); 944 if (0 == s.len || s.ptr[0] != '"') { 945 lua_pushvalue(L, -1); 946 return 1; 947 } 948 buffer * const b = magnet_tmpbuf_acquire(L); 949 char *p = buffer_string_prepare_append(b, s.len);/*(s.len-1 is sufficient)*/ 950 size_t i = 1; 951 for (; i < s.len && s.ptr[i] != '"'; ++i) { 952 if (s.ptr[i] == '\\') { 953 if (i+2 < s.len) 954 ++i; 955 else 956 break; 957 } 958 *p++ = s.ptr[i]; 959 } 960 int rc = (i == s.len-1 && s.ptr[i] == '"'); 961 if (rc) 962 lua_pushlstring(L, b->ptr, (size_t)(p - b->ptr)); 963 magnet_tmpbuf_release(b); 964 return rc; /* 1 on success (pushed string); 0 on failure (no value) */ 965 } 966 967 static int magnet_quotedenc(lua_State *L) { 968 if (lua_isnoneornil(L, -1)) { 969 lua_pushlstring(L, "", 0); 970 return 1; 971 } 972 const_buffer s = magnet_checkconstbuffer(L, -1); 973 if (0 == s.len) { 974 lua_pushvalue(L, -1); 975 return 1; 976 } 977 buffer * const b = magnet_tmpbuf_acquire(L); 978 char *p = buffer_string_prepare_append(b, 2+(s.len << 1)); 979 *p++ = '"'; 980 for (size_t i = 0; i < s.len; ++i) { 981 /*(note: not strictly checking for TEXT)*/ 982 /*(TEXT: any OCTET except CTLs but including LWS)*/ 983 if (s.ptr[i] == '"' || s.ptr[i] == '\\') 984 *p++ = '\\'; 985 *p++ = s.ptr[i]; 986 } 987 *p++ = '"'; 988 lua_pushlstring(L, b->ptr, (size_t)(p - b->ptr)); 989 magnet_tmpbuf_release(b); 990 return 1; 991 } 992 993 static int magnet_xmlenc(lua_State *L) { 994 if (lua_isnoneornil(L, -1)) { 995 lua_pushlstring(L, "", 0); 996 return 1; 997 } 998 const_buffer s = magnet_checkconstbuffer(L, -1); 999 if (0 == s.len) { 1000 lua_pushvalue(L, -1); 1001 return 1; 1002 } 1003 buffer * const b = magnet_tmpbuf_acquire(L); 1004 #if 1 1005 buffer_append_string_encoded(b, s.ptr, s.len, ENCODING_MINIMAL_XML); 1006 #else 1007 const char *e; 1008 size_t i, n, elen; 1009 for (i = 0, n = 0; i < s.len; ++i) { 1010 switch (s.ptr[i]) { 1011 default: continue; 1012 case '<': e = "<"; elen = sizeof("<")-1; break; 1013 case '>': e = ">"; elen = sizeof(">")-1; break; 1014 case '&': e = "&"; elen = sizeof("&")-1; break; 1015 case '\'': e = "'"; elen = sizeof("'")-1; break; 1016 case '"': e = """; elen = sizeof(""")-1; break; 1017 /*(XXX: would be good idea to add CTRLs, DEL, '`' */ 1018 } 1019 buffer_append_str2(b, s.ptr+n, i-n, e, elen); 1020 n = i+1; 1021 } 1022 if (i-n) 1023 buffer_append_string_len(b, s.ptr+n, i-n); 1024 #endif 1025 lua_pushlstring(L, BUF_PTR_LEN(b)); 1026 magnet_tmpbuf_release(b); 1027 return 1; 1028 } 1029 1030 static int magnet_urldec(lua_State *L) { 1031 /* url-decode and replace non-printable chars with '_' 1032 * This function should not be used on query-string unless it is used on 1033 * portions of query-string after splitting on '&', replacing '+' w/ ' ' */ 1034 if (lua_isnoneornil(L, -1)) { 1035 lua_pushlstring(L, "", 0); 1036 return 1; 1037 } 1038 const_buffer s = magnet_checkconstbuffer(L, -1); 1039 if (0 == s.len) { 1040 lua_pushvalue(L, -1); 1041 return 1; 1042 } 1043 buffer * const b = magnet_tmpbuf_acquire(L); 1044 buffer_copy_string_len(b, s.ptr, s.len); 1045 buffer_urldecode_path(b); 1046 lua_pushlstring(L, BUF_PTR_LEN(b)); 1047 magnet_tmpbuf_release(b); 1048 return 1; 1049 } 1050 1051 static int magnet_urlenc(lua_State *L) { 1052 /* url-encode path 1053 * ('?' is encoded, if present) 1054 * caller must split string if '?' is part of query-string) 1055 * ('/' is not encoded; caller must encode if not path separator) */ 1056 if (lua_isnoneornil(L, -1)) { 1057 lua_pushlstring(L, "", 0); 1058 return 1; 1059 } 1060 const_buffer s = magnet_checkconstbuffer(L, -1); 1061 if (0 == s.len) { 1062 lua_pushvalue(L, -1); 1063 return 1; 1064 } 1065 buffer * const b = magnet_tmpbuf_acquire(L); 1066 buffer_append_string_encoded(b, s.ptr, s.len, ENCODING_REL_URI); 1067 lua_pushlstring(L, BUF_PTR_LEN(b)); 1068 magnet_tmpbuf_release(b); 1069 return 1; 1070 } 1071 1072 static void magnet_urldec_query_part(buffer * const b, const char *s, const size_t slen) { 1073 buffer_clear(b); 1074 char *p = buffer_extend(b, slen); 1075 for (size_t i = 0; i < slen; ++i) 1076 p[i] = (s[i] != '+') ? s[i] : ' '; 1077 buffer_urldecode_path(b); 1078 } 1079 1080 static int magnet_urldec_query(lua_State *L) { 1081 /* split on '&' and '=', url-decode and replace non-printable chars w/ '_', 1082 * and store components in table 1083 * (string input should be query-string without leading '?') 1084 * (note: duplicated keys replace earlier values, but this interface returns 1085 * a table useful for lookups, so this limitation is often acceptable) */ 1086 lua_createtable(L, 0, 0); 1087 if (lua_isnoneornil(L, 1)) { 1088 return 1; 1089 } 1090 const_buffer s = magnet_checkconstbuffer(L, 1); 1091 if (0 == s.len) { 1092 return 1; 1093 } 1094 buffer * const b = magnet_tmpbuf_acquire(L); 1095 for (const char *qs = s.ptr, *eq, *amp; *qs; qs = amp+1) { 1096 for (amp = qs, eq = NULL; *amp && *amp != '&'; ++amp) { 1097 if (*amp == '=' && !eq) eq = amp; 1098 } 1099 if (amp != qs) { 1100 if (eq) { 1101 magnet_urldec_query_part(b, qs, (size_t)(eq - qs)); 1102 lua_pushlstring(L, BUF_PTR_LEN(b)); 1103 magnet_urldec_query_part(b, eq+1, (size_t)(amp - (eq+1))); 1104 lua_pushlstring(L, BUF_PTR_LEN(b)); 1105 } 1106 else { 1107 magnet_urldec_query_part(b, qs, (size_t)(amp - qs)); 1108 lua_pushlstring(L, BUF_PTR_LEN(b)); 1109 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/ 1110 } 1111 lua_rawset(L, -3); 1112 } 1113 if (*amp == '\0') break; 1114 } 1115 magnet_tmpbuf_release(b); 1116 return 1; 1117 } 1118 1119 static void magnet_urlenc_query_part(buffer * const b, const char * const s, const size_t slen, const int iskey) { 1120 /* encode query part (each part is typically much smaller than chunk buffer) 1121 * all required chars plus '&' ';' '+' '\'' (and encode '=' if part of key) 1122 * (burl_append(b,str,len,BURL_ENCODE_ALL) works, but over-encodes) */ 1123 #if 0 1124 /* alternative: (over-encodes some, but less than burl_append()) */ 1125 UNUSED(iskey); 1126 buffer_append_string_encoded(b, s, slen, ENCODING_REL_URI); 1127 #else 1128 static const char hex_chars_uc[] = "0123456789ABCDEF"; 1129 char * const p = buffer_string_prepare_append(b, slen*3); 1130 int j = 0; 1131 for (size_t i = 0; i < slen; ++i, ++j) { 1132 int c = s[i]; 1133 if (!light_isalnum(c)) switch (c) { 1134 case ' ': 1135 c = '+'; 1136 break; 1137 /*case '\'':*//*(ok in url query-part, but might be misused in HTML)*/ 1138 case '!': case '$': case '(': case ')': case '*': case ',': case '-': 1139 case '.': case '/': case ':': case '?': case '@': case '_': case '~': 1140 break; 1141 case '=': 1142 if (!iskey) break; 1143 __attribute_fallthrough__ 1144 default: 1145 p[j] = '%'; 1146 p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; 1147 p[++j] = hex_chars_uc[s[i] & 0xF]; 1148 continue; 1149 } 1150 p[j] = c; 1151 } 1152 buffer_commit(b, j); 1153 #endif 1154 } 1155 1156 static int magnet_urlenc_query(lua_State *L) { 1157 /* encode pairs in lua table into query string 1158 * (caller should add leading '?' or '&' when appending to full URL) 1159 * (caller should skip empty table if appending to existing query-string) */ 1160 if (!lua_istable(L, 1)) { 1161 lua_pushlstring(L, "", 0); 1162 return 1; 1163 } 1164 buffer * const b = magnet_tmpbuf_acquire(L); 1165 const_buffer s; 1166 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { 1167 if (lua_isstring(L, -2)) { 1168 if (!buffer_is_blank(b)) 1169 buffer_append_string_len(b, CONST_STR_LEN("&")); 1170 s = magnet_checkconstbuffer(L, -2); 1171 magnet_urlenc_query_part(b, s.ptr, s.len, 1); 1172 if (!lua_isnil(L, -1)) { 1173 s = magnet_checkconstbuffer(L, -1); 1174 buffer_append_string_len(b, CONST_STR_LEN("=")); 1175 magnet_urlenc_query_part(b, s.ptr, s.len, 0); 1176 } 1177 } 1178 } 1179 lua_pushlstring(L, BUF_PTR_LEN(b)); 1180 magnet_tmpbuf_release(b); 1181 return 1; 1182 } 1183 1184 static int magnet_urlenc_normalize(lua_State *L) { 1185 /* normalize url-encoding 1186 * url-encode (and url-decode safe chars) to normalize url-path 1187 * ('?' is treated as start of query-string and is not encoded; 1188 * caller must encode '?' if intended to be part of url-path) 1189 * ('/' is not encoded; caller must encode if not path separator) 1190 * (burl_append() is not exposed here; caller might want to build 1191 * url with lighty.c.urlenc() and lighty.c.urlenc_query(), 1192 * then call lighty.c.urlenc_normalize()) */ 1193 if (lua_isnoneornil(L, -1)) { 1194 lua_pushlstring(L, "", 0); 1195 return 1; 1196 } 1197 const_buffer s = magnet_checkconstbuffer(L, -1); 1198 if (0 == s.len) { 1199 lua_pushvalue(L, -1); 1200 return 1; 1201 } 1202 buffer * const b = magnet_tmpbuf_acquire(L); 1203 buffer * const t = chunk_buffer_acquire(); 1204 #if 0 /*(?maybe have different interface to use config policy?)*/ 1205 request_st * const r = magnet_get_request(L); 1206 const int flags = r->conf.http_parseopts; 1207 #else 1208 const int flags = HTTP_PARSEOPT_URL_NORMALIZE 1209 | HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED 1210 | HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED 1211 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE 1212 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE 1213 | HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS; 1214 #endif 1215 buffer_copy_string_len(b, s.ptr, s.len); 1216 burl_normalize(b, t, flags); 1217 lua_pushlstring(L, BUF_PTR_LEN(b)); 1218 chunk_buffer_release(t); 1219 magnet_tmpbuf_release(b); 1220 return 1; 1221 } 1222 1223 static int magnet_fspath_simplify(lua_State *L) { 1224 /* simplify filesystem path */ 1225 if (lua_isnoneornil(L, -1)) { 1226 lua_pushlstring(L, "", 0); 1227 return 1; 1228 } 1229 const_buffer s = magnet_checkconstbuffer(L, -1); 1230 if (0 == s.len) { 1231 lua_pushvalue(L, -1); 1232 return 1; 1233 } 1234 buffer * const b = magnet_tmpbuf_acquire(L); 1235 buffer_copy_string_len(b, s.ptr, s.len); 1236 buffer_path_simplify(b); 1237 lua_pushlstring(L, BUF_PTR_LEN(b)); 1238 magnet_tmpbuf_release(b); 1239 return 1; 1240 } 1241 1242 __attribute_pure__ 1243 static const char * magnet_scan_quoted_string (const char *s) { 1244 /* scan to end of of quoted-string (with s starting at '"') 1245 * loose parse; non-validating 1246 * - permit missing '"' at end of string (caller should check) 1247 * - stop at stray '\\' at end of string missing closing '"' 1248 * - not rejecting non-WS CTL chars 1249 */ 1250 /*force_assert(*s == '"');*/ 1251 do { ++s; } while (*s && *s != '"' && (*s != '\\' || (s[1] ? ++s : 0))); 1252 /*do { ++s; } while (*s && *s != '"' && (*s != '\\' || (*++s || (--s, 0))));*/ 1253 return s; 1254 } 1255 1256 static const char * magnet_push_quoted_string_range (lua_State *L, const char *b, const char *s) { 1257 /* quoted-string is unmodified (except for quoted-string end consistency) 1258 * including surrounding double-quotes and with quoted-pair unmodified */ 1259 if (__builtin_expect( (*s == '"'), 1)) 1260 lua_pushlstring(L, b, (size_t)(++s-b)); 1261 else { /*(else invalid quoted-string, but handle anyway)*/ 1262 /* append closing '"' for consistency */ 1263 lua_pushlstring(L, b, (size_t)(s-b)); 1264 if (*s != '\\') 1265 lua_pushlstring(L, "\"", 1); 1266 else { /* unescaped backslash at end of string; escape and close '"' */ 1267 lua_pushlstring(L, "\\\\\"", 3); 1268 ++s; /*(now *s == '\0')*/ 1269 } 1270 lua_concat(L, 2); 1271 } 1272 return s; 1273 } 1274 1275 static const char * magnet_push_quoted_string(lua_State *L, const char *s) { 1276 return magnet_push_quoted_string_range(L, s, magnet_scan_quoted_string(s)); 1277 } 1278 1279 static const char * magnet_cookie_param_push_token(lua_State *L, const char *s) { 1280 const char *b = s; 1281 while (*s!='=' /*(note: not strictly rejecting all 'separators')*/ 1282 && *s!=';' && *s!=' ' && *s!='\t' && *s!='\r' && *s!='\n' && *s) 1283 ++s; 1284 lua_pushlstring(L, b, (size_t)(s-b)); 1285 return s; 1286 } 1287 1288 static int magnet_cookie_tokens(lua_State *L) { 1289 /*(special-case cookies (';' separator); dup cookie-names overwritten)*/ 1290 lua_createtable(L, 0, 0); 1291 if (lua_isnoneornil(L, 1)) 1292 return 1; 1293 const char *s = luaL_checkstring(L, 1); 1294 do { 1295 while (*s==';' || *s==' ' || *s=='\t' || *s=='\r' || *s=='\n') 1296 ++s; 1297 if (*s == '\0') break; 1298 s = magnet_cookie_param_push_token(L, s); 1299 while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n') 1300 ++s; 1301 if (*s == '=') { 1302 do { 1303 ++s; 1304 } while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n'); 1305 if (*s==';' || *s=='\0') 1306 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/ 1307 else if (*s != '"') 1308 s = magnet_cookie_param_push_token(L, s); 1309 else 1310 s = magnet_push_quoted_string(L, s); 1311 } 1312 else { 1313 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/ 1314 } 1315 lua_settable(L, -3); 1316 while (*s!=';' && *s!='\0') ++s; /* ignore/skip stray tokens */ 1317 } while (*s++); 1318 return 1; 1319 } 1320 1321 static const char * magnet_push_token(lua_State *L, const char *s) { 1322 const char *b = s; 1323 while ( *s!=' ' && *s!='\t' && *s!='\r' && *s!='\n' 1324 && *s!=',' && *s!=';' && *s!='=' && *s) 1325 ++s; 1326 lua_pushlstring(L, b, (size_t)(s-b)); 1327 return s; 1328 } 1329 1330 static int magnet_header_tokens(lua_State *L) { 1331 /* split into sequence of tokens/words 1332 * Intermediate table is then more convenient to walk once quoted-string 1333 * parsed into table entries since quoted-string may contain separators. 1334 * Each token can be passed to lighty.c.quoteddec() 1335 * (lighty.c.quoteddec() returns string as-is if not quoted-string) 1336 * (note: non-validating; 1337 * e.g. existence of '=' token does not mean that next token is value, 1338 * and '=' in value which is not part of quoted-string will be 1339 * treated as separate token) 1340 * (note: case is preserved; non-quoted-string tokens are not lower-cased) 1341 * (words separated by whitespace are separate tokens unless quoted-string) 1342 * (if that format not permitted in a specific header, caller must detect) 1343 * (optional whitespace (OWS) and bad whitespace (BWS) are removed) 1344 * (caller can create lookup table from sequence table, as appropriate) */ 1345 lua_createtable(L, 0, 0); 1346 if (lua_isnoneornil(L, 1)) 1347 return 1; 1348 const char *s = luaL_checkstring(L, 1); 1349 int i = 0; 1350 do { 1351 while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n') 1352 ++s; 1353 if (*s=='\0') break; 1354 if (*s==',' || *s==';' || *s=='=') 1355 lua_pushlstring(L, s++, 1); 1356 else if (*s != '"') 1357 s = magnet_push_token(L, s); 1358 else 1359 s = magnet_push_quoted_string(L, s); 1360 lua_rawseti(L, -2, ++i); 1361 } while (*s); 1362 return 1; 1363 } 1364 1365 static int magnet_atpanic(lua_State *L) { 1366 request_st * const r = magnet_get_request(L); 1367 log_error(r->conf.errh, __FILE__, __LINE__, "(lua-atpanic) %s", 1368 lua_isstring(L, 1) ? lua_tostring(L, 1) : ""); 1369 /*longjmp(exceptionjmp, 1);*//*(must be init with setjmp() elsewhere)*/ 1370 return 0; 1371 } 1372 1373 static int magnet_reqhdr_get(lua_State *L) { 1374 /* __index: param 1 is the (empty) table the value was not found in */ 1375 size_t klen; 1376 const char * const k = luaL_checklstring(L, 2, &klen); 1377 request_st * const r = magnet_get_request(L); 1378 const int id = http_header_hkey_get(k, (uint32_t)klen); 1379 magnet_push_buffer(L, http_header_request_get(r, id, k, klen)); 1380 return 1; 1381 } 1382 1383 static int magnet_reqhdr_set(lua_State *L) { 1384 /* __newindex: param 1 is (empty) table in which value is to be set */ 1385 const_buffer k = magnet_checkconstbuffer(L, 2); 1386 const_buffer v = magnet_checkconstbuffer(L, 3); 1387 1388 request_st * const r = magnet_get_request(L); 1389 enum http_header_e id = http_header_hkey_get(k.ptr, (uint32_t)k.len); 1390 1391 switch (id) { 1392 /*case HTTP_HEADER_OTHER:*/ 1393 default: 1394 break; 1395 1396 case HTTP_HEADER_HOST: 1397 /* do not allow Host to be unset, even if HTTP/1.0 1398 * (change Host to something else, if you must */ 1399 if (0 == v.len) return 0; 1400 1401 /*(must set r->http_host if r->http_host was not previously set)*/ 1402 /* copied from request.c:http_request_header_set_Host() */ 1403 r->http_host = http_header_request_set_ptr(r, HTTP_HEADER_HOST, 1404 CONST_STR_LEN("Host")); 1405 buffer_copy_string_len_lc(r->http_host, v.ptr, v.len); 1406 return 0; 1407 1408 case HTTP_HEADER_CONTENT_LENGTH: 1409 /* not attempting to handle Content-Length modification; may revisit */ 1410 /* future: might permit setting to 0 to force discard of request body 1411 * but would have to check if request body present, and if 1412 * Content-Length was set, or if Transfer-Encoding: chunked, 1413 * and handle resetting internal chunked encoding state, 1414 * as well as making sure that this is handled properly for HTTP/2 */ 1415 return 0; /* silently ignore; do not allow modification */ 1416 1417 /* do not permit modification of hop-by-hop (connection) headers */ 1418 1419 case HTTP_HEADER_CONNECTION: 1420 /* do not permit modification of Connection, incl add/remove tokens */ 1421 /* future: might provide a different interface to set r->keep_alive = 0, 1422 * and also handle in context if HTTP/2 */ 1423 case HTTP_HEADER_TRANSFER_ENCODING: 1424 case HTTP_HEADER_SET_COOKIE:/*(response hdr;avoid accidental reflection)*/ 1425 return 0; /* silently ignore; do not allow modification */ 1426 #if 0 /*(eh, if script sets Upgrade, script probably intends this action)*/ 1427 case HTTP_HEADER_UPGRADE: 1428 /* note: modifications here do not modify Connection header 1429 * to add or remove "upgrade" token */ 1430 /* future: might allow removal of existing tokens, but not addition */ 1431 if (0 != v.len) return 0; /* do not allow Upgrade other than to unset */ 1432 break; 1433 #endif 1434 #if 0 /*(eh, if script sets TE, script probably intends this action)*/ 1435 case HTTP_HEADER_TE: 1436 if (0 != v.len) return 0; /* do not allow TE other than to unset */ 1437 break; 1438 #endif 1439 } 1440 1441 v.len 1442 ? http_header_request_set(r, id, k.ptr, k.len, v.ptr, v.len) 1443 : http_header_request_unset(r, id, k.ptr, k.len); 1444 return 0; 1445 } 1446 1447 static int magnet_reqhdr_pairs(lua_State *L) { 1448 request_st * const r = magnet_get_request(L); 1449 return magnet_array_pairs(L, &r->rqst_headers); 1450 } 1451 1452 static int magnet_resphdr_get(lua_State *L) { 1453 /* __index: param 1 is the (empty) table the value was not found in */ 1454 /* Note: access to lighttpd r->resp_headers here is *independent* from 1455 * the (pending) changes in the (deprecated) lua lighty.headers[] table */ 1456 size_t klen; 1457 const char * const k = luaL_checklstring(L, 2, &klen); 1458 request_st * const r = magnet_get_request(L); 1459 const int id = http_header_hkey_get(k, (uint32_t)klen); 1460 magnet_push_buffer(L, http_header_response_get(r, id, k, klen)); 1461 return 1; 1462 } 1463 1464 static int magnet_resphdr_set_kv(lua_State *L, request_st * const r) { 1465 const const_buffer k = magnet_checkconstbuffer(L, -2); 1466 const const_buffer v = magnet_checkconstbuffer(L, -1); 1467 const enum http_header_e id = http_header_hkey_get(k.ptr, (uint32_t)k.len); 1468 1469 switch (id) { 1470 /*case HTTP_HEADER_OTHER:*/ 1471 default: 1472 break; 1473 1474 case HTTP_HEADER_CONTENT_LENGTH: 1475 /* lighttpd handles Content-Length or Transfer-Encoding for response */ 1476 return 0; /* silently ignore; do not allow modification */ 1477 1478 /* do not permit modification of hop-by-hop (connection) headers */ 1479 1480 case HTTP_HEADER_CONNECTION: 1481 /* do not permit modification of Connection, incl add/remove tokens */ 1482 /* future: might provide a different interface to set r->keep_alive = 0, 1483 * and also handle in context if HTTP/2 */ 1484 case HTTP_HEADER_TRANSFER_ENCODING: 1485 return 0; /* silently ignore; do not allow modification */ 1486 } 1487 1488 if (0 == v.len) { 1489 http_header_response_unset(r, id, k.ptr, k.len); 1490 return 0; 1491 } 1492 1493 buffer * const vb = http_header_response_set_ptr(r, id, k.ptr, k.len); 1494 buffer_copy_string_len(vb, v.ptr, v.len); 1495 1496 if (r->http_version >= HTTP_VERSION_2) { 1497 /* handle multi-line response headers with HTTP/2 1498 * (lowercase header name and mark r->resp_header_repeated) 1499 * (similar to http_header.c:http_header_response_insert_addtl()) */ 1500 for (char *n = vb->ptr; (n = strchr(n, '\n')); ) { 1501 r->resp_header_repeated = 1; 1502 do { 1503 ++n; 1504 if (light_isupper(*n)) *n |= 0x20; 1505 } while (*n != ':' && *n != '\n' && *n != '\0'); 1506 } 1507 } 1508 1509 return 0; 1510 } 1511 1512 static int magnet_resphdr_set(lua_State *L) { 1513 /* __newindex: param 1 is (empty) table in which value is to be set */ 1514 /*const_buffer k = magnet_checkconstbuffer(L, 2);*/ 1515 /*const_buffer v = magnet_checkconstbuffer(L, 3);*/ 1516 request_st * const r = magnet_get_request(L); 1517 return magnet_resphdr_set_kv(L, r); 1518 } 1519 1520 static int magnet_resphdr_pairs(lua_State *L) { 1521 request_st * const r = magnet_get_request(L); 1522 return magnet_array_pairs(L, &r->resp_headers); 1523 } 1524 1525 static int magnet_status_get(lua_State *L) { 1526 /* __index: param 1 is the (empty) table the value was not found in */ 1527 const_buffer key = magnet_checkconstbuffer(L, 2); 1528 int *i = status_counter_get_counter(key.ptr, key.len); 1529 lua_pushinteger(L, (lua_Integer)*i); 1530 1531 return 1; 1532 } 1533 1534 static int magnet_status_set(lua_State *L) { 1535 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */ 1536 const_buffer key = magnet_checkconstbuffer(L, 2); 1537 int counter = (int) luaL_checkinteger(L, 3); 1538 1539 status_counter_set(key.ptr, key.len, counter); 1540 1541 return 0; 1542 } 1543 1544 static int magnet_status_pairs(lua_State *L) { 1545 return magnet_array_pairs(L, &plugin_stats); 1546 } 1547 1548 typedef struct { 1549 const char *name; 1550 uint32_t nlen; 1551 enum { 1552 MAGNET_ENV_UNSET, 1553 1554 MAGNET_ENV_PHYSICAL_PATH, 1555 MAGNET_ENV_PHYSICAL_REL_PATH, 1556 MAGNET_ENV_PHYSICAL_DOC_ROOT, 1557 MAGNET_ENV_PHYSICAL_BASEDIR, 1558 1559 MAGNET_ENV_URI_PATH, 1560 MAGNET_ENV_URI_PATH_RAW, 1561 MAGNET_ENV_URI_SCHEME, 1562 MAGNET_ENV_URI_AUTHORITY, 1563 MAGNET_ENV_URI_QUERY, 1564 1565 MAGNET_ENV_REQUEST_METHOD, 1566 MAGNET_ENV_REQUEST_URI, 1567 MAGNET_ENV_REQUEST_ORIG_URI, 1568 MAGNET_ENV_REQUEST_PATH_INFO, 1569 MAGNET_ENV_REQUEST_REMOTE_ADDR, 1570 MAGNET_ENV_REQUEST_REMOTE_PORT, 1571 MAGNET_ENV_REQUEST_SERVER_ADDR, 1572 MAGNET_ENV_REQUEST_SERVER_PORT, 1573 MAGNET_ENV_REQUEST_PROTOCOL, 1574 1575 MAGNET_ENV_RESPONSE_HTTP_STATUS, 1576 MAGNET_ENV_RESPONSE_BODY_LENGTH, 1577 MAGNET_ENV_RESPONSE_BODY 1578 } type; 1579 } magnet_env_t; 1580 1581 static const magnet_env_t magnet_env[] = { 1582 { CONST_STR_LEN("physical.path"), MAGNET_ENV_PHYSICAL_PATH }, 1583 { CONST_STR_LEN("physical.rel-path"), MAGNET_ENV_PHYSICAL_REL_PATH }, 1584 { CONST_STR_LEN("physical.doc-root"), MAGNET_ENV_PHYSICAL_DOC_ROOT }, 1585 { CONST_STR_LEN("physical.basedir"), MAGNET_ENV_PHYSICAL_BASEDIR }, 1586 1587 { CONST_STR_LEN("uri.path"), MAGNET_ENV_URI_PATH }, 1588 { CONST_STR_LEN("uri.path-raw"), MAGNET_ENV_URI_PATH_RAW }, 1589 { CONST_STR_LEN("uri.scheme"), MAGNET_ENV_URI_SCHEME }, 1590 { CONST_STR_LEN("uri.authority"), MAGNET_ENV_URI_AUTHORITY }, 1591 { CONST_STR_LEN("uri.query"), MAGNET_ENV_URI_QUERY }, 1592 1593 { CONST_STR_LEN("request.method"), MAGNET_ENV_REQUEST_METHOD }, 1594 { CONST_STR_LEN("request.uri"), MAGNET_ENV_REQUEST_URI }, 1595 { CONST_STR_LEN("request.orig-uri"), MAGNET_ENV_REQUEST_ORIG_URI }, 1596 { CONST_STR_LEN("request.path-info"), MAGNET_ENV_REQUEST_PATH_INFO }, 1597 { CONST_STR_LEN("request.remote-ip"), MAGNET_ENV_REQUEST_REMOTE_ADDR }, 1598 { CONST_STR_LEN("request.remote-addr"), MAGNET_ENV_REQUEST_REMOTE_ADDR }, 1599 { CONST_STR_LEN("request.remote-port"), MAGNET_ENV_REQUEST_REMOTE_PORT }, 1600 { CONST_STR_LEN("request.server-addr"), MAGNET_ENV_REQUEST_SERVER_ADDR }, 1601 { CONST_STR_LEN("request.server-port"), MAGNET_ENV_REQUEST_SERVER_PORT }, 1602 { CONST_STR_LEN("request.protocol"), MAGNET_ENV_REQUEST_PROTOCOL }, 1603 1604 { CONST_STR_LEN("response.http-status"), MAGNET_ENV_RESPONSE_HTTP_STATUS }, 1605 { CONST_STR_LEN("response.body-length"), MAGNET_ENV_RESPONSE_BODY_LENGTH }, 1606 { CONST_STR_LEN("response.body"), MAGNET_ENV_RESPONSE_BODY }, 1607 1608 { NULL, 0, MAGNET_ENV_UNSET } 1609 }; 1610 1611 static buffer *magnet_env_get_buffer_by_id(request_st * const r, int id) { 1612 buffer *dest = NULL; 1613 1614 /** 1615 * map all internal variables to lua 1616 * 1617 */ 1618 1619 switch (id) { 1620 case MAGNET_ENV_PHYSICAL_PATH: dest = &r->physical.path; break; 1621 case MAGNET_ENV_PHYSICAL_REL_PATH: dest = &r->physical.rel_path; break; 1622 case MAGNET_ENV_PHYSICAL_DOC_ROOT: dest = &r->physical.doc_root; break; 1623 case MAGNET_ENV_PHYSICAL_BASEDIR: dest = &r->physical.basedir; break; 1624 1625 case MAGNET_ENV_URI_PATH: dest = &r->uri.path; break; 1626 case MAGNET_ENV_URI_PATH_RAW: 1627 { 1628 dest = r->tmp_buf; 1629 buffer_clear(dest); 1630 uint32_t len = buffer_clen(&r->target); 1631 char *qmark = memchr(r->target.ptr, '?', len); 1632 buffer_copy_string_len(dest, r->target.ptr, qmark ? (uint32_t)(qmark - r->target.ptr) : len); 1633 break; 1634 } 1635 case MAGNET_ENV_URI_SCHEME: dest = &r->uri.scheme; break; 1636 case MAGNET_ENV_URI_AUTHORITY: dest = &r->uri.authority; break; 1637 case MAGNET_ENV_URI_QUERY: dest = &r->uri.query; break; 1638 1639 case MAGNET_ENV_REQUEST_METHOD: 1640 dest = r->tmp_buf; 1641 buffer_clear(dest); 1642 http_method_append(dest, r->http_method); 1643 break; 1644 case MAGNET_ENV_REQUEST_URI: dest = &r->target; break; 1645 case MAGNET_ENV_REQUEST_ORIG_URI: dest = &r->target_orig; break; 1646 case MAGNET_ENV_REQUEST_PATH_INFO: dest = &r->pathinfo; break; 1647 case MAGNET_ENV_REQUEST_REMOTE_ADDR: dest = &r->con->dst_addr_buf; break; 1648 case MAGNET_ENV_REQUEST_REMOTE_PORT: 1649 dest = r->tmp_buf; 1650 buffer_clear(dest); 1651 buffer_append_int(dest, sock_addr_get_port(&r->con->dst_addr)); 1652 break; 1653 case MAGNET_ENV_REQUEST_SERVER_ADDR: /* local IP without port */ 1654 { 1655 const server_socket * const srv_socket = r->con->srv_socket; 1656 dest = r->tmp_buf; 1657 buffer_clear(dest); 1658 switch (sock_addr_get_family(&srv_socket->addr)) { 1659 case AF_INET: 1660 case AF_INET6: 1661 if (sock_addr_is_addr_wildcard(&srv_socket->addr)) { 1662 sock_addr addrbuf; 1663 socklen_t addrlen = sizeof(addrbuf); 1664 const int fd = r->con->fd; 1665 if (0 == getsockname(fd,(struct sockaddr *)&addrbuf,&addrlen)) { 1666 char buf[INET6_ADDRSTRLEN + 1]; 1667 const char *s = sock_addr_inet_ntop(&addrbuf, buf, sizeof(buf)); 1668 if (NULL != s) 1669 buffer_copy_string_len(dest, s, strlen(s)); 1670 } 1671 } 1672 else 1673 buffer_copy_string_len(dest, srv_socket->srv_token->ptr, 1674 srv_socket->srv_token_colon); 1675 break; 1676 default: 1677 break; 1678 } 1679 break; 1680 } 1681 case MAGNET_ENV_REQUEST_SERVER_PORT: 1682 { 1683 const server_socket * const srv_socket = r->con->srv_socket; 1684 const buffer * const srv_token = srv_socket->srv_token; 1685 const uint32_t portoffset = srv_socket->srv_token_colon+1; 1686 dest = r->tmp_buf; 1687 buffer_copy_string_len(dest, srv_token->ptr+portoffset, 1688 buffer_clen(srv_token)-portoffset); 1689 break; 1690 } 1691 case MAGNET_ENV_REQUEST_PROTOCOL: 1692 dest = r->tmp_buf; 1693 buffer_clear(dest); 1694 http_version_append(dest, r->http_version); 1695 break; 1696 case MAGNET_ENV_RESPONSE_HTTP_STATUS: 1697 dest = r->tmp_buf; 1698 buffer_clear(dest); 1699 buffer_append_int(dest, r->http_status); 1700 break; 1701 case MAGNET_ENV_RESPONSE_BODY_LENGTH: 1702 dest = r->tmp_buf; 1703 buffer_clear(dest); 1704 if (!r->resp_body_finished) 1705 break; 1706 buffer_append_int(dest, chunkqueue_length(&r->write_queue)); 1707 break; 1708 case MAGNET_ENV_RESPONSE_BODY: 1709 if (!r->resp_body_finished) 1710 break; 1711 else { 1712 chunkqueue * const cq = &r->write_queue; 1713 off_t len = chunkqueue_length(cq); 1714 if (0 == len) { 1715 dest = r->tmp_buf; 1716 buffer_copy_string_len(dest, CONST_STR_LEN("")); 1717 break; 1718 } 1719 dest = chunkqueue_read_squash(cq, r->conf.errh); 1720 if (NULL == dest) { 1721 dest = r->tmp_buf; 1722 buffer_clear(dest); 1723 } 1724 } 1725 break; 1726 1727 case MAGNET_ENV_UNSET: break; 1728 } 1729 1730 return dest; 1731 } 1732 1733 static int magnet_env_get_id(const char * const key, const size_t klen) { 1734 for (int i = 0; magnet_env[i].name; ++i) { 1735 if (klen == magnet_env[i].nlen 1736 && 0 == memcmp(key, magnet_env[i].name, klen)) 1737 return magnet_env[i].type; 1738 } 1739 return MAGNET_ENV_UNSET; 1740 } 1741 1742 static buffer *magnet_env_get_buffer(request_st * const r, const char * const k, const size_t klen) { 1743 return magnet_env_get_buffer_by_id(r, magnet_env_get_id(k, klen)); 1744 } 1745 1746 static int magnet_env_get(lua_State *L) { 1747 /* __index: param 1 is the (empty) table the value was not found in */ 1748 size_t klen; 1749 const char * const k = luaL_checklstring(L, 2, &klen); 1750 request_st * const r = magnet_get_request(L); 1751 magnet_push_buffer(L, magnet_env_get_buffer(r, k, klen)); 1752 return 1; 1753 } 1754 1755 static int magnet_env_set(lua_State *L) { 1756 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */ 1757 size_t klen; 1758 const char * const key = luaL_checklstring(L, 2, &klen); 1759 const_buffer val = magnet_checkconstbuffer(L, 3); 1760 1761 request_st * const r = magnet_get_request(L); 1762 const int env_id = magnet_env_get_id(key, klen); 1763 1764 switch (env_id) { 1765 default: 1766 break; 1767 case MAGNET_ENV_URI_PATH_RAW: 1768 { 1769 /* modify uri-path of r->target; preserve query-part, if present */ 1770 /* XXX: should we require that resulting path begin with '/' or %2F ? */ 1771 const uint32_t len = buffer_clen(&r->target); 1772 const char * const qmark = memchr(r->target.ptr, '?', len); 1773 if (NULL != qmark) 1774 buffer_copy_string_len(r->tmp_buf, qmark, 1775 len - (uint32_t)(qmark - r->target.ptr)); 1776 buffer_copy_string_len(&r->target, val.ptr, val.len); 1777 if (NULL != qmark) 1778 buffer_append_string_buffer(&r->target, r->tmp_buf); 1779 return 0; 1780 } 1781 case MAGNET_ENV_REQUEST_REMOTE_ADDR: 1782 #ifdef HAVE_SYS_UN_H 1783 if (val.len && *val.ptr == '/' 1784 && 0 == sock_addr_assign(&r->con->dst_addr, AF_UNIX, 0, val.ptr)) { 1785 } 1786 else 1787 #endif 1788 { 1789 sock_addr saddr; 1790 saddr.plain.sa_family = AF_UNSPEC; 1791 if (1 == sock_addr_from_str_numeric(&saddr, val.ptr, r->conf.errh) 1792 && saddr.plain.sa_family != AF_UNSPEC) { 1793 sock_addr_set_port(&saddr, 0); 1794 memcpy(&r->con->dst_addr, &saddr, sizeof(sock_addr)); 1795 } 1796 else { 1797 return luaL_error(L, "lighty.r.req_attr['%s'] invalid addr: %s", 1798 key, val.ptr); 1799 } 1800 } 1801 buffer_copy_string_len(&r->con->dst_addr_buf, val.ptr, val.len); 1802 config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP); 1803 return 0; 1804 case MAGNET_ENV_REQUEST_REMOTE_PORT: 1805 sock_addr_set_port(&r->con->dst_addr, (unsigned short)atoi(val.ptr)); 1806 return 0; 1807 case MAGNET_ENV_RESPONSE_HTTP_STATUS: 1808 case MAGNET_ENV_RESPONSE_BODY_LENGTH: 1809 case MAGNET_ENV_RESPONSE_BODY: 1810 return luaL_error(L, "lighty.r.req_attr['%s'] is read-only", key); 1811 } 1812 1813 buffer * const dest = magnet_env_get_buffer_by_id(r, env_id); 1814 if (NULL == dest) 1815 return luaL_error(L, "couldn't store '%s' in lighty.r.req_attr[]", key); 1816 1817 if (lua_isnoneornil(L, 3)) { 1818 if (env_id==MAGNET_ENV_URI_QUERY || env_id==MAGNET_ENV_PHYSICAL_PATH) 1819 buffer_clear(dest); 1820 else 1821 buffer_blank(dest); 1822 } 1823 else { 1824 buffer_copy_string_len(dest, val.ptr, val.len); 1825 /* NB: setting r->uri.query does not modify query-part in r->target */ 1826 } 1827 1828 switch (env_id) { 1829 case MAGNET_ENV_URI_SCHEME: 1830 buffer_to_lower(dest); 1831 config_cond_cache_reset_item(r, COMP_HTTP_SCHEME); 1832 break; 1833 case MAGNET_ENV_URI_AUTHORITY: 1834 r->server_name = dest; 1835 buffer_to_lower(dest); 1836 config_cond_cache_reset_item(r, COMP_HTTP_HOST); 1837 break; 1838 case MAGNET_ENV_URI_PATH: 1839 config_cond_cache_reset_item(r, COMP_HTTP_URL); 1840 break; 1841 case MAGNET_ENV_URI_QUERY: 1842 config_cond_cache_reset_item(r, COMP_HTTP_QUERY_STRING); 1843 break; 1844 default: 1845 break; 1846 } 1847 1848 return 0; 1849 } 1850 1851 static int magnet_env_next(lua_State *L) { 1852 const int pos = lua_tointeger(L, lua_upvalueindex(1)); 1853 1854 /* ignore previous key: use upvalue for current pos */ 1855 lua_settop(L, 0); 1856 1857 if (NULL == magnet_env[pos].name) return 0; /* end of list */ 1858 /* Update our positional upval to reflect our new current position */ 1859 lua_pushinteger(L, pos + 1); 1860 lua_replace(L, lua_upvalueindex(1)); 1861 1862 /* key to return */ 1863 lua_pushlstring(L, magnet_env[pos].name, magnet_env[pos].nlen); 1864 1865 /* get value */ 1866 request_st * const r = magnet_get_request(L); 1867 magnet_push_buffer(L, magnet_env_get_buffer_by_id(r, magnet_env[pos].type)); 1868 1869 /* return 2 items on the stack (key, value) */ 1870 return 2; 1871 } 1872 1873 static int magnet_env_pairs(lua_State *L) { 1874 lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */ 1875 lua_pushcclosure(L, magnet_env_next, 1); /* Push our new closure with 1 upvals */ 1876 return 1; 1877 } 1878 1879 static int magnet_envvar_get(lua_State *L) { 1880 /* __index: param 1 is the (empty) table the value was not found in */ 1881 size_t klen; 1882 const char * const k = luaL_checklstring(L, 2, &klen); 1883 request_st * const r = magnet_get_request(L); 1884 magnet_push_buffer(L, http_header_env_get(r, k, klen)); 1885 return 1; 1886 } 1887 1888 static int magnet_envvar_set(lua_State *L) { 1889 /* __newindex: param 1 is the (empty) table the value is supposed to be set in */ 1890 const_buffer key = magnet_checkconstbuffer(L, 2); 1891 if (__builtin_expect( (lua_isnil(L, 3)), 0)) { 1892 request_st * const r = magnet_get_request(L); 1893 buffer * const v = http_header_env_get(r, key.ptr, key.len); 1894 if (v) buffer_clear(v); /*(unset)*/ 1895 return 0; 1896 } 1897 const_buffer val = magnet_checkconstbuffer(L, 3); 1898 request_st * const r = magnet_get_request(L); 1899 http_header_env_set(r, key.ptr, key.len, val.ptr, val.len); 1900 return 0; 1901 } 1902 1903 static int magnet_envvar_pairs(lua_State *L) { 1904 request_st * const r = magnet_get_request(L); 1905 return magnet_array_pairs(L, &r->env); 1906 } 1907 1908 1909 static int magnet_respbody_add(lua_State *L) { 1910 request_st * const r = magnet_get_request(L); 1911 if (lua_isstring(L, -1)) { 1912 const_buffer data = magnet_checkconstbuffer(L, -1); 1913 http_chunk_append_mem(r, data.ptr, data.len); 1914 return 1; /* boolean true */ 1915 } 1916 else if (!lua_istable(L, -1)) 1917 return 0; /* boolean false */ 1918 1919 /* note: differs from magnet_attach_content(); 1920 * magnet_attach_content() has misnamed 'length' param which 1921 * is treated as 0-offset pos one after end of range to send. 1922 * Here, 'length' means 'length', as one would expect */ 1923 for (int i=1, end=0, n=(int)lua_rawlen(L,-1); !end && i <= n; ++i) { 1924 lua_rawgeti(L, -1, i); 1925 1926 if (lua_isstring(L, -1)) { 1927 const_buffer data = magnet_checkconstbuffer(L, -1); 1928 http_chunk_append_mem(r, data.ptr, data.len); 1929 } 1930 else if (lua_istable(L, -1)) { 1931 lua_getfield(L, -1, "filename"); 1932 lua_getfield(L, -2, "length"); 1933 lua_getfield(L, -3, "offset"); 1934 1935 if (lua_isstring(L, -3)) { /* filename has to be a string */ 1936 off_t off = (off_t) luaL_optinteger(L, -1, 0); 1937 off_t len = (off_t) luaL_optinteger(L, -2, -1); 1938 /*(-1 len as flag to use file size minus offset (below))*/ 1939 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/ 1940 const buffer * const fn = magnet_checkbuffer(L, -3, &stor); 1941 stat_cache_entry * const sce = (!buffer_is_blank(fn)) 1942 ? stat_cache_get_entry_open(fn, r->conf.follow_symlink) 1943 : NULL; 1944 if (sce && (sce->fd >= 0 || sce->st.st_size == 0)) { 1945 /* treat negative offset as bytes from end of file */ 1946 /* treat negative len as bytes from offset to end of file */ 1947 if (off > sce->st.st_size) 1948 off = sce->st.st_size; 1949 else if (off < 0) { 1950 off = sce->st.st_size - off; 1951 if (off < 0) off = 0; 1952 } 1953 if (len < 0 || sce->st.st_size - off < len) 1954 len = sce->st.st_size - off; 1955 if (len) 1956 http_chunk_append_file_ref_range(r, sce, off, len); 1957 } 1958 else { 1959 log_error(r->conf.errh, __FILE__, __LINE__, 1960 "error opening file '%s'", fn->ptr); 1961 end = 1; 1962 } 1963 } 1964 else { 1965 log_error(r->conf.errh, __FILE__, __LINE__, 1966 "body[%d] table field \"filename\" must be a string", i); 1967 end = 1; 1968 } 1969 1970 lua_pop(L, 3); 1971 } 1972 else if (lua_isnil(L, -1)) { /* end of list */ 1973 end = 1; 1974 } 1975 else { 1976 log_error(r->conf.errh, __FILE__, __LINE__, 1977 "body[%d] is neither a string nor a table", i); 1978 end = 1; 1979 } 1980 1981 lua_pop(L, 1); /* pop the content[...] entry value */ 1982 } 1983 1984 return 1; /* boolean true */ 1985 } 1986 1987 1988 static int magnet_respbody(lua_State *L) { 1989 /* __index: param 1 is the (empty) table the value was not found in */ 1990 size_t klen; 1991 const char * const k = luaL_checklstring(L, 2, &klen); 1992 switch (k[0]) { 1993 case 'a': /* add; lighty.r.resp_body.add */ 1994 if (k[1] == 'd' && k[2] == 'd' && k[3] == '\0') { 1995 lua_pushcclosure(L, magnet_respbody_add, 0); 1996 return 1; 1997 } 1998 break; 1999 #if 0 /*(future: provide pairs() interface to iterate over chunkqueue)*/ 2000 /*(might convert chunks into table of strings, {filename="..."})*/ 2001 /*(what about c->offset into chunk?)*/ 2002 case 'g': /* get; lighty.r.resp_body.get */ 2003 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') { 2004 /* equivalent to lighty.r.attr["response.body"] */ 2005 /* equivalent to lighty.env["response.body"] */ 2006 if (r->resp_body_finished) { 2007 chunkqueue * const cq = &r->write_queue; 2008 chunkqueue_length(cq) 2009 ? magnet_push_buffer(L, 2010 chunkqueue_read_squash(cq,r->conf.errh)) 2011 : lua_pushlstring(L, "", 0); 2012 } 2013 else 2014 lua_pushnil(L); /*(?maybe return -1 instead if len unknown?)*/ 2015 return 1; 2016 } 2017 break; 2018 #endif 2019 case 'l': /* len; lighty.r.resp_body.len */ 2020 if (k[1] == 'e' && k[2] == 'n' && k[3] == '\0') { 2021 /* equivalent to lighty.r.req_attr["response.body-length"] */ 2022 /* equivalent to lighty.env["response.body-length"] */ 2023 request_st * const r = magnet_get_request(L); 2024 if (r->resp_body_finished) 2025 lua_pushinteger(L, chunkqueue_length(&r->write_queue)); 2026 else 2027 lua_pushnil(L); /*(?maybe return -1 instead if len unknown?)*/ 2028 return 1; 2029 } 2030 break; 2031 case 's': /* set; lighty.r.resp_body.set */ 2032 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') { 2033 request_st * const r = magnet_get_request(L); 2034 http_response_body_clear(r, 0); /* clear respbody, then add */ 2035 lua_pushcclosure(L, magnet_respbody_add, 0); 2036 return 1; 2037 } 2038 break; 2039 default: 2040 break; 2041 } 2042 lua_pushliteral(L, "lighty.r.resp_body invalid method or param"); 2043 lua_error(L); 2044 return 0; 2045 } 2046 2047 2048 static int magnet_lighty_result_get(lua_State *L) { 2049 /* __index: param 1 is the lighty table the value was not found in */ 2050 lua_getfield(L, 1, "result"); /* lighty.result */ 2051 lua_pushvalue(L, 2); 2052 lua_rawget(L, -2); 2053 if (lua_isnil(L, -1)) { 2054 const_buffer k = magnet_checkconstbuffer(L, 2); 2055 if (k.len == 7 && 0 == memcmp(k.ptr, "content", 7)) { 2056 lua_createtable(L, 0, 0); /* create "content" table on demand */ 2057 lua_pushvalue(L, 2); /* k: "content" */ 2058 lua_pushvalue(L, -2); /* v: table */ 2059 lua_rawset(L, 3); /* set in "lighty.result" */ 2060 } 2061 } 2062 lua_replace(L, 3); 2063 return 1; 2064 } 2065 2066 static int magnet_lighty_result_set(lua_State *L) { 2067 /* __newindex: param 1 is lighty table the value is supposed to be set in */ 2068 /* assign value to table, replacing existing value, if any 2069 * (expecting "content" here, but compatible with prior misuse potential) 2070 * (special-case "header" back into lighty.header) */ 2071 const_buffer k = magnet_checkconstbuffer(L, 2); 2072 if (k.len != 6 || 0 != memcmp(k.ptr, "header", 6)) { 2073 lua_getfield(L, 1, "result"); /* lighty.result */ 2074 lua_replace(L, 1); /* replace param 1, original target table */ 2075 } 2076 lua_rawset(L, -3); 2077 return 0; 2078 } 2079 2080 2081 static void magnet_copy_response_header(lua_State * const L, request_st * const r, const int lighty_table_ndx) { 2082 if (lua_getfield_and_type(L, lighty_table_ndx, "header") == LUA_TTABLE) { 2083 /* walk lighty.header */ 2084 for (lua_pushnil(L); lua_next(L, -2); ) { 2085 if (lua_isstring(L, -1) && lua_isstring(L, -2)) 2086 magnet_resphdr_set_kv(L, r); 2087 /* unset value after use while walking table entries */ 2088 lua_pop(L, 1); 2089 lua_pushvalue(L, -1); 2090 lua_pushnil(L); 2091 lua_rawset(L, -4); 2092 } 2093 } 2094 /*lua_pop(L, 1);*/ /* pop lighty.header */ /* defer to later */ 2095 } 2096 2097 /** 2098 * walk through the content array set by lua script, e.g. 2099 * lighy.header["Content-Type"] = "text/html" 2100 * lighty.content = 2101 * { "<html><body><pre>", { file = "/content" } , "</pre></body></html>" } 2102 * return 200 2103 */ 2104 __attribute_noinline__ 2105 static void magnet_attach_content_table(lua_State * const L, request_st * const r) { 2106 for (int i=1, end=0, n=(int)lua_rawlen(L,-1); !end && i <= n; ++i) { 2107 lua_rawgeti(L, -1, i); 2108 2109 /* -1 is the value and should be the value ... aka a table */ 2110 if (lua_isstring(L, -1)) { 2111 const_buffer data = magnet_checkconstbuffer(L, -1); 2112 http_chunk_append_mem(r, data.ptr, data.len); 2113 } else if (lua_istable(L, -1)) { 2114 lua_getfield(L, -1, "filename"); 2115 lua_getfield(L, -2, "length"); /* (0-based) end of range (not actually "length") */ 2116 lua_getfield(L, -3, "offset"); /* (0-based) start of range */ 2117 2118 if (lua_isstring(L, -3)) { /* filename has to be a string */ 2119 /*(luaL_optinteger might raise error, which we want to avoid)*/ 2120 /*off_t off = (off_t) luaL_optinteger(L, -1, 0);*/ 2121 /*off_t len = (off_t) luaL_optinteger(L, -2, -1);*/ /*(-1 as flag to use file size minus offset (below))*/ 2122 int isnum = 1; 2123 off_t off = lua_isnil(L, -1) ? 0 : (off_t) lua_tointegerx(L, -1, &isnum); 2124 if (!isnum) { 2125 off = 0; 2126 log_error(r->conf.errh, __FILE__, __LINE__, 2127 "content[%d] is a table and field \"offset\" must be an integer", i); 2128 } 2129 isnum = 1; 2130 off_t len = lua_isnil(L, -2) ? -1 : (off_t) lua_tointegerx(L, -2, &isnum); 2131 /*(-1 len as flag to use file size minus offset (below))*/ 2132 if (!isnum) { 2133 len = -1; 2134 log_error(r->conf.errh, __FILE__, __LINE__, 2135 "content[%d] is a table and field \"length\" must be an integer", i); 2136 } 2137 if (off < 0) { 2138 log_error(r->conf.errh, __FILE__, __LINE__, 2139 "offset for '%s' is negative", lua_tostring(L, -3)); 2140 end = 1; 2141 } else if (len >= off) { 2142 len -= off; 2143 } else if (-1 != len) { 2144 log_error(r->conf.errh, __FILE__, __LINE__, 2145 "offset > length for '%s'", lua_tostring(L, -3)); 2146 end = 1; 2147 } 2148 2149 if (!end && 0 != len) { 2150 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/ 2151 const buffer * const fn = magnet_checkbuffer(L, -3, &stor); 2152 stat_cache_entry * const sce = (!buffer_is_blank(fn)) 2153 ? stat_cache_get_entry_open(fn, r->conf.follow_symlink) 2154 : NULL; 2155 if (sce && (sce->fd >= 0 || sce->st.st_size == 0)) { 2156 if (len == -1 || sce->st.st_size - off < len) 2157 len = sce->st.st_size - off; 2158 if (len > 0) 2159 http_chunk_append_file_ref_range(r, sce, off, len); 2160 } else { 2161 log_error(r->conf.errh, __FILE__, __LINE__, 2162 "error opening file content '%s' at offset %lld", 2163 lua_tostring(L, -3), (long long)off); 2164 end = 1; 2165 } 2166 } 2167 } else { 2168 log_error(r->conf.errh, __FILE__, __LINE__, 2169 "content[%d] is a table and field \"filename\" must be a string", i); 2170 end = 1; 2171 } 2172 2173 lua_pop(L, 3); 2174 } else if (lua_isnil(L, -1)) { 2175 /* end of list */ 2176 end = 1; 2177 } else { 2178 log_error(r->conf.errh, __FILE__, __LINE__, 2179 "content[%d] is neither a string nor a table", i); 2180 end = 1; 2181 } 2182 2183 lua_pop(L, 1); /* pop the content[...] entry value */ 2184 } 2185 } 2186 2187 static void magnet_attach_content(lua_State * const L, request_st * const r, const int lighty_table_ndx) { 2188 if (lua_getfield_and_type(L, lighty_table_ndx, "result") != LUA_TTABLE) { 2189 /*lua_pop(L, 1);*/ /* pop nil lighty.result */ /* defer to later */ 2190 return; 2191 } 2192 switch (lua_getfield_and_type(L, -1, "content")) {/*lighty.result.content*/ 2193 case LUA_TNIL: 2194 break; 2195 case LUA_TTABLE: 2196 /* content found and is a table */ 2197 http_response_body_clear(r, 0); 2198 magnet_attach_content_table(L, r); 2199 /* assign nil to content to remove from "result" table */ 2200 lua_pushnil(L); 2201 lua_setfield(L, -3, "content"); 2202 break; 2203 default: 2204 log_error(r->conf.errh, __FILE__, __LINE__, 2205 "lighty.content has to be a table"); 2206 break; 2207 } 2208 /* defer lua_pop() to later */ 2209 /*lua_pop(L, 2);*/ /* pop lighty.result.content and lighty.result */ 2210 } 2211 2212 static void magnet_mainenv_metatable(lua_State * const L) { 2213 if (luaL_newmetatable(L, "lighty.mainenv")) { /* (sp += 1) */ 2214 lua_pushglobaltable(L); /* (sp += 1) */ 2215 lua_setfield(L, -2, "__index"); /* { __index = _G } (sp -= 1) */ 2216 lua_pushboolean(L, 0); /* (sp += 1) */ 2217 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2218 } 2219 } 2220 2221 __attribute_cold__ 2222 __attribute_noinline__ 2223 static void magnet_init_lighty_table(lua_State * const L) { 2224 /* init lighty table and other initial setup for global lua_State */ 2225 2226 lua_atpanic(L, magnet_atpanic); 2227 2228 lua_pushglobaltable(L); /* (sp += 1) */ 2229 2230 /* we have to overwrite the print function */ 2231 lua_pushcfunction(L, magnet_print); /* (sp += 1) */ 2232 lua_setfield(L, -2, "print"); /* -1 is the env we want to set(sp -= 1) */ 2233 2234 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 2235 /* override the default pairs() function to our __pairs capable version; 2236 * not needed for lua 5.2+ 2237 */ 2238 lua_getglobal(L, "pairs"); /* push original pairs() (sp += 1) */ 2239 lua_pushcclosure(L, magnet_pairs, 1); 2240 lua_setfield(L, -2, "pairs"); /* (sp -= 1) */ 2241 #endif 2242 2243 lua_pop(L, 1); /* pop global table */ /* (sp -= 1) */ 2244 2245 magnet_mainenv_metatable(L); /* init table for mem locality (sp += 1) */ 2246 magnet_stat_metatable(L); /* init table for mem locality (sp += 1) */ 2247 magnet_readdir_metatable(L); /* init table for mem locality (sp += 1) */ 2248 lua_pop(L, 3); /* pop init'd metatables (sp -= 3) */ 2249 2250 /* lighty table 2251 * 2252 * lighty.r.req_header[] HTTP request headers 2253 * lighty.r.req_attr[] HTTP request attributes / components 2254 * lighty.r.req_env[] HTTP request environment variables 2255 * lighty.r.resp_header[] HTTP response headers 2256 * lighty.r.resp_body.* HTTP response body accessors 2257 * lighty.r.resp_body.len HTTP response body length 2258 * lighty.r.resp_body.add() HTTP response body add (string or table) 2259 * lighty.r.resp_body.set() HTTP response body set (string or table) 2260 * 2261 * (older interface) 2262 * 2263 * lighty.request[] HTTP request headers 2264 * lighty.req_env[] environment variables 2265 * lighty.env[] lighttpd request metadata, 2266 * various url components, 2267 * physical file paths; 2268 * might contain nil values 2269 * 2270 * lighty.header[] (script) HTTP response headers 2271 * lighty.content[] (script) HTTP response body (table of string/file) 2272 * 2273 * lighty.status[] lighttpd status counters 2274 */ 2275 2276 /*(adjust the preallocation if more entries are added)*/ 2277 lua_createtable(L, 0, 9); /* lighty.* (returned on stack) (sp += 1) */ 2278 2279 lua_createtable(L, 0, 5); /* lighty.r (sp += 1) */ 2280 2281 lua_createtable(L, 0, 0); /* {} (sp += 1) */ 2282 lua_createtable(L, 0, 4); /* metatable for req_header table (sp += 1) */ 2283 lua_pushcfunction(L, magnet_reqhdr_get); /* (sp += 1) */ 2284 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2285 lua_pushcfunction(L, magnet_reqhdr_set); /* (sp += 1) */ 2286 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2287 lua_pushcfunction(L, magnet_reqhdr_pairs); /* (sp += 1) */ 2288 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2289 lua_pushboolean(L, 0); /* (sp += 1) */ 2290 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2291 lua_setmetatable(L, -2); /* tie the metatable to req_header (sp -= 1) */ 2292 lua_setfield(L, -2, "req_header"); /* req_header = {} (sp -= 1) */ 2293 2294 lua_createtable(L, 0, 0); /* {} (sp += 1) */ 2295 lua_createtable(L, 0, 4); /* metatable for req_attr table (sp += 1) */ 2296 lua_pushcfunction(L, magnet_env_get); /* (sp += 1) */ 2297 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2298 lua_pushcfunction(L, magnet_env_set); /* (sp += 1) */ 2299 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2300 lua_pushcfunction(L, magnet_env_pairs); /* (sp += 1) */ 2301 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2302 lua_pushboolean(L, 0); /* (sp += 1) */ 2303 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2304 lua_setmetatable(L, -2); /* tie the metatable to req_attr (sp -= 1) */ 2305 lua_setfield(L, -2, "req_attr"); /* req_attr = {} (sp -= 1) */ 2306 2307 lua_createtable(L, 0, 0); /* {} (sp += 1) */ 2308 lua_createtable(L, 0, 4); /* metatable for req_env table (sp += 1) */ 2309 lua_pushcfunction(L, magnet_envvar_get); /* (sp += 1) */ 2310 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2311 lua_pushcfunction(L, magnet_envvar_set); /* (sp += 1) */ 2312 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2313 lua_pushcfunction(L, magnet_envvar_pairs); /* (sp += 1) */ 2314 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2315 lua_pushboolean(L, 0); /* (sp += 1) */ 2316 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2317 lua_setmetatable(L, -2); /* tie the metatable to req_env (sp -= 1) */ 2318 lua_setfield(L, -2, "req_env"); /* req_env = {} (sp -= 1) */ 2319 2320 lua_createtable(L, 0, 0); /* {} (sp += 1) */ 2321 lua_createtable(L, 0, 4); /* metatable for resp_header table (sp += 1) */ 2322 lua_pushcfunction(L, magnet_resphdr_get); /* (sp += 1) */ 2323 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2324 lua_pushcfunction(L, magnet_resphdr_set); /* (sp += 1) */ 2325 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2326 lua_pushcfunction(L, magnet_resphdr_pairs); /* (sp += 1) */ 2327 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2328 lua_pushboolean(L, 0); /* (sp += 1) */ 2329 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2330 lua_setmetatable(L, -2); /* tie the metatable to resp_header (sp -= 1) */ 2331 lua_setfield(L, -2, "resp_header"); /* resp_header = {} (sp -= 1) */ 2332 2333 lua_createtable(L, 0, 0); /* {} (sp += 1) */ 2334 lua_createtable(L, 0, 3); /* metatable for resp_body table (sp += 1) */ 2335 lua_pushcfunction(L, magnet_respbody); /* (sp += 1) */ 2336 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2337 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 2338 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2339 lua_pushboolean(L, 0); /* (sp += 1) */ 2340 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2341 lua_setmetatable(L, -2); /* tie the metatable to resp_body (sp -= 1) */ 2342 lua_setfield(L, -2, "resp_body"); /* resp_body = {} (sp -= 1) */ 2343 2344 lua_createtable(L, 0, 2); /* metatable for r table (sp += 1) */ 2345 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 2346 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2347 lua_pushboolean(L, 0); /* (sp += 1) */ 2348 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2349 lua_setmetatable(L, -2); /* tie the metatable to r (sp -= 1) */ 2350 lua_setfield(L, -2, "r"); /* lighty.r = {} (sp -= 1) */ 2351 2352 /* compatibility with previous mod_magnet interfaces in top of lighty.* */ 2353 lua_getfield(L, -1, "r"); /* (sp += 1) */ 2354 /* alias lighty.request -> lighty.r.req_header */ 2355 lua_getfield(L, -1, "req_header"); /* (sp += 1) */ 2356 lua_setfield(L, -3, "request"); /* request = {} (sp -= 1) */ 2357 /* alias lighty.env -> lighty.r.req_attr */ 2358 lua_getfield(L, -1, "req_attr"); /* (sp += 1) */ 2359 lua_setfield(L, -3, "env"); /* env = {} (sp -= 1) */ 2360 /* alias lighty.req_env -> lighty.r.req_env */ 2361 lua_getfield(L, -1, "req_env"); /* (sp += 1) */ 2362 lua_setfield(L, -3, "req_env"); /* req_env = {} (sp -= 1) */ 2363 lua_pop(L, 1); /* (sp -= 1) */ 2364 2365 lua_createtable(L, 0, 0); /* {} (sp += 1) */ 2366 lua_createtable(L, 0, 4); /* metatable for status table (sp += 1) */ 2367 lua_pushcfunction(L, magnet_status_get); /* (sp += 1) */ 2368 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2369 lua_pushcfunction(L, magnet_status_set); /* (sp += 1) */ 2370 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2371 lua_pushcfunction(L, magnet_status_pairs); /* (sp += 1) */ 2372 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2373 lua_pushboolean(L, 0); /* (sp += 1) */ 2374 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2375 lua_setmetatable(L, -2); /* tie the metatable to status (sp -= 1) */ 2376 lua_setfield(L, -2, "status"); /* status = {} (sp -= 1) */ 2377 2378 lua_pushinteger(L, MAGNET_RESTART_REQUEST); 2379 lua_setfield(L, -2, "RESTART_REQUEST"); 2380 2381 lua_pushcfunction(L, magnet_stat); /* (sp += 1) */ 2382 lua_setfield(L, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */ 2383 2384 /* add empty 'header' and 'result' tables; ('content' is under 'result') */ 2385 /* (prefer newer lighty.r.resp_header(), lighty.r.resp_body() interfaces) */ 2386 lua_createtable(L, 0, 8); /* {} (sp += 1) */ 2387 lua_setfield(L, -2, "header"); /* header = {} (sp -= 1) */ 2388 2389 lua_createtable(L, 0, 1); /* {} (sp += 1) */ 2390 lua_setfield(L, -2, "result"); /* result = {} (sp -= 1) */ 2391 2392 static const luaL_Reg cmethods[] = { 2393 { "stat", magnet_stat } 2394 ,{ "time", magnet_time } 2395 ,{ "rand", magnet_rand } 2396 ,{ "md", magnet_md_once } /* message digest */ 2397 ,{ "hmac", magnet_hmac_once } /* HMAC */ 2398 ,{ "digest_eq", magnet_digest_eq } /* timing-safe eq fixed len */ 2399 ,{ "secret_eq", magnet_secret_eq } /* timing-safe eq variable len */ 2400 ,{ "b64urldec", magnet_b64urldec } /* validate and decode base64url */ 2401 ,{ "b64urlenc", magnet_b64urlenc } /* base64url encode, no padding */ 2402 ,{ "b64dec", magnet_b64stddec } /* validate and decode base64 */ 2403 ,{ "b64enc", magnet_b64stdenc } /* base64 encode, no padding */ 2404 ,{ "hexdec", magnet_hexdec } /* validate and decode hex str */ 2405 ,{ "hexenc", magnet_hexenc } /* uc; lc w/ lua s = s:lower() */ 2406 ,{ "xmlenc", magnet_xmlenc } /* xml-encode/html-encode: <>&'\" */ 2407 ,{ "urldec", magnet_urldec } /* url-decode (path) */ 2408 ,{ "urlenc", magnet_urlenc } /* url-encode (path) */ 2409 ,{ "urldec_query", magnet_urldec_query } /* url-decode query-string */ 2410 ,{ "urlenc_query", magnet_urlenc_query } /* url-encode query-string */ 2411 ,{ "urlenc_normalize", magnet_urlenc_normalize }/* url-enc normalization */ 2412 ,{ "fspath_simplify", magnet_fspath_simplify } /* simplify fspath */ 2413 ,{ "cookie_tokens", magnet_cookie_tokens } /* parse cookie tokens */ 2414 ,{ "header_tokens", magnet_header_tokens } /* parse header tokens seq */ 2415 ,{ "readdir", magnet_readdir } /* dir walk */ 2416 ,{ "quoteddec", magnet_quoteddec } /* quoted-string decode */ 2417 ,{ "quotedenc", magnet_quotedenc } /* quoted-string encode */ 2418 ,{ NULL, NULL } 2419 }; 2420 2421 lua_createtable(L, 0, sizeof(cmethods)/sizeof(luaL_Reg)-1);/*(sp += 1) */ 2422 luaL_setfuncs(L, cmethods, 0); 2423 lua_createtable(L, 0, 2); /* metatable for c table (sp += 1) */ 2424 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 2425 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2426 lua_pushboolean(L, 0); /* (sp += 1) */ 2427 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2428 lua_setmetatable(L, -2); /* tie the metatable to c (sp -= 1) */ 2429 lua_setfield(L, -2, "c"); /* c = {} (sp -= 1) */ 2430 2431 lua_createtable(L, 0, 3); /* metatable for lighty table (sp += 1) */ 2432 lua_pushcfunction(L, magnet_lighty_result_get); /* (sp += 1) */ 2433 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2434 lua_pushcfunction(L, magnet_lighty_result_set); /* (sp += 1) */ 2435 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2436 lua_pushboolean(L, 0); /* (sp += 1) */ 2437 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2438 lua_setmetatable(L, -2); /* tie the metatable to lighty (sp -= 1) */ 2439 2440 /* lighty table (returned on stack) */ 2441 } 2442 2443 static void magnet_clear_table(lua_State * const L, int ndx) { 2444 /*(avoid lua_absindex() func call since expecting empty tables for legacy 2445 * interfaces to lighty.header and lighty.content, though not script-env)*/ 2446 /*ndx = lua_absindex(ndx);*//*(lua 5.2+)*/ 2447 if (ndx < 0) --ndx; 2448 for (lua_pushnil(L); lua_next(L, ndx); ) { 2449 lua_pop(L, 1); 2450 lua_pushvalue(L, -1); 2451 lua_pushnil(L); 2452 lua_rawset(L, ndx < 0 ? ndx - 2 : ndx); 2453 } 2454 } 2455 2456 static void magnet_reset_lighty_table(lua_State * const L, const int lighty_table_ndx) { 2457 /* clear response tables (release mem if reusing lighty table) */ 2458 if (lua_getfield_and_type(L, lighty_table_ndx, "result") == LUA_TTABLE) 2459 magnet_clear_table(L, -1); /*lighty.result*/ 2460 else { 2461 lua_createtable(L, 0, 1); 2462 lua_setfield(L, lighty_table_ndx, "result"); 2463 } 2464 2465 if (lua_getfield_and_type(L, lighty_table_ndx, "header") == LUA_TTABLE) 2466 magnet_clear_table(L, -1); /*lighty.header*/ 2467 else { 2468 lua_createtable(L, 0, 0); 2469 lua_setfield(L, lighty_table_ndx, "header"); 2470 } 2471 2472 /* pop two tables (or nils) added by two lua_getfield_and_type() calls */ 2473 /*lua_pop(L, 2);*/ /* defer to later */ 2474 } 2475 2476 static int magnet_traceback(lua_State *L) { 2477 if (!lua_isstring(L, 1)) /* 'message' not a string? */ 2478 return 1; /* keep it intact */ 2479 if (lua_getglobal_and_type(L, "debug") != LUA_TTABLE) { 2480 lua_pop(L, 1); 2481 return 1; 2482 } 2483 if (lua_getfield_and_type(L, -1, "traceback") != LUA_TFUNCTION) { 2484 lua_pop(L, 2); 2485 return 1; 2486 } 2487 lua_pushvalue(L, 1); /* pass error message */ 2488 lua_pushinteger(L, 2); /* skip this function and traceback */ 2489 lua_call(L, 2, 1); /* call debug.traceback */ 2490 return 1; 2491 } 2492 2493 static handler_t magnet_attract(request_st * const r, plugin_data * const p, script * const sc) { 2494 lua_State * const L = sc->L; 2495 const int func_ndx = 1; 2496 const int errfunc_ndx = 2; 2497 const int env_ndx = 3; 2498 const int lighty_table_ndx = 4; 2499 2500 if (__builtin_expect( (lua_gettop(L) == 4), 1)) { 2501 /*force_assert(lua_istable(L, -1));*//* lighty.* table */ 2502 } 2503 else if (lua_isfunction(L, func_ndx)) { 2504 /*force_assert(lua_gettop(L) == 1);*/ 2505 lua_pushcfunction(L, magnet_traceback);/*errfunc*//* (sp += 1) */ 2506 /* create empty table for script environment (reused) 2507 * setmetatable({}, {__index = _G}) 2508 * if a function symbol is not defined in our env, 2509 * __index will lookup in the global env. */ 2510 lua_createtable(L, 0, 1); /* (sp += 1) */ 2511 magnet_mainenv_metatable(L); /* (sp += 1) */ 2512 lua_setmetatable(L, -2); /* (sp -= 1) */ 2513 /* insert lighty table at index 4 (lighty_table_ndx = 4) */ 2514 magnet_init_lighty_table(L); /* lighty.* (sp += 1) */ 2515 } 2516 else { 2517 if (lua_isstring(L, func_ndx)) 2518 log_error(r->conf.errh, __FILE__, __LINE__, 2519 "loading script %s failed: %s", sc->name.ptr, 2520 lua_tostring(L, func_ndx)); 2521 else /*(lua_gettop(L) == 0)*/ 2522 log_perror(r->conf.errh, __FILE__, __LINE__, 2523 "loading script %s failed", sc->name.ptr); 2524 lua_settop(L, 0); 2525 2526 if (p->conf.stage != -1) { /* skip for response-start */ 2527 r->http_status = 500; 2528 r->handler_module = NULL; 2529 } 2530 2531 return HANDLER_FINISHED; 2532 } 2533 2534 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 2535 lua_pushlightuserdata(L, r); 2536 lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_REQUEST); 2537 #else 2538 *(request_st **)lua_getextraspace(L) = r; 2539 #endif 2540 2541 /** 2542 * all variables created in the script-env will be thrown 2543 * away at the end of the script run. 2544 */ 2545 lua_pushvalue(L, lighty_table_ndx); /* (sp += 1) */ 2546 lua_setfield(L, env_ndx, "lighty"); /* lighty.* (sp -= 1) */ 2547 lua_pushvalue(L, env_ndx); /* (sp += 1) */ 2548 magnet_setfenv_mainfn(L, 1); /* (sp -= 1) */ 2549 2550 /* pcall will destroy the func value, duplicate it */ 2551 lua_pushvalue(L, func_ndx); /* (sp += 1) */ 2552 int ret = lua_pcall(L, 0, 1, errfunc_ndx); /* (sp -= 1; sp += 1) */ 2553 2554 /* reset environment */ 2555 lua_pushglobaltable(L); /* (sp += 1) */ 2556 magnet_setfenv_mainfn(L, 1); /* (sp -= 1) */ 2557 2558 handler_t result = HANDLER_GO_ON; 2559 if (0 != ret) { 2560 log_error(r->conf.errh, __FILE__, __LINE__, 2561 "lua_pcall(): %s", lua_tostring(L, -1)); 2562 /*lua_pop(L, 1);*/ /* pop error msg */ /* defer to later */ 2563 /* only func, errfunc, env, lighty table should remain on stack */ 2564 /*force_assert(lua_gettop(L) == 4);*//* lua_pop() deferred, so +1 */ 2565 2566 if (p->conf.stage != -1) { /* skip for response-start */ 2567 r->http_status = 500; 2568 r->handler_module = NULL; 2569 } 2570 2571 result = HANDLER_FINISHED; 2572 } 2573 else { 2574 /* should have func, errfunc, env, lighty table, return value on stack*/ 2575 /*force_assert(lua_gettop(L) == 5);*/ 2576 2577 /*(luaL_optinteger might raise error, which we want to avoid)*/ 2578 /*lua_return_value = (int) luaL_optinteger(L, -1, -1);*/ 2579 int isnum = 1; 2580 int lua_return_value = lua_isnil(L, -1) 2581 ? 0 2582 : (int) lua_tointegerx(L, -1, &isnum); 2583 if (!isnum) { 2584 lua_return_value = -1; 2585 log_error(r->conf.errh, __FILE__, __LINE__, 2586 "lua_pcall(): unexpected non-integer return type: %s", 2587 luaL_typename(L, -1)); 2588 } 2589 /*lua_pop(L, 1);*/ /* pop return value */ /* defer to later */ 2590 /*force_assert(lua_istable(sc->L, -1));*/ 2591 2592 magnet_copy_response_header(L, r, lighty_table_ndx); 2593 2594 if (lua_return_value >= 200) { 2595 r->http_status = lua_return_value; 2596 r->resp_body_finished = 1; 2597 /*(note: body may already have been set via lighty.r.resp_body.*)*/ 2598 magnet_attach_content(L, r, lighty_table_ndx); 2599 if (!chunkqueue_is_empty(&r->write_queue)) { 2600 r->handler_module = p->self; 2601 } 2602 result = HANDLER_FINISHED; 2603 } else if (lua_return_value >= 100 && p->conf.stage != -1) { 2604 /*(skip for response-start; send response as-is w/ added headers)*/ 2605 /*(custom lua code should not return 101 Switching Protocols)*/ 2606 r->http_status = lua_return_value; 2607 result = http_response_send_1xx(r) 2608 ? HANDLER_GO_ON 2609 : HANDLER_ERROR; 2610 } else if (MAGNET_RESTART_REQUEST == lua_return_value) { 2611 /*(could detect restart loops in same way as is done in mod_rewrite, 2612 * but using r->env means that we do not need to reset plugin state 2613 * at end of every request, as is done in mod_rewrite. mod_rewrite 2614 * always restarts the request processing (if request is rewritten), 2615 * whereas mod_magnet can be used in many other ways)*/ 2616 buffer *vb = 2617 http_header_env_get(r, CONST_STR_LEN("_L_MAGNET_RESTART")); 2618 if (NULL == vb) { 2619 vb = 2620 http_header_env_set_ptr(r,CONST_STR_LEN("_L_MAGNET_RESTART")); 2621 buffer_append_string_len(vb, "0", 1); 2622 } 2623 result = HANDLER_COMEBACK; 2624 if (++*vb->ptr-'0' >= 10) { 2625 log_error(r->conf.errh, __FILE__, __LINE__, 2626 "too many request restarts (infinite loop?) for %s", 2627 sc->name.ptr); 2628 result = HANDLER_ERROR; 2629 } 2630 } 2631 2632 } 2633 magnet_clear_table(L, env_ndx); 2634 magnet_reset_lighty_table(L, lighty_table_ndx); 2635 /* reset stack to have only func, errfunc, env, lighty table */ 2636 lua_settop(L, lighty_table_ndx); /*(handle deferred lua_pop()s)*/ 2637 return result; 2638 } 2639 2640 static handler_t magnet_attract_array(request_st * const r, plugin_data * const p, int stage) { 2641 mod_magnet_patch_config(r, p); 2642 p->conf.stage = stage; 2643 2644 script * const *scripts; 2645 switch (stage) { 2646 case 1: scripts = p->conf.url_raw; break; 2647 case 0: scripts = p->conf.physical_path; break; 2648 case -1: scripts = p->conf.response_start; break; 2649 default: scripts = NULL; break; 2650 } 2651 if (NULL == scripts) return HANDLER_GO_ON; /* no scripts set */ 2652 2653 /*(always check at least mtime and size to trigger script reload)*/ 2654 const int etag_flags = r->conf.etag_flags | ETAG_USE_MTIME | ETAG_USE_SIZE; 2655 int req_env_inited = 0; 2656 2657 /* execute scripts sequentially while HANDLER_GO_ON */ 2658 handler_t rc = HANDLER_GO_ON; 2659 do { 2660 script_cache_check_script(*scripts, etag_flags); 2661 if ((*scripts)->req_env_init && !req_env_inited) { 2662 /*(request env init is deferred until needed)*/ 2663 req_env_inited = 1; 2664 r->con->srv->request_env(r); 2665 } 2666 rc = magnet_attract(r, p, *scripts); 2667 } while (rc == HANDLER_GO_ON && *++scripts); 2668 2669 if (r->error_handler_saved_status) { 2670 /* retrieve (possibly modified) REDIRECT_STATUS and store as number */ 2671 int x; 2672 const buffer * const vb = http_header_env_get(r, CONST_STR_LEN("REDIRECT_STATUS")); 2673 if (vb && (x = http_header_str_to_code(vb->ptr)) != -1) 2674 r->error_handler_saved_status = 2675 r->error_handler_saved_status > 0 ? (int)x : -(int)x; 2676 } 2677 2678 return rc; 2679 } 2680 2681 URIHANDLER_FUNC(mod_magnet_uri_handler) { 2682 return magnet_attract_array(r, p_d, 1); 2683 } 2684 2685 URIHANDLER_FUNC(mod_magnet_physical) { 2686 return magnet_attract_array(r, p_d, 0); 2687 } 2688 2689 URIHANDLER_FUNC(mod_magnet_response_start) { 2690 return magnet_attract_array(r, p_d, -1); 2691 } 2692 2693 2694 int mod_magnet_plugin_init(plugin *p); 2695 int mod_magnet_plugin_init(plugin *p) { 2696 p->version = LIGHTTPD_VERSION_ID; 2697 p->name = "magnet"; 2698 2699 p->init = mod_magnet_init; 2700 p->handle_uri_clean = mod_magnet_uri_handler; 2701 p->handle_physical = mod_magnet_physical; 2702 p->handle_response_start = mod_magnet_response_start; 2703 p->set_defaults = mod_magnet_set_defaults; 2704 p->cleanup = mod_magnet_free; 2705 2706 return 0; 2707 } 2708