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 "fdevent.h" 13 #include "h2.h" 14 #include "http_chunk.h" 15 #include "http_etag.h" 16 #include "http_header.h" 17 #include "rand.h" 18 #include "response.h" /* http_response_send_1xx() */ 19 20 #include "plugin.h" 21 22 #include "mod_magnet_cache.h" 23 #include "sock_addr.h" 24 #include "stat_cache.h" 25 26 #include <dirent.h> 27 #include <stdlib.h> 28 #include <string.h> 29 /*#include <setjmp.h>*//*(not currently used)*/ 30 31 #include <lua.h> 32 #include <lauxlib.h> 33 34 #define MAGNET_RESTART_REQUEST 99 35 36 /* plugin config for all request/connections */ 37 38 /*static jmp_buf exceptionjmp;*//*(not currently used)*/ 39 40 typedef struct { 41 script * const *url_raw; 42 script * const *physical_path; 43 script * const *response_start; 44 int stage; 45 } plugin_config; 46 47 typedef struct { 48 PLUGIN_DATA; 49 plugin_config defaults; 50 plugin_config conf; 51 52 script_cache cache; 53 } plugin_data; 54 55 static plugin_data *plugin_data_singleton; 56 57 INIT_FUNC(mod_magnet_init) { 58 plugin_data_singleton = (plugin_data *)ck_calloc(1, sizeof(plugin_data)); 59 return plugin_data_singleton; 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 ck_malloc((cpv->v.a->used+1)*sizeof(script *)); 153 for (uint32_t j = 0; j < cpv->v.a->used; ++j) { 154 data_string *ds = (data_string *)cpv->v.a->data[j]; 155 if (buffer_is_blank(&ds->value)) { 156 log_error(srv->errh, __FILE__, __LINE__, 157 "unexpected (blank) value for %s; " 158 "expected list of \"scriptpath\"", cpk[cpv->k_id].k); 159 free(a); 160 return HANDLER_ERROR; 161 } 162 a[j] = script_cache_get_script(&p->cache, &ds->value); 163 } 164 a[cpv->v.a->used] = NULL; 165 cpv->v.v = a; 166 cpv->vtype = T_CONFIG_LOCAL; 167 } 168 break; 169 default:/* should not happen */ 170 break; 171 } 172 } 173 } 174 175 /* initialize p->defaults from global config context */ 176 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 177 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 178 if (-1 != cpv->k_id) 179 mod_magnet_merge_config(&p->defaults, cpv); 180 } 181 182 return HANDLER_GO_ON; 183 } 184 185 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 504 186 #define lua_newuserdata0(L, sz) lua_newuserdata((L),(sz)) 187 #else 188 #define lua_newuserdata0(L, sz) lua_newuserdatauv((L),(sz),0) 189 #endif 190 191 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 192 #define lua_getfield_and_type(L,idx,k) \ 193 (lua_getfield((L),(idx),(k)), lua_type((L),-1)) 194 #define lua_getglobal_and_type(L,name) \ 195 (lua_getglobal((L),(name)), lua_type((L),-1)) 196 #else 197 #define lua_getfield_and_type(L,idx,k) \ 198 lua_getfield((L),(idx),(k)) 199 #define lua_getglobal_and_type(L,name) \ 200 lua_getglobal((L),(name)) 201 #endif 202 203 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 204 static lua_Integer 205 lua_tointegerx (lua_State * const L, int idx, int *isnum) 206 { 207 /*(caller should check for LUA_TNIL if using a default value is desired)*/ 208 /*(note: return 0 for floating point not convertible to integer)*/ 209 *isnum = lua_isnumber(L, idx); 210 return *isnum ? lua_tointeger(L, idx) : 0; 211 } 212 #endif 213 214 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 215 /* lua5.1 backward compat definition */ 216 static void lua_pushglobaltable(lua_State *L) { /* (-0, +1, -) */ 217 lua_pushvalue(L, LUA_GLOBALSINDEX); 218 } 219 #endif 220 221 static void magnet_setfenv_mainfn(lua_State *L, int funcIndex) { /* (-1, 0, -) */ 222 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502 223 /* set "_ENV" upvalue, which should be the first upvalue of a "main" lua 224 * function if it uses any global names 225 */ 226 227 const char* first_upvalue_name = lua_getupvalue(L, funcIndex, 1); 228 if (NULL == first_upvalue_name) return; /* doesn't have any upvalues */ 229 lua_pop(L, 1); /* only need the name of the upvalue, not the value */ 230 231 if (0 != strcmp(first_upvalue_name, "_ENV")) return; 232 233 if (NULL == lua_setupvalue(L, funcIndex, 1)) { 234 /* pop value if lua_setupvalue didn't set the (not existing) upvalue */ 235 lua_pop(L, 1); 236 } 237 #else 238 lua_setfenv(L, funcIndex); 239 #endif 240 } 241 242 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 243 /* lua 5.1 deprecated luaL_getn() for lua_objlen() */ 244 /* lua 5.2 renamed lua_objlen() to lua_rawlen() */ 245 #define lua_rawlen lua_objlen 246 /* lua 5.2 deprecated luaL_register() for luaL_setfuncs() 247 * (this define is valid only when 0 == nup) */ 248 #define luaL_setfuncs(L, l, nup) luaL_register((L), NULL, (l)) 249 #endif 250 251 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 252 /* lua 5.2 already supports __pairs */ 253 254 /* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details. 255 * Override the default pairs() function to allow us to use a __pairs metakey 256 */ 257 static int magnet_pairs(lua_State *L) { 258 luaL_checkany(L, 1); /* "self" */ 259 260 if (luaL_getmetafield(L, 1, "__pairs")) { 261 /* call __pairs(self) */ 262 lua_pushvalue(L, 1); 263 lua_call(L, 1, 3); 264 } else { 265 /* call <original-pairs-method>(self) */ 266 lua_pushvalue(L, lua_upvalueindex(1)); 267 lua_pushvalue(L, 1); 268 lua_call(L, 1, 3); 269 } 270 return 3; 271 } 272 #endif 273 274 275 /* XXX: mystery why dir walk (readdir) is not already part of lua io liolib.c */ 276 277 #ifndef _D_EXACT_NAMLEN 278 #ifdef _DIRENT_HAVE_D_NAMLEN 279 #define _D_EXACT_NAMLEN(d) ((d)->d_namlen) 280 #else 281 #define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name)) 282 #endif 283 #endif 284 285 static int magnet_readdir_iter(lua_State *L) { 286 DIR ** const d = (DIR **)lua_touserdata(L, lua_upvalueindex(1)); 287 if (NULL == *d) return 0; 288 289 /* readdir() and skip over "." and ".." */ 290 struct dirent *de; 291 const char *n; 292 do { 293 de = readdir(*d); 294 } while (de && (n = de->d_name)[0] == '.' 295 && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))); 296 297 if (de) { 298 lua_pushlstring(L, de->d_name, _D_EXACT_NAMLEN(de)); 299 return 1; 300 } 301 else { /* EOF */ 302 closedir(*d); 303 *d = NULL; 304 return 0; 305 } 306 } 307 308 static int magnet_readdir_gc(lua_State *L) { 309 /*DIR ** const d = ((DIR **)luaL_checkudata(L, 1, "li.DIR"));*/ 310 DIR ** const d = lua_touserdata(L, 1); 311 if (*d) closedir(*d); 312 return 0; 313 } 314 315 static void magnet_readdir_metatable(lua_State * const L) { 316 if (luaL_newmetatable(L, "li.DIR")) { /* (sp += 1) */ 317 lua_pushcclosure(L, magnet_readdir_gc, 0); /* (sp += 1) */ 318 lua_setfield(L, -2, "__gc"); /* (sp -= 1) */ 319 lua_pushboolean(L, 0); /* (sp += 1) */ 320 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 321 } 322 } 323 324 static int magnet_readdir(lua_State *L) { 325 const char * const s = luaL_checkstring(L, 1); 326 DIR * const d = opendir(s); 327 if (d) { 328 *(DIR **)lua_newuserdata0(L, sizeof(DIR *)) = 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 __attribute_cold__ 340 static int magnet_newindex_readonly(lua_State *L) { 341 lua_pushliteral(L, "lua table is read-only"); 342 return lua_error(L); 343 } 344 345 static void magnet_push_buffer(lua_State *L, const buffer *b) { 346 if (b && !buffer_is_unset(b)) 347 lua_pushlstring(L, BUF_PTR_LEN(b)); 348 else 349 lua_pushnil(L); 350 } 351 352 #if 0 353 static int magnet_array_get_element(lua_State *L, const array *a) { 354 /* __index: param 1 is the (empty) table the value was not found in */ 355 size_t klen; 356 const char * const k = luaL_checklstring(L, 2, &klen); 357 const data_string * const ds = (const data_string *) 358 array_get_element_klen(a, k, klen); 359 magnet_push_buffer(L, NULL != ds ? &ds->value : NULL); 360 return 1; 361 } 362 #endif 363 364 /* Define a function that will iterate over an array* (in upval 2) using current position (upval 1) */ 365 static int magnet_array_next(lua_State *L) { 366 lua_settop(L, 0); 367 const uint32_t pos = lua_tointeger(L, lua_upvalueindex(1)); 368 const array * const a = lua_touserdata(L, lua_upvalueindex(2)); 369 const data_unset * const du = pos < a->used ? a->data[pos] : NULL; 370 if (NULL == du) return 0; 371 372 lua_pushlstring(L, BUF_PTR_LEN(&du->key)); 373 switch (du->type) { 374 case TYPE_STRING: 375 magnet_push_buffer(L, &((const data_string *)du)->value); 376 break; 377 case TYPE_INTEGER: 378 lua_pushinteger(L, ((const data_integer *)du)->value); 379 break; 380 default: 381 lua_pushnil(L); 382 break; 383 } 384 385 /* Update our positional upval to reflect our new current position */ 386 lua_pushinteger(L, pos+1); 387 lua_replace(L, lua_upvalueindex(1)); 388 389 /* Returning 2 items on the stack (key, value) */ 390 return 2; 391 } 392 393 /* Create the closure necessary to iterate over the array *a with the above function */ 394 __attribute_noinline__ 395 static int magnet_array_pairs(lua_State *L, array *a) { 396 lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */ 397 lua_pushlightuserdata(L, a); /* Push our array *a into upval 2 */ 398 lua_pushcclosure(L, magnet_array_next, 2); /* Push our new closure with 2 upvals */ 399 return 1; 400 } 401 402 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 403 #define LUA_RIDX_LIGHTTPD_REQUEST "li.request" 404 #endif 405 406 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 407 __attribute_noinline__ 408 #endif 409 static request_st * magnet_get_request(lua_State *L) { 410 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 411 lua_getfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_REQUEST); 412 request_st * const r = lua_touserdata(L, -1); 413 lua_pop(L, 1); 414 return r; 415 #else 416 return *(request_st **)lua_getextraspace(L); 417 #endif 418 } 419 420 static void magnet_set_request(lua_State *L, request_st * const r) { 421 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 422 lua_pushlightuserdata(L, r); 423 lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_REQUEST); 424 #else 425 *(request_st **)lua_getextraspace(L) = r; 426 #endif 427 } 428 429 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504 430 __attribute_noinline__ 431 #endif 432 static buffer * magnet_tmpbuf_acquire(lua_State *L) 433 { 434 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 435 UNUSED(L); 436 return chunk_buffer_acquire(); 437 #else 438 request_st * const r = magnet_get_request(L); 439 buffer * const tb = r->tmp_buf; 440 buffer_clear(tb); 441 return tb; 442 #endif 443 } 444 445 static void magnet_tmpbuf_release(buffer *b) 446 { 447 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503 448 chunk_buffer_release(b); 449 #else 450 UNUSED(b); 451 #endif 452 } 453 454 typedef struct { 455 const char *ptr; 456 size_t len; 457 } const_buffer; 458 459 __attribute_noinline__ 460 static const_buffer magnet_checkconstbuffer(lua_State *L, int idx) { 461 const_buffer cb; 462 if (!lua_isnoneornil(L, idx)) 463 cb.ptr = luaL_checklstring(L, idx, &cb.len); 464 else { 465 cb.ptr = NULL; 466 cb.len = 0; 467 } 468 return cb; 469 } 470 471 static const buffer* magnet_checkbuffer(lua_State *L, int idx, buffer *b) { 472 const_buffer cb = magnet_checkconstbuffer(L, idx); 473 /* assign result into (buffer *), and return (const buffer *) 474 * (note: caller must not free result) */ 475 *(const char **)&b->ptr = cb.ptr ? cb.ptr : ""; 476 b->used = cb.len+1; 477 b->size = 0; 478 return b; 479 } 480 481 482 static int magnet_return_upvalue2(lua_State *L) { 483 /*(XXX: is there a better way to do this?)*/ 484 lua_pushvalue(L, lua_upvalueindex(1)); 485 lua_pushvalue(L, lua_upvalueindex(2)); 486 return 2; 487 } 488 489 static int magnet_stat_field(lua_State *L) { 490 if (lua_gettop(L) != 2) 491 return 0; /*(should not happen; __index method in protected metatable)*/ 492 493 stat_cache_entry * const sce = *(stat_cache_entry **)lua_touserdata(L, -2); 494 const_buffer k = magnet_checkconstbuffer(L, -1); 495 switch (k.len ? k.ptr[0] : 0) { 496 case 'c': { /* content-type */ 497 if (0 != strcmp(k.ptr, "content-type")) break; 498 request_st * const r = magnet_get_request(L); 499 const buffer *content_type = stat_cache_content_type_get(sce, r); 500 if (content_type && !buffer_is_blank(content_type)) 501 lua_pushlstring(L, BUF_PTR_LEN(content_type)); 502 else 503 lua_pushnil(L); 504 return 1; 505 } 506 case 'e': { /* etag */ 507 if (0 != strcmp(k.ptr, "etag")) break; 508 request_st * const r = magnet_get_request(L); 509 const buffer *etag = stat_cache_etag_get(sce, r->conf.etag_flags); 510 if (etag && !buffer_is_blank(etag)) 511 lua_pushlstring(L, BUF_PTR_LEN(etag)); 512 else 513 lua_pushnil(L); 514 return 1; 515 } 516 case 'h': { /* http-response-send-file */ 517 if (0 != strcmp(k.ptr, "http-response-send-file")) break; 518 request_st * const r = magnet_get_request(L); 519 r->http_status = 0; 520 http_response_body_clear(r, 0); 521 http_response_send_file(r, &sce->name, sce); 522 lua_pushinteger(L, r->http_status); 523 return 1; 524 } 525 case 'i': /* is_* */ 526 if (k.len < 4) break; 527 switch (k.ptr[3]) { 528 case 'b': /* is_block */ 529 if (0 == strcmp(k.ptr, "is_block")) { 530 lua_pushboolean(L, S_ISBLK(sce->st.st_mode)); 531 return 1; 532 } 533 break; 534 case 'c': /* is_char */ 535 if (0 == strcmp(k.ptr, "is_char")) { 536 lua_pushboolean(L, S_ISCHR(sce->st.st_mode)); 537 return 1; 538 } 539 break; 540 case 'd': /* is_dir */ 541 if (0 == strcmp(k.ptr, "is_dir")) { 542 lua_pushboolean(L, S_ISDIR(sce->st.st_mode)); 543 return 1; 544 } 545 break; 546 case 'f': /* is_file is_fifo */ 547 if (0 == strcmp(k.ptr, "is_file")) { 548 lua_pushboolean(L, S_ISREG(sce->st.st_mode)); 549 return 1; 550 } 551 if (0 == strcmp(k.ptr, "is_fifo")) { 552 lua_pushboolean(L, S_ISFIFO(sce->st.st_mode)); 553 return 1; 554 } 555 break; 556 case 'l': /* is_link */ 557 if (0 == strcmp(k.ptr, "is_link")) { 558 lua_pushboolean(L, S_ISLNK(sce->st.st_mode)); 559 return 1; 560 } 561 break; 562 case 's': /* is_socket */ 563 if (0 == strcmp(k.ptr, "is_socket")) { 564 lua_pushboolean(L, S_ISSOCK(sce->st.st_mode)); 565 return 1; 566 } 567 break; 568 default: 569 break; 570 } 571 break; 572 case 's': /* st_* */ 573 if (k.len < 4) break; 574 switch (k.ptr[3]) { 575 case 'a': /* st_atime */ 576 if (0 == strcmp(k.ptr, "st_atime")) { 577 lua_pushinteger(L, TIME64_CAST(sce->st.st_atime)); 578 return 1; 579 } 580 if (0 == strcmp(k.ptr, "st_atim")) { 581 lua_pushinteger(L, TIME64_CAST(sce->st.st_atime)); 582 #ifdef st_atime /* high-precision timestamp if available */ 583 #if defined(__APPLE__) && defined(__MACH__) 584 lua_pushinteger(L, sce->st.st_atimespec.tv_nsec); 585 #else 586 lua_pushinteger(L, sce->st.st_atim.tv_nsec); 587 #endif 588 #else 589 lua_pushinteger(L, 0); 590 #endif 591 lua_pushcclosure(L, magnet_return_upvalue2, 2); 592 return 1; 593 } 594 break; 595 case 'c': /* st_ctime */ 596 if (0 == strcmp(k.ptr, "st_ctime")) { 597 lua_pushinteger(L, TIME64_CAST(sce->st.st_ctime)); 598 return 1; 599 } 600 if (0 == strcmp(k.ptr, "st_ctim")) { 601 lua_pushinteger(L, TIME64_CAST(sce->st.st_ctime)); 602 #ifdef st_ctime /* high-precision timestamp if available */ 603 #if defined(__APPLE__) && defined(__MACH__) 604 lua_pushinteger(L, sce->st.st_ctimespec.tv_nsec); 605 #else 606 lua_pushinteger(L, sce->st.st_ctim.tv_nsec); 607 #endif 608 #else 609 lua_pushinteger(L, 0); 610 #endif 611 lua_pushcclosure(L, magnet_return_upvalue2, 2); 612 return 1; 613 } 614 break; 615 case 'i': /* st_ino */ 616 if (0 == strcmp(k.ptr, "st_ino")) { 617 lua_pushinteger(L, sce->st.st_ino); 618 return 1; 619 } 620 break; 621 case 'm': /* st_mtime st_mode */ 622 if (0 == strcmp(k.ptr, "st_mtime")) { 623 lua_pushinteger(L, TIME64_CAST(sce->st.st_mtime)); 624 return 1; 625 } 626 if (0 == strcmp(k.ptr, "st_mtim")) { 627 lua_pushinteger(L, TIME64_CAST(sce->st.st_mtime)); 628 #ifdef st_mtime /* high-precision timestamp if available */ 629 #if defined(__APPLE__) && defined(__MACH__) 630 lua_pushinteger(L, sce->st.st_mtimespec.tv_nsec); 631 #else 632 lua_pushinteger(L, sce->st.st_mtim.tv_nsec); 633 #endif 634 #else 635 lua_pushinteger(L, 0); 636 #endif 637 lua_pushcclosure(L, magnet_return_upvalue2, 2); 638 return 1; 639 } 640 if (0 == strcmp(k.ptr, "st_mode")) { 641 lua_pushinteger(L, sce->st.st_mode); 642 return 1; 643 } 644 break; 645 case 'g': /* st_gid */ 646 if (0 == strcmp(k.ptr, "st_gid")) { 647 lua_pushinteger(L, sce->st.st_gid); 648 return 1; 649 } 650 break; 651 case 's': /* st_size */ 652 if (0 == strcmp(k.ptr, "st_size")) { 653 lua_pushinteger(L, sce->st.st_size); 654 return 1; 655 } 656 break; 657 case 'u': /* st_uid */ 658 if (0 == strcmp(k.ptr, "st_uid")) { 659 lua_pushinteger(L, sce->st.st_uid); 660 return 1; 661 } 662 break; 663 default: 664 break; 665 } 666 break; 667 default: 668 break; 669 } 670 671 lua_pushliteral(L, "stat[\"field\"] invalid: "); 672 lua_pushvalue(L, -2); /* field */ 673 lua_concat(L, 2); 674 lua_error(L); 675 return 0; 676 } 677 678 679 __attribute_cold__ 680 static int magnet_stat_pairs_noimpl_iter(lua_State *L) { 681 request_st * const r = magnet_get_request(L); 682 log_error(r->conf.errh, __FILE__, __LINE__, 683 "(lua) pairs() not implemented on lighty.stat object; " 684 "returning empty iter"); 685 return 0; 686 } 687 688 689 __attribute_cold__ 690 static int magnet_stat_pairs_noimpl(lua_State *L) { 691 lua_pushcclosure(L, magnet_stat_pairs_noimpl_iter, 0); 692 return 1; 693 } 694 695 696 static void magnet_stat_metatable(lua_State *L) { 697 if (luaL_newmetatable(L, "li.stat")) { /* (sp += 1) */ 698 lua_pushcfunction(L, magnet_stat_field); /* (sp += 1) */ 699 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 700 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 701 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 702 lua_pushcfunction(L, magnet_stat_pairs_noimpl); /* (sp += 1) */ 703 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 704 lua_pushboolean(L, 0); /* (sp += 1) */ 705 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 706 } 707 } 708 709 710 static int magnet_stat(lua_State *L) { 711 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/ 712 const buffer * const sb = magnet_checkbuffer(L, 1, &stor); 713 stat_cache_entry * const sce = (!buffer_is_blank(sb)) 714 ? stat_cache_get_entry(sb) 715 : NULL; 716 if (NULL == sce) { 717 lua_pushnil(L); 718 return 1; 719 } 720 721 /* note: caching sce valid only for procedural script which does not yield; 722 * (sce might not be valid if script yields and is later resumed) 723 * (script must not cache sce in persistent global state for later use) 724 * (If we did want sce to be persistent, then could increment sce refcnt, 725 * and set up __gc metatable method to decrement sce refcnt) */ 726 stat_cache_entry ** const udata =(struct stat_cache_entry**)/* (sp += 1) */ 727 lua_newuserdata0(L, sizeof(stat_cache_entry *)); 728 *udata = sce; 729 730 magnet_stat_metatable(L); /* (sp += 1) */ 731 lua_setmetatable(L, -2); /* (sp -= 1) */ 732 return 1; 733 } 734 735 736 static int magnet_time(lua_State *L) { 737 lua_pushinteger(L, (lua_Integer)log_epoch_secs); 738 return 1; 739 } 740 741 742 static int magnet_hrtime(lua_State *L) { 743 unix_timespec64_t ts; 744 if (0 != log_clock_gettime_realtime(&ts)) 745 return 0; 746 lua_pushinteger(L, (lua_Integer)ts.tv_sec); 747 lua_pushinteger(L, (lua_Integer)ts.tv_nsec); 748 return 2; 749 } 750 751 752 static int magnet_rand(lua_State *L) { 753 lua_pushinteger(L, (lua_Integer)li_rand_pseudo()); 754 return 1; 755 } 756 757 758 static int magnet_md_once(lua_State *L) { 759 if (lua_gettop(L) != 2) { 760 lua_pushliteral(L, 761 "lighty.c.md(algo, data): incorrect number of arguments"); 762 return lua_error(L); 763 } 764 const_buffer algo = magnet_checkconstbuffer(L, -2); 765 const_buffer msg = magnet_checkconstbuffer(L, -1); 766 uint8_t digest[MD_DIGEST_LENGTH_MAX]; 767 uint32_t dlen = 0; 768 switch (algo.len) { 769 #ifdef USE_LIB_CRYPTO 770 case 6: 771 #ifdef USE_LIB_CRYPTO_SHA512 772 if (0 == memcmp(algo.ptr, "sha512", 6)) { 773 SHA512_once(digest, msg.ptr, msg.len); 774 dlen = SHA512_DIGEST_LENGTH; 775 break; 776 } 777 #endif 778 #ifdef USE_LIB_CRYPTO_SHA256 779 if (0 == memcmp(algo.ptr, "sha256", 6)) { 780 SHA256_once(digest, msg.ptr, msg.len); 781 dlen = SHA256_DIGEST_LENGTH; 782 break; 783 } 784 #endif 785 break; 786 case 4: 787 #ifdef USE_LIB_CRYPTO_SHA1 788 if (0 == memcmp(algo.ptr, "sha1", 4)) { 789 SHA1_once(digest, msg.ptr, msg.len); 790 dlen = SHA1_DIGEST_LENGTH; 791 break; 792 } 793 #endif 794 break; 795 #endif 796 case 3: 797 if (0 == memcmp(algo.ptr, "md5", 3)) { 798 MD5_once(digest, msg.ptr, msg.len); 799 dlen = MD5_DIGEST_LENGTH; 800 break; 801 } 802 break; 803 default: 804 break; 805 } 806 807 if (dlen) { 808 char dighex[MD_DIGEST_LENGTH_MAX*2+1]; 809 li_tohex_uc(dighex, sizeof(dighex), (char *)digest, dlen); 810 lua_pushlstring(L, dighex, dlen*2); 811 } 812 else 813 lua_pushnil(L); 814 815 return 1; 816 } 817 818 static int magnet_hmac_once(lua_State *L) { 819 if (lua_gettop(L) != 3) { 820 lua_pushliteral(L, 821 "lighty.c.hmac(algo, secret, data): incorrect number of arguments"); 822 return lua_error(L); 823 } 824 const_buffer algo = magnet_checkconstbuffer(L, -3); 825 const_buffer secret = magnet_checkconstbuffer(L, -2); 826 const_buffer msg = magnet_checkconstbuffer(L, -1); 827 const uint8_t * const msgptr = (uint8_t *)msg.ptr; 828 uint8_t digest[MD_DIGEST_LENGTH_MAX]; 829 uint32_t dlen = 0; 830 int rc = 0; 831 switch (algo.len) { 832 #ifdef USE_LIB_CRYPTO 833 case 6: 834 #ifdef USE_LIB_CRYPTO_SHA512 835 if (0 == memcmp(algo.ptr, "sha512", 6)) { 836 rc = li_hmac_sha512(digest,secret.ptr,secret.len,msgptr,msg.len); 837 dlen = SHA512_DIGEST_LENGTH; 838 break; 839 } 840 #endif 841 #ifdef USE_LIB_CRYPTO_SHA256 842 if (0 == memcmp(algo.ptr, "sha256", 6)) { 843 rc = li_hmac_sha256(digest,secret.ptr,secret.len,msgptr,msg.len); 844 dlen = SHA256_DIGEST_LENGTH; 845 break; 846 } 847 #endif 848 break; 849 case 4: 850 #ifdef USE_LIB_CRYPTO_SHA1 851 if (0 == memcmp(algo.ptr, "sha1", 4)) { 852 rc = li_hmac_sha1(digest,secret.ptr,secret.len,msgptr,msg.len); 853 dlen = SHA1_DIGEST_LENGTH; 854 break; 855 } 856 #endif 857 break; 858 #endif 859 case 3: 860 if (0 == memcmp(algo.ptr, "md5", 3)) { 861 rc = li_hmac_md5(digest,secret.ptr,secret.len,msgptr,msg.len); 862 dlen = MD5_DIGEST_LENGTH; 863 break; 864 } 865 break; 866 default: 867 break; 868 } 869 870 if (rc) { 871 char dighex[MD_DIGEST_LENGTH_MAX*2+1]; 872 li_tohex_uc(dighex, sizeof(dighex), (char *)digest, dlen); 873 lua_pushlstring(L, dighex, dlen*2); 874 } 875 else 876 lua_pushnil(L); 877 878 return 1; 879 } 880 881 static int magnet_digest_eq(lua_State *L) { 882 if (lua_gettop(L) != 2) { 883 lua_pushliteral(L, 884 "lighty.c.digest_eq(d1, d2): incorrect number of arguments"); 885 return lua_error(L); 886 } 887 const_buffer d1 = magnet_checkconstbuffer(L, -2); 888 const_buffer d2 = magnet_checkconstbuffer(L, -1); 889 /* convert hex to binary: validate hex and eliminate hex case comparison */ 890 uint8_t b1[MD_DIGEST_LENGTH_MAX]; 891 uint8_t b2[MD_DIGEST_LENGTH_MAX]; 892 int rc = (d1.len == d2.len) 893 && 0 == li_hex2bin(b1, sizeof(b1), d1.ptr, d1.len) 894 && 0 == li_hex2bin(b2, sizeof(b2), d2.ptr, d2.len) 895 && ck_memeq_const_time_fixed_len(b1, b2, d2.len >> 1); 896 lua_pushboolean(L, rc); 897 return 1; 898 } 899 900 static int magnet_secret_eq(lua_State *L) { 901 if (lua_gettop(L) != 2) { 902 lua_pushliteral(L, 903 "lighty.c.secret_eq(d1, d2): incorrect number of arguments"); 904 return lua_error(L); 905 } 906 const_buffer d1 = magnet_checkconstbuffer(L, -2); 907 const_buffer d2 = magnet_checkconstbuffer(L, -1); 908 lua_pushboolean(L, ck_memeq_const_time(d1.ptr, d1.len, d2.ptr, d2.len)); 909 return 1; 910 } 911 912 static int magnet_b64dec(lua_State *L, base64_charset dict) { 913 if (lua_isnoneornil(L, -1)) { 914 lua_pushlstring(L, "", 0); 915 return 1; 916 } 917 const_buffer s = magnet_checkconstbuffer(L, -1); 918 if (0 == s.len) { 919 lua_pushvalue(L, -1); 920 return 1; 921 } 922 buffer * const b = magnet_tmpbuf_acquire(L); 923 if (buffer_append_base64_decode(b, s.ptr, s.len, dict)) 924 lua_pushlstring(L, BUF_PTR_LEN(b)); 925 else 926 lua_pushnil(L); 927 magnet_tmpbuf_release(b); 928 return 1; 929 } 930 931 static int magnet_b64enc(lua_State *L, base64_charset dict) { 932 if (lua_isnoneornil(L, -1)) { 933 lua_pushlstring(L, "", 0); 934 return 1; 935 } 936 const_buffer s = magnet_checkconstbuffer(L, -1); 937 if (0 == s.len) { 938 lua_pushvalue(L, -1); 939 return 1; 940 } 941 buffer * const b = magnet_tmpbuf_acquire(L); 942 buffer_append_base64_encode_no_padding(b, (uint8_t *)s.ptr, s.len, dict); 943 lua_pushlstring(L, BUF_PTR_LEN(b)); 944 magnet_tmpbuf_release(b); 945 return 1; 946 } 947 948 static int magnet_b64urldec(lua_State *L) { 949 return magnet_b64dec(L, BASE64_URL); 950 } 951 952 static int magnet_b64urlenc(lua_State *L) { 953 return magnet_b64enc(L, BASE64_URL); 954 } 955 956 static int magnet_b64stddec(lua_State *L) { 957 return magnet_b64dec(L, BASE64_STANDARD); 958 } 959 960 static int magnet_b64stdenc(lua_State *L) { 961 return magnet_b64enc(L, BASE64_STANDARD); 962 } 963 964 static int magnet_hexdec(lua_State *L) { 965 if (lua_isnoneornil(L, -1)) { 966 lua_pushlstring(L, "", 0); 967 return 1; 968 } 969 const_buffer s = magnet_checkconstbuffer(L, -1); 970 if (0 == s.len) { 971 lua_pushvalue(L, -1); 972 return 1; 973 } 974 buffer * const b = magnet_tmpbuf_acquire(L); 975 uint8_t * const p = (uint8_t *)buffer_extend(b, s.len >> 1); 976 int rc = li_hex2bin(p, s.len >> 1, s.ptr, s.len); 977 if (0 == rc) 978 lua_pushlstring(L, BUF_PTR_LEN(b)); 979 magnet_tmpbuf_release(b); 980 return rc+1; /* 1 on success (pushed string); 0 on failure (no value) */ 981 } 982 983 static int magnet_hexenc(lua_State *L) { 984 if (lua_isnoneornil(L, -1)) { 985 lua_pushlstring(L, "", 0); 986 return 1; 987 } 988 const_buffer s = magnet_checkconstbuffer(L, -1); 989 if (0 == s.len) { 990 lua_pushvalue(L, -1); 991 return 1; 992 } 993 buffer * const b = magnet_tmpbuf_acquire(L); 994 buffer_append_string_encoded_hex_uc(b, s.ptr, s.len); 995 lua_pushlstring(L, BUF_PTR_LEN(b)); 996 magnet_tmpbuf_release(b); 997 return 1; /* uppercase hex string; use lua s = s:lower() to lowercase */ 998 } 999 1000 static int magnet_quoteddec(lua_State *L) { 1001 if (lua_isnoneornil(L, -1)) { 1002 lua_pushlstring(L, "", 0); 1003 return 1; 1004 } 1005 const_buffer s = magnet_checkconstbuffer(L, -1); 1006 if (0 == s.len || s.ptr[0] != '"') { 1007 lua_pushvalue(L, -1); 1008 return 1; 1009 } 1010 buffer * const b = magnet_tmpbuf_acquire(L); 1011 char *p = buffer_string_prepare_append(b, s.len);/*(s.len-1 is sufficient)*/ 1012 size_t i = 1; 1013 for (; i < s.len && s.ptr[i] != '"'; ++i) { 1014 if (s.ptr[i] == '\\') { 1015 if (i+2 < s.len) 1016 ++i; 1017 else 1018 break; 1019 } 1020 *p++ = s.ptr[i]; 1021 } 1022 int rc = (i == s.len-1 && s.ptr[i] == '"'); 1023 if (rc) 1024 lua_pushlstring(L, b->ptr, (size_t)(p - b->ptr)); 1025 magnet_tmpbuf_release(b); 1026 return rc; /* 1 on success (pushed string); 0 on failure (no value) */ 1027 } 1028 1029 static int magnet_quotedenc(lua_State *L) { 1030 if (lua_isnoneornil(L, -1)) { 1031 lua_pushlstring(L, "", 0); 1032 return 1; 1033 } 1034 const_buffer s = magnet_checkconstbuffer(L, -1); 1035 if (0 == s.len) { 1036 lua_pushvalue(L, -1); 1037 return 1; 1038 } 1039 buffer * const b = magnet_tmpbuf_acquire(L); 1040 char *p = buffer_string_prepare_append(b, 2+(s.len << 1)); 1041 *p++ = '"'; 1042 for (size_t i = 0; i < s.len; ++i) { 1043 /*(note: not strictly checking for TEXT)*/ 1044 /*(TEXT: any OCTET except CTLs but including LWS)*/ 1045 if (s.ptr[i] == '"' || s.ptr[i] == '\\') 1046 *p++ = '\\'; 1047 *p++ = s.ptr[i]; 1048 } 1049 *p++ = '"'; 1050 lua_pushlstring(L, b->ptr, (size_t)(p - b->ptr)); 1051 magnet_tmpbuf_release(b); 1052 return 1; 1053 } 1054 1055 /*(future: might move to buffer.c:buffer_append_bs_unescaped())*/ 1056 static void 1057 magnet_buffer_append_bsdec (buffer * const restrict b, 1058 const char * restrict s, const size_t len) 1059 { 1060 /* decode backslash escapes */ 1061 /*(caller must check result for decoded '\0', if necessary)*/ 1062 char *d = buffer_string_prepare_append(b, len); /*(upper-bound len)*/ 1063 for (const char * const end = s+len; s < end; ++s) { 1064 const char * const ptr = s; 1065 while (__builtin_expect( (*s != '\\'), 1) && ++s < end) ; 1066 if (s - ptr) { 1067 memcpy(d, ptr, (size_t)(s - ptr)); 1068 d += (size_t)(s - ptr); 1069 } 1070 1071 if (s == end) 1072 break; 1073 1074 int c; 1075 switch ((c = ++s != end ? *s : '\\')) { /*(preserve stray '\\' at end)*/ 1076 case '"': case '\\': 1077 default: 1078 break; 1079 case 'x': 1080 if (s+3 <= end) { 1081 unsigned char hi = hex2int(((unsigned char *)s)[1]); 1082 unsigned char lo = hex2int(((unsigned char *)s)[2]); 1083 if (0xFF != hi && 0xFF != lo) { 1084 c = (hi << 4) | lo; 1085 s += 2; 1086 } 1087 } 1088 break; 1089 case 'a':case 'b':case 't':case 'n':case 'v':case 'f':case 'r': 1090 c = "\a\bcde\fghijklm\nopq\rstu\vwxyz"[c-'a']; 1091 break; 1092 case 'u': 1093 if (s+5 <= end) { 1094 unsigned char hi = hex2int(((unsigned char *)s)[3]); 1095 unsigned char lo = hex2int(((unsigned char *)s)[4]); 1096 if (0xFF == hi || 0xFF == lo) 1097 break; 1098 c = (hi << 4) | lo; 1099 if (__builtin_expect( (s[1] != '0'), 0) 1100 || __builtin_expect( (s[2] != '0'), 0)) { 1101 unsigned char hhi = hex2int(((unsigned char *)s)[1]); 1102 unsigned char hlo = hex2int(((unsigned char *)s)[2]); 1103 if (0xFF == hhi || 0xFF == hlo) 1104 break; 1105 c |= (int)((hhi << 12) | (hlo << 8)); 1106 if ((unsigned int)c - 0xd800u < 0x800) 1107 break; /* 0xD800 - 0xDFFF ill-formed UTF-8 */ 1108 } 1109 /* adapted from 1110 * https://stackoverflow.com/questions/4607413/is-there-a-c-library-to-convert-unicode-code-points-to-utf-8 */ 1111 if (__builtin_expect( (c > 0x7F), 0)) { 1112 if (c < 0x800) 1113 *d++ = 0xC0 | (c >> 6); 1114 else { 1115 *d++ = 0xE0 | (c >> 12); 1116 *d++ = 0x80 | ((c >> 6) & 0x3F); 1117 } 1118 c = 0x80 | (c & 0x3F); 1119 } 1120 s += 4; 1121 } 1122 break; 1123 case '0': case '1': case '2': case '3': 1124 if (s+3 <= end 1125 /*&& ((unsigned char *)s)[0] - '0' < 4*//* 0-3 */ 1126 && ((unsigned char *)s)[1] - '0' < 8 /* 0-7 */ 1127 && ((unsigned char *)s)[2] - '0' < 8) { /* 0-7 */ 1128 c = ((s[0]-'0') << 6) | ((s[1]-'0') << 3) | (s[2]-'0'); 1129 s += 2; 1130 } 1131 else if (*s == '0') 1132 c = '\0'; /*(special-case "\\0" not part of octal "\\ooo")*/ 1133 break; 1134 } 1135 *d++ = c; 1136 } 1137 buffer_truncate(b, d - b->ptr); 1138 } 1139 1140 static int magnet_bsdec(lua_State *L) { 1141 if (lua_isnoneornil(L, -1)) { 1142 lua_pushlstring(L, "", 0); 1143 return 1; 1144 } 1145 const_buffer s = magnet_checkconstbuffer(L, -1); 1146 if (0 == s.len) { 1147 lua_pushvalue(L, -1); 1148 return 1; 1149 } 1150 const char *ptr = s.ptr; 1151 size_t len = s.len; 1152 if (ptr[0] == '"' && ptr[len-1] == '"') { 1153 /*(ignore double-quotes ('"') surrounding string for convenience)*/ 1154 ++ptr; 1155 len -= 2; 1156 } 1157 buffer * const b = magnet_tmpbuf_acquire(L); 1158 magnet_buffer_append_bsdec(b, ptr, len); 1159 lua_pushlstring(L, BUF_PTR_LEN(b)); 1160 magnet_tmpbuf_release(b); 1161 return 1; 1162 } 1163 1164 static int magnet_bsenc(lua_State *L, const int esc_json) { 1165 if (lua_isnoneornil(L, -1)) { 1166 lua_pushlstring(L, "", 0); 1167 return 1; 1168 } 1169 const_buffer s = magnet_checkconstbuffer(L, -1); 1170 if (0 == s.len) { 1171 lua_pushvalue(L, -1); 1172 return 1; 1173 } 1174 buffer * const b = magnet_tmpbuf_acquire(L); 1175 if (esc_json) 1176 buffer_append_bs_escaped(b, s.ptr, s.len); 1177 else 1178 buffer_append_bs_escaped_json(b, s.ptr, s.len); 1179 lua_pushlstring(L, BUF_PTR_LEN(b)); 1180 magnet_tmpbuf_release(b); 1181 return 1; 1182 } 1183 1184 static int magnet_bsenc_default(lua_State *L) { 1185 return magnet_bsenc(L, 0); 1186 } 1187 1188 static int magnet_bsenc_json(lua_State *L) { 1189 return magnet_bsenc(L, 1); 1190 } 1191 1192 static int magnet_xmlenc(lua_State *L) { 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 #if 1 1204 buffer_append_string_encoded(b, s.ptr, s.len, ENCODING_MINIMAL_XML); 1205 #else 1206 const char *e; 1207 size_t i, n, elen; 1208 for (i = 0, n = 0; i < s.len; ++i) { 1209 switch (s.ptr[i]) { 1210 default: continue; 1211 case '<': e = "<"; elen = sizeof("<")-1; break; 1212 case '>': e = ">"; elen = sizeof(">")-1; break; 1213 case '&': e = "&"; elen = sizeof("&")-1; break; 1214 case '\'': e = "'"; elen = sizeof("'")-1; break; 1215 case '"': e = """; elen = sizeof(""")-1; break; 1216 /*(XXX: would be good idea to add CTRLs, DEL, '`' */ 1217 } 1218 buffer_append_str2(b, s.ptr+n, i-n, e, elen); 1219 n = i+1; 1220 } 1221 if (i-n) 1222 buffer_append_string_len(b, s.ptr+n, i-n); 1223 #endif 1224 lua_pushlstring(L, BUF_PTR_LEN(b)); 1225 magnet_tmpbuf_release(b); 1226 return 1; 1227 } 1228 1229 static int magnet_urldec(lua_State *L) { 1230 /* url-decode and replace non-printable chars with '_' 1231 * This function should not be used on query-string unless it is used on 1232 * portions of query-string after splitting on '&', replacing '+' w/ ' ' */ 1233 if (lua_isnoneornil(L, -1)) { 1234 lua_pushlstring(L, "", 0); 1235 return 1; 1236 } 1237 const_buffer s = magnet_checkconstbuffer(L, -1); 1238 if (0 == s.len) { 1239 lua_pushvalue(L, -1); 1240 return 1; 1241 } 1242 buffer * const b = magnet_tmpbuf_acquire(L); 1243 buffer_copy_string_len(b, s.ptr, s.len); 1244 buffer_urldecode_path(b); 1245 lua_pushlstring(L, BUF_PTR_LEN(b)); 1246 magnet_tmpbuf_release(b); 1247 return 1; 1248 } 1249 1250 static int magnet_urlenc(lua_State *L) { 1251 /* url-encode path 1252 * ('?' is encoded, if present) 1253 * caller must split string if '?' is part of query-string) 1254 * ('/' is not encoded; caller must encode if not path separator) */ 1255 if (lua_isnoneornil(L, -1)) { 1256 lua_pushlstring(L, "", 0); 1257 return 1; 1258 } 1259 const_buffer s = magnet_checkconstbuffer(L, -1); 1260 if (0 == s.len) { 1261 lua_pushvalue(L, -1); 1262 return 1; 1263 } 1264 buffer * const b = magnet_tmpbuf_acquire(L); 1265 buffer_append_string_encoded(b, s.ptr, s.len, ENCODING_REL_URI); 1266 lua_pushlstring(L, BUF_PTR_LEN(b)); 1267 magnet_tmpbuf_release(b); 1268 return 1; 1269 } 1270 1271 static void magnet_urldec_query_part(buffer * const b, const char *s, const size_t slen) { 1272 buffer_clear(b); 1273 char *p = buffer_extend(b, slen); 1274 for (size_t i = 0; i < slen; ++i) 1275 p[i] = (s[i] != '+') ? s[i] : ' '; 1276 buffer_urldecode_path(b); 1277 } 1278 1279 static int magnet_urldec_query(lua_State *L) { 1280 /* split on '&' and '=', url-decode and replace non-printable chars w/ '_', 1281 * and store components in table 1282 * (string input should be query-string without leading '?') 1283 * (note: duplicated keys replace earlier values, but this interface returns 1284 * a table useful for lookups, so this limitation is often acceptable) */ 1285 lua_createtable(L, 0, 0); 1286 if (lua_isnoneornil(L, 1)) { 1287 return 1; 1288 } 1289 const_buffer s = magnet_checkconstbuffer(L, 1); 1290 if (0 == s.len) { 1291 return 1; 1292 } 1293 buffer * const b = magnet_tmpbuf_acquire(L); 1294 for (const char *qs = s.ptr, *eq, *amp; *qs; qs = amp+1) { 1295 for (amp = qs, eq = NULL; *amp && *amp != '&'; ++amp) { 1296 if (*amp == '=' && !eq) eq = amp; 1297 } 1298 if (amp != qs) { 1299 if (eq) { 1300 magnet_urldec_query_part(b, qs, (size_t)(eq - qs)); 1301 lua_pushlstring(L, BUF_PTR_LEN(b)); 1302 magnet_urldec_query_part(b, eq+1, (size_t)(amp - (eq+1))); 1303 lua_pushlstring(L, BUF_PTR_LEN(b)); 1304 } 1305 else { 1306 magnet_urldec_query_part(b, qs, (size_t)(amp - qs)); 1307 lua_pushlstring(L, BUF_PTR_LEN(b)); 1308 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/ 1309 } 1310 lua_rawset(L, -3); 1311 } 1312 if (*amp == '\0') break; 1313 } 1314 magnet_tmpbuf_release(b); 1315 return 1; 1316 } 1317 1318 static void magnet_urlenc_query_part(buffer * const b, const char * const s, const size_t slen, const int iskey) { 1319 /* encode query part (each part is typically much smaller than chunk buffer) 1320 * all required chars plus '&' ';' '+' '\'' (and encode '=' if part of key) 1321 * (burl_append(b,str,len,BURL_ENCODE_ALL) works, but over-encodes) */ 1322 #if 0 1323 /* alternative: (over-encodes some, but less than burl_append()) */ 1324 UNUSED(iskey); 1325 buffer_append_string_encoded(b, s, slen, ENCODING_REL_URI); 1326 #else 1327 static const char hex_chars_uc[] = "0123456789ABCDEF"; 1328 char * const p = buffer_string_prepare_append(b, slen*3); 1329 int j = 0; 1330 for (size_t i = 0; i < slen; ++i, ++j) { 1331 int c = s[i]; 1332 if (!light_isalnum(c)) switch (c) { 1333 case ' ': 1334 c = '+'; 1335 break; 1336 /*case '\'':*//*(ok in url query-part, but might be misused in HTML)*/ 1337 case '!': case '$': case '(': case ')': case '*': case ',': case '-': 1338 case '.': case '/': case ':': case '?': case '@': case '_': case '~': 1339 break; 1340 case '=': 1341 if (!iskey) break; 1342 __attribute_fallthrough__ 1343 default: 1344 p[j] = '%'; 1345 p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; 1346 p[++j] = hex_chars_uc[s[i] & 0xF]; 1347 continue; 1348 } 1349 p[j] = c; 1350 } 1351 buffer_commit(b, j); 1352 #endif 1353 } 1354 1355 static int magnet_urlenc_query(lua_State *L) { 1356 /* encode pairs in lua table into query string 1357 * (caller should add leading '?' or '&' when appending to full URL) 1358 * (caller should skip empty table if appending to existing query-string) */ 1359 if (!lua_istable(L, 1)) { 1360 lua_pushlstring(L, "", 0); 1361 return 1; 1362 } 1363 buffer * const b = magnet_tmpbuf_acquire(L); 1364 const_buffer s; 1365 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { 1366 if (lua_isstring(L, -2)) { 1367 if (!buffer_is_blank(b)) 1368 buffer_append_char(b, '&'); 1369 s = magnet_checkconstbuffer(L, -2); 1370 magnet_urlenc_query_part(b, s.ptr, s.len, 1); 1371 if (!lua_isnil(L, -1)) { 1372 s = magnet_checkconstbuffer(L, -1); 1373 buffer_append_char(b, '='); 1374 magnet_urlenc_query_part(b, s.ptr, s.len, 0); 1375 } 1376 } 1377 } 1378 lua_pushlstring(L, BUF_PTR_LEN(b)); 1379 magnet_tmpbuf_release(b); 1380 return 1; 1381 } 1382 1383 static int magnet_urlenc_normalize(lua_State *L) { 1384 /* normalize url-encoding 1385 * url-encode (and url-decode safe chars) to normalize url-path 1386 * ('?' is treated as start of query-string and is not encoded; 1387 * caller must encode '?' if intended to be part of url-path) 1388 * ('/' is not encoded; caller must encode if not path separator) 1389 * (burl_append() is not exposed here; caller might want to build 1390 * url with lighty.c.urlenc() and lighty.c.urlenc_query(), 1391 * then call lighty.c.urlenc_normalize()) */ 1392 if (lua_isnoneornil(L, -1)) { 1393 lua_pushlstring(L, "", 0); 1394 return 1; 1395 } 1396 const_buffer s = magnet_checkconstbuffer(L, -1); 1397 if (0 == s.len) { 1398 lua_pushvalue(L, -1); 1399 return 1; 1400 } 1401 buffer * const b = magnet_tmpbuf_acquire(L); 1402 buffer * const t = chunk_buffer_acquire(); 1403 #if 0 /*(?maybe have different interface to use config policy?)*/ 1404 request_st * const r = magnet_get_request(L); 1405 const int flags = r->conf.http_parseopts; 1406 #else 1407 const int flags = HTTP_PARSEOPT_URL_NORMALIZE 1408 | HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED 1409 | HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED 1410 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE 1411 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE 1412 | HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS; 1413 #endif 1414 buffer_copy_string_len(b, s.ptr, s.len); 1415 burl_normalize(b, t, flags); 1416 lua_pushlstring(L, BUF_PTR_LEN(b)); 1417 chunk_buffer_release(t); 1418 magnet_tmpbuf_release(b); 1419 return 1; 1420 } 1421 1422 static int magnet_fspath_simplify(lua_State *L) { 1423 /* simplify filesystem path */ 1424 if (lua_isnoneornil(L, -1)) { 1425 lua_pushlstring(L, "", 0); 1426 return 1; 1427 } 1428 const_buffer s = magnet_checkconstbuffer(L, -1); 1429 if (0 == s.len) { 1430 lua_pushvalue(L, -1); 1431 return 1; 1432 } 1433 buffer * const b = magnet_tmpbuf_acquire(L); 1434 buffer_copy_string_len(b, s.ptr, s.len); 1435 buffer_path_simplify(b); 1436 lua_pushlstring(L, BUF_PTR_LEN(b)); 1437 magnet_tmpbuf_release(b); 1438 return 1; 1439 } 1440 1441 __attribute_pure__ 1442 static const char * magnet_scan_quoted_string (const char *s) { 1443 /* scan to end of of quoted-string (with s starting at '"') 1444 * loose parse; non-validating 1445 * - permit missing '"' at end of string (caller should check) 1446 * - stop at stray '\\' at end of string missing closing '"' 1447 * - not rejecting non-WS CTL chars 1448 */ 1449 /*force_assert(*s == '"');*/ 1450 do { ++s; } while (*s && *s != '"' && (*s != '\\' || (s[1] ? ++s : 0))); 1451 /*do { ++s; } while (*s && *s != '"' && (*s != '\\' || (*++s || (--s, 0))));*/ 1452 return s; 1453 } 1454 1455 static const char * magnet_push_quoted_string_range (lua_State *L, const char *b, const char *s) { 1456 /* quoted-string is unmodified (except for quoted-string end consistency) 1457 * including surrounding double-quotes and with quoted-pair unmodified */ 1458 if (__builtin_expect( (*s == '"'), 1)) 1459 lua_pushlstring(L, b, (size_t)(++s-b)); 1460 else { /*(else invalid quoted-string, but handle anyway)*/ 1461 /* append closing '"' for consistency */ 1462 lua_pushlstring(L, b, (size_t)(s-b)); 1463 if (*s != '\\') 1464 lua_pushlstring(L, "\"", 1); 1465 else { /* unescaped backslash at end of string; escape and close '"' */ 1466 lua_pushlstring(L, "\\\\\"", 3); 1467 ++s; /*(now *s == '\0')*/ 1468 } 1469 lua_concat(L, 2); 1470 } 1471 return s; 1472 } 1473 1474 static const char * magnet_push_quoted_string(lua_State *L, const char *s) { 1475 return magnet_push_quoted_string_range(L, s, magnet_scan_quoted_string(s)); 1476 } 1477 1478 static const char * magnet_cookie_param_push_token(lua_State *L, const char *s) { 1479 const char *b = s; 1480 while (*s!='=' /*(note: not strictly rejecting all 'separators')*/ 1481 && *s!=';' && *s!=' ' && *s!='\t' && *s!='\r' && *s!='\n' && *s) 1482 ++s; 1483 lua_pushlstring(L, b, (size_t)(s-b)); 1484 return s; 1485 } 1486 1487 static int magnet_cookie_tokens(lua_State *L) { 1488 /*(special-case cookies (';' separator); dup cookie-names overwritten)*/ 1489 lua_createtable(L, 0, 0); 1490 if (lua_isnoneornil(L, 1)) 1491 return 1; 1492 const char *s = luaL_checkstring(L, 1); 1493 do { 1494 while (*s==';' || *s==' ' || *s=='\t' || *s=='\r' || *s=='\n') 1495 ++s; 1496 if (*s == '\0') break; 1497 s = magnet_cookie_param_push_token(L, s); 1498 while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n') 1499 ++s; 1500 if (*s == '=') { 1501 do { 1502 ++s; 1503 } while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n'); 1504 if (*s==';' || *s=='\0') 1505 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/ 1506 else if (*s != '"') 1507 s = magnet_cookie_param_push_token(L, s); 1508 else 1509 s = magnet_push_quoted_string(L, s); 1510 } 1511 else { 1512 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/ 1513 } 1514 lua_settable(L, -3); 1515 while (*s!=';' && *s!='\0') ++s; /* ignore/skip stray tokens */ 1516 } while (*s++); 1517 return 1; 1518 } 1519 1520 static const char * magnet_push_token(lua_State *L, const char *s) { 1521 const char *b = s; 1522 while ( *s!=' ' && *s!='\t' && *s!='\r' && *s!='\n' 1523 && *s!=',' && *s!=';' && *s!='=' && *s) 1524 ++s; 1525 lua_pushlstring(L, b, (size_t)(s-b)); 1526 return s; 1527 } 1528 1529 static int magnet_header_tokens(lua_State *L) { 1530 /* split into sequence of tokens/words 1531 * Intermediate table is then more convenient to walk once quoted-string 1532 * parsed into table entries since quoted-string may contain separators. 1533 * Each token can be passed to lighty.c.quoteddec() 1534 * (lighty.c.quoteddec() returns string as-is if not quoted-string) 1535 * (note: non-validating; 1536 * e.g. existence of '=' token does not mean that next token is value, 1537 * and '=' in value which is not part of quoted-string will be 1538 * treated as separate token) 1539 * (note: case is preserved; non-quoted-string tokens are not lower-cased) 1540 * (words separated by whitespace are separate tokens unless quoted-string) 1541 * (if that format not permitted in a specific header, caller must detect) 1542 * (optional whitespace (OWS) and bad whitespace (BWS) are removed) 1543 * (caller can create lookup table from sequence table, as appropriate) */ 1544 lua_createtable(L, 0, 0); 1545 if (lua_isnoneornil(L, 1)) 1546 return 1; 1547 const char *s = luaL_checkstring(L, 1); 1548 int i = 0; 1549 do { 1550 while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n') 1551 ++s; 1552 if (*s=='\0') break; 1553 if (*s==',' || *s==';' || *s=='=') 1554 lua_pushlstring(L, s++, 1); 1555 else if (*s != '"') 1556 s = magnet_push_token(L, s); 1557 else 1558 s = magnet_push_quoted_string(L, s); 1559 lua_rawseti(L, -2, ++i); 1560 } while (*s); 1561 return 1; 1562 } 1563 1564 static int magnet_reqhdr_get(lua_State *L) { 1565 size_t klen; 1566 const char * const k = luaL_checklstring(L, 2, &klen); 1567 const request_st * const r = **(request_st ***)lua_touserdata(L, 1); 1568 const int id = http_header_hkey_get(k, (uint32_t)klen); 1569 magnet_push_buffer(L, http_header_request_get(r, id, k, klen)); 1570 return 1; 1571 } 1572 1573 static int magnet_reqhdr_set(lua_State *L) { 1574 const_buffer k = magnet_checkconstbuffer(L, 2); 1575 const_buffer v = magnet_checkconstbuffer(L, 3); 1576 1577 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 1578 enum http_header_e id = http_header_hkey_get(k.ptr, (uint32_t)k.len); 1579 1580 switch (id) { 1581 /*case HTTP_HEADER_OTHER:*/ 1582 default: 1583 break; 1584 1585 case HTTP_HEADER_HOST: 1586 /* do not allow Host to be unset, even if HTTP/1.0 1587 * (change Host to something else, if you must */ 1588 if (0 == v.len) return 0; 1589 1590 /*(must set r->http_host if r->http_host was not previously set)*/ 1591 /* copied from request.c:http_request_header_set_Host() */ 1592 r->http_host = http_header_request_set_ptr(r, HTTP_HEADER_HOST, 1593 CONST_STR_LEN("Host")); 1594 buffer_copy_string_len_lc(r->http_host, v.ptr, v.len); 1595 return 0; 1596 1597 case HTTP_HEADER_CONTENT_LENGTH: 1598 /* not attempting to handle Content-Length modification; may revisit */ 1599 /* future: might permit setting to 0 to force discard of request body 1600 * but would have to check if request body present, and if 1601 * Content-Length was set, or if Transfer-Encoding: chunked, 1602 * and handle resetting internal chunked encoding state, 1603 * as well as making sure that this is handled properly for HTTP/2 */ 1604 return 0; /* silently ignore; do not allow modification */ 1605 1606 /* do not permit modification of hop-by-hop (connection) headers */ 1607 1608 case HTTP_HEADER_CONNECTION: 1609 /* do not permit modification of Connection, incl add/remove tokens */ 1610 /* future: might provide a different interface to set r->keep_alive = 0, 1611 * and also handle in context if HTTP/2 */ 1612 case HTTP_HEADER_TRANSFER_ENCODING: 1613 case HTTP_HEADER_SET_COOKIE:/*(response hdr;avoid accidental reflection)*/ 1614 return 0; /* silently ignore; do not allow modification */ 1615 #if 0 /*(eh, if script sets Upgrade, script probably intends this action)*/ 1616 case HTTP_HEADER_UPGRADE: 1617 /* note: modifications here do not modify Connection header 1618 * to add or remove "upgrade" token */ 1619 /* future: might allow removal of existing tokens, but not addition */ 1620 if (0 != v.len) return 0; /* do not allow Upgrade other than to unset */ 1621 break; 1622 #endif 1623 #if 0 /*(eh, if script sets TE, script probably intends this action)*/ 1624 case HTTP_HEADER_TE: 1625 if (0 != v.len) return 0; /* do not allow TE other than to unset */ 1626 break; 1627 #endif 1628 } 1629 1630 v.len 1631 ? http_header_request_set(r, id, k.ptr, k.len, v.ptr, v.len) 1632 : http_header_request_unset(r, id, k.ptr, k.len); 1633 return 0; 1634 } 1635 1636 static int magnet_reqhdr_pairs(lua_State *L) { 1637 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 1638 return magnet_array_pairs(L, &r->rqst_headers); 1639 } 1640 1641 static int magnet_resphdr_get(lua_State *L) { 1642 /* Note: access to lighttpd r->resp_headers here is *independent* from 1643 * the (pending) changes in the (deprecated) lua lighty.header[] table */ 1644 size_t klen; 1645 const char * const k = luaL_checklstring(L, 2, &klen); 1646 const request_st * const r = **(request_st ***)lua_touserdata(L, 1); 1647 const int id = http_header_hkey_get(k, (uint32_t)klen); 1648 magnet_push_buffer(L, http_header_response_get(r, id, k, klen)); 1649 return 1; 1650 } 1651 1652 static int magnet_resphdr_set_kv(lua_State *L, request_st * const r) { 1653 const const_buffer k = magnet_checkconstbuffer(L, -2); 1654 const const_buffer v = magnet_checkconstbuffer(L, -1); 1655 const enum http_header_e id = http_header_hkey_get(k.ptr, (uint32_t)k.len); 1656 1657 switch (id) { 1658 /*case HTTP_HEADER_OTHER:*/ 1659 default: 1660 break; 1661 1662 case HTTP_HEADER_CONTENT_LENGTH: 1663 /* lighttpd handles Content-Length or Transfer-Encoding for response */ 1664 return 0; /* silently ignore; do not allow modification */ 1665 1666 /* do not permit modification of hop-by-hop (connection) headers */ 1667 1668 case HTTP_HEADER_CONNECTION: 1669 /* do not permit modification of Connection, incl add/remove tokens */ 1670 /* future: might provide a different interface to set r->keep_alive = 0, 1671 * and also handle in context if HTTP/2 */ 1672 case HTTP_HEADER_TRANSFER_ENCODING: 1673 return 0; /* silently ignore; do not allow modification */ 1674 } 1675 1676 if (0 == v.len) { 1677 http_header_response_unset(r, id, k.ptr, k.len); 1678 return 0; 1679 } 1680 1681 buffer * const vb = http_header_response_set_ptr(r, id, k.ptr, k.len); 1682 buffer_copy_string_len(vb, v.ptr, v.len); 1683 1684 if (r->http_version >= HTTP_VERSION_2) { 1685 /* handle multi-line response headers with HTTP/2 1686 * (lowercase header name and mark r->resp_header_repeated) 1687 * (similar to http_header.c:http_header_response_insert_addtl()) */ 1688 for (char *n = vb->ptr; (n = strchr(n, '\n')); ) { 1689 r->resp_header_repeated = 1; 1690 do { 1691 ++n; 1692 if (light_isupper(*n)) *n |= 0x20; 1693 } while (*n != ':' && *n != '\n' && *n != '\0'); 1694 } 1695 } 1696 1697 return 0; 1698 } 1699 1700 static int magnet_resphdr_set(lua_State *L) { 1701 /*const_buffer k = magnet_checkconstbuffer(L, 2);*/ 1702 /*const_buffer v = magnet_checkconstbuffer(L, 3);*/ 1703 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 1704 return magnet_resphdr_set_kv(L, r); 1705 } 1706 1707 static int magnet_resphdr_pairs(lua_State *L) { 1708 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 1709 return magnet_array_pairs(L, &r->resp_headers); 1710 } 1711 1712 static int magnet_plugin_stats_get(lua_State *L) { 1713 const_buffer k = magnet_checkconstbuffer(L, 2); 1714 lua_pushinteger(L, (lua_Integer)*plugin_stats_get_ptr(k.ptr, k.len)); 1715 return 1; 1716 } 1717 1718 static int magnet_plugin_stats_set(lua_State *L) { 1719 const_buffer k = magnet_checkconstbuffer(L, 2); 1720 plugin_stats_set(k.ptr, k.len, luaL_checkinteger(L, 3)); 1721 return 0; 1722 } 1723 1724 static int magnet_plugin_stats_pairs(lua_State *L) { 1725 return magnet_array_pairs(L, &plugin_stats); 1726 } 1727 1728 1729 static int 1730 magnet_req_item_get (lua_State *L) 1731 { 1732 size_t klen; 1733 const char * const k = luaL_checklstring(L, 2, &klen); 1734 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 1735 switch (klen) { 1736 case 8: 1737 if (0 == memcmp(k, "bytes_in", 8)) { 1738 lua_pushinteger(L, (lua_Integer)http_request_stats_bytes_in(r)); 1739 return 1; 1740 } 1741 break; 1742 case 9: 1743 if (0 == memcmp(k, "bytes_out", 9)) { 1744 lua_pushinteger(L, (lua_Integer)http_request_stats_bytes_out(r)); 1745 return 1; 1746 } 1747 if (0 == memcmp(k, "stream_id", 9)) { 1748 lua_pushinteger(L, (lua_Integer)r->h2id); 1749 return 1; 1750 } 1751 if (0 == memcmp(k, "req_count", 9)) { 1752 lua_pushinteger(L, (lua_Integer)r->con->request_count); 1753 return 1; 1754 } 1755 break; 1756 case 10: 1757 if (0 == memcmp(k, "start_time", 10)) { 1758 lua_pushinteger(L, (lua_Integer)r->start_hp.tv_sec); 1759 lua_pushinteger(L, (lua_Integer)r->start_hp.tv_nsec); 1760 lua_pushcclosure(L, magnet_return_upvalue2, 2); 1761 return 1; 1762 } 1763 if (0 == memcmp(k, "keep_alive", 10)) { 1764 lua_pushinteger(L, (lua_Integer)r->keep_alive); 1765 return 1; 1766 } 1767 break; 1768 case 11: 1769 if (0 == memcmp(k, "http_status", 11)) { 1770 lua_pushinteger(L, (lua_Integer)r->http_status); 1771 return 1; 1772 } 1773 break; 1774 case 14: 1775 if (0 == memcmp(k, "req_header_len", 14)) { 1776 lua_pushinteger(L, (lua_Integer)r->rqst_header_len); 1777 return 1; 1778 } 1779 break; 1780 case 15: 1781 if (0 == memcmp(k, "resp_header_len", 15)) { 1782 lua_pushinteger(L, (lua_Integer)r->resp_header_len); 1783 return 1; 1784 } 1785 break; 1786 default: 1787 break; 1788 } 1789 return luaL_error(L, "r.req_item['%s'] invalid", k); 1790 } 1791 1792 static int 1793 magnet_req_item_set (lua_State *L) 1794 { 1795 size_t klen; 1796 const char * const k = luaL_checklstring(L, 2, &klen); 1797 int v = (int)luaL_checkinteger(L, 3); 1798 1799 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 1800 switch (klen) { 1801 case 10: 1802 if (0 == memcmp(k, "keep_alive", 10)) { 1803 if (v == 0 || v == -1) r->keep_alive = v; 1804 return 0; 1805 } 1806 break; 1807 default: 1808 break; 1809 } 1810 return luaL_error(L, "r.req_item['%s'] invalid or read-only", k); 1811 } 1812 1813 1814 typedef struct { 1815 const char *name; 1816 uint32_t nlen; 1817 enum { 1818 MAGNET_ENV_UNSET, 1819 1820 MAGNET_ENV_PHYSICAL_PATH, 1821 MAGNET_ENV_PHYSICAL_REL_PATH, 1822 MAGNET_ENV_PHYSICAL_DOC_ROOT, 1823 MAGNET_ENV_PHYSICAL_BASEDIR, 1824 1825 MAGNET_ENV_URI_PATH, 1826 MAGNET_ENV_URI_PATH_RAW, 1827 MAGNET_ENV_URI_SCHEME, 1828 MAGNET_ENV_URI_AUTHORITY, 1829 MAGNET_ENV_URI_QUERY, 1830 1831 MAGNET_ENV_REQUEST_METHOD, 1832 MAGNET_ENV_REQUEST_URI, 1833 MAGNET_ENV_REQUEST_ORIG_URI, 1834 MAGNET_ENV_REQUEST_PATH_INFO, 1835 MAGNET_ENV_REQUEST_REMOTE_ADDR, 1836 MAGNET_ENV_REQUEST_REMOTE_PORT, 1837 MAGNET_ENV_REQUEST_SERVER_ADDR, 1838 MAGNET_ENV_REQUEST_SERVER_PORT, 1839 MAGNET_ENV_REQUEST_PROTOCOL, 1840 MAGNET_ENV_REQUEST_SERVER_NAME, 1841 MAGNET_ENV_REQUEST_STAGE 1842 } type; 1843 } magnet_env_t; 1844 1845 /*(NB: coordinate any changes with scan offsets in magnet_env_get_id())*/ 1846 static const magnet_env_t magnet_env[] = { 1847 { CONST_STR_LEN("physical.path"), MAGNET_ENV_PHYSICAL_PATH }, 1848 { CONST_STR_LEN("physical.rel-path"), MAGNET_ENV_PHYSICAL_REL_PATH }, 1849 { CONST_STR_LEN("physical.doc-root"), MAGNET_ENV_PHYSICAL_DOC_ROOT }, 1850 { CONST_STR_LEN("physical.basedir"), MAGNET_ENV_PHYSICAL_BASEDIR }, 1851 1852 { CONST_STR_LEN("uri.path"), MAGNET_ENV_URI_PATH }, 1853 { CONST_STR_LEN("uri.path-raw"), MAGNET_ENV_URI_PATH_RAW }, 1854 { CONST_STR_LEN("uri.scheme"), MAGNET_ENV_URI_SCHEME }, 1855 { CONST_STR_LEN("uri.authority"), MAGNET_ENV_URI_AUTHORITY }, 1856 { CONST_STR_LEN("uri.query"), MAGNET_ENV_URI_QUERY }, 1857 1858 { CONST_STR_LEN("request.method"), MAGNET_ENV_REQUEST_METHOD }, 1859 { CONST_STR_LEN("request.uri"), MAGNET_ENV_REQUEST_URI }, 1860 { CONST_STR_LEN("request.orig-uri"), MAGNET_ENV_REQUEST_ORIG_URI }, 1861 { CONST_STR_LEN("request.path-info"), MAGNET_ENV_REQUEST_PATH_INFO }, 1862 { CONST_STR_LEN("request.remote-ip"), MAGNET_ENV_REQUEST_REMOTE_ADDR }, 1863 { CONST_STR_LEN("request.remote-addr"), MAGNET_ENV_REQUEST_REMOTE_ADDR }, 1864 { CONST_STR_LEN("request.remote-port"), MAGNET_ENV_REQUEST_REMOTE_PORT }, 1865 { CONST_STR_LEN("request.server-addr"), MAGNET_ENV_REQUEST_SERVER_ADDR }, 1866 { CONST_STR_LEN("request.server-port"), MAGNET_ENV_REQUEST_SERVER_PORT }, 1867 { CONST_STR_LEN("request.protocol"), MAGNET_ENV_REQUEST_PROTOCOL }, 1868 { CONST_STR_LEN("request.server-name"), MAGNET_ENV_REQUEST_SERVER_NAME }, 1869 { CONST_STR_LEN("request.stage"), MAGNET_ENV_REQUEST_STAGE }, 1870 1871 { NULL, 0, MAGNET_ENV_UNSET } 1872 }; 1873 1874 __attribute_cold__ 1875 static void 1876 magnet_env_get_uri_path_raw (buffer * const dest, const buffer * const target) 1877 { 1878 const uint32_t len = buffer_clen(target); 1879 const char * const qmark = memchr(target->ptr, '?', len); 1880 buffer_copy_string_len(dest, target->ptr, 1881 qmark ? (uint32_t)(qmark - target->ptr) : len); 1882 } 1883 1884 __attribute_cold__ 1885 static int 1886 magnet_env_set_uri_path_raw (request_st * const r, 1887 const const_buffer * const val) 1888 { 1889 /* modify uri-path of r->target; preserve query-part, if present */ 1890 /* XXX: should we require that resulting path begin with '/' or %2F ? */ 1891 const uint32_t len = buffer_clen(&r->target); 1892 const char * const qmark = memchr(r->target.ptr, '?', len); 1893 if (NULL != qmark) 1894 buffer_copy_string_len(r->tmp_buf, qmark, 1895 len - (uint32_t)(qmark - r->target.ptr)); 1896 buffer_copy_string_len(&r->target, val->ptr, val->len); 1897 if (NULL != qmark) 1898 buffer_append_string_buffer(&r->target, r->tmp_buf); 1899 return 0; 1900 } 1901 1902 __attribute_cold__ 1903 __attribute_noinline__ 1904 static buffer * 1905 magnet_env_get_laddr_by_id (request_st * const r, const int id) 1906 { 1907 buffer * const dest = r->tmp_buf; 1908 const server_socket * const srv_socket = r->con->srv_socket; 1909 switch (id) { 1910 case MAGNET_ENV_REQUEST_SERVER_ADDR: /* local IP without port */ 1911 if (sock_addr_is_addr_wildcard(&srv_socket->addr)) { 1912 sock_addr addrbuf; 1913 socklen_t addrlen = sizeof(addrbuf); 1914 const int fd = r->con->fd; 1915 if (0 == getsockname(fd,(struct sockaddr *)&addrbuf,&addrlen)) { 1916 char buf[INET6_ADDRSTRLEN + 1]; 1917 const char *s = sock_addr_inet_ntop(&addrbuf, buf, sizeof(buf)); 1918 if (NULL != s) { 1919 buffer_copy_string_len(dest, s, strlen(s)); 1920 break; 1921 } 1922 } 1923 } 1924 buffer_copy_string_len(dest, srv_socket->srv_token->ptr, 1925 srv_socket->srv_token_colon); 1926 break; 1927 case MAGNET_ENV_REQUEST_SERVER_PORT: 1928 { 1929 const buffer * const srv_token = srv_socket->srv_token; 1930 const uint32_t tlen = buffer_clen(srv_token); 1931 uint32_t portoffset = srv_socket->srv_token_colon; 1932 portoffset = portoffset < tlen ? portoffset+1 : tlen; 1933 buffer_copy_string_len(dest, srv_token->ptr+portoffset, 1934 tlen-portoffset); 1935 break; 1936 } 1937 default: 1938 break; 1939 } 1940 return dest; 1941 } 1942 1943 static buffer * 1944 magnet_env_get_buffer_by_id (request_st * const r, const int id) 1945 { 1946 buffer *dest = r->tmp_buf; 1947 buffer_clear(dest); 1948 1949 switch (id) { 1950 case MAGNET_ENV_PHYSICAL_PATH: dest = &r->physical.path; break; 1951 case MAGNET_ENV_PHYSICAL_REL_PATH: dest = &r->physical.rel_path; break; 1952 case MAGNET_ENV_PHYSICAL_DOC_ROOT: dest = &r->physical.doc_root; break; 1953 case MAGNET_ENV_PHYSICAL_BASEDIR: dest = &r->physical.basedir; break; 1954 1955 case MAGNET_ENV_URI_PATH: dest = &r->uri.path; break; 1956 case MAGNET_ENV_URI_PATH_RAW: 1957 magnet_env_get_uri_path_raw(dest, &r->target); 1958 break; 1959 case MAGNET_ENV_URI_SCHEME: dest = &r->uri.scheme; break; 1960 case MAGNET_ENV_URI_AUTHORITY: dest = &r->uri.authority; break; 1961 case MAGNET_ENV_URI_QUERY: dest = &r->uri.query; break; 1962 1963 case MAGNET_ENV_REQUEST_METHOD: 1964 http_method_append(dest, r->http_method); 1965 break; 1966 case MAGNET_ENV_REQUEST_URI: dest = &r->target; break; 1967 case MAGNET_ENV_REQUEST_ORIG_URI: dest = &r->target_orig; break; 1968 case MAGNET_ENV_REQUEST_PATH_INFO: dest = &r->pathinfo; break; 1969 case MAGNET_ENV_REQUEST_REMOTE_ADDR: dest = &r->con->dst_addr_buf; break; 1970 case MAGNET_ENV_REQUEST_REMOTE_PORT: 1971 buffer_append_int(dest, sock_addr_get_port(&r->con->dst_addr)); 1972 break; 1973 case MAGNET_ENV_REQUEST_SERVER_ADDR: /* local IP without port */ 1974 case MAGNET_ENV_REQUEST_SERVER_PORT: 1975 return magnet_env_get_laddr_by_id(r, id); 1976 case MAGNET_ENV_REQUEST_PROTOCOL: 1977 http_version_append(dest, r->http_version); 1978 break; 1979 case MAGNET_ENV_REQUEST_SERVER_NAME: 1980 buffer_copy_buffer(dest, r->server_name); 1981 break; 1982 case MAGNET_ENV_REQUEST_STAGE: 1983 if (http_request_state_is_keep_alive(r)) 1984 buffer_append_string_len(dest, CONST_STR_LEN("keep-alive")); 1985 else 1986 http_request_state_append(dest, r->state); 1987 break; 1988 1989 case MAGNET_ENV_UNSET: 1990 return NULL; 1991 } 1992 1993 return dest; 1994 } 1995 1996 __attribute_pure__ 1997 static int magnet_env_get_id(const char * const key, const size_t klen) { 1998 /*(NB: ensure offsets match position in magnet_env[])*/ 1999 int i; /* magnet_env[] scan offset */ 2000 switch (*key) { 2001 case 'r': /* request.* or response.* */ 2002 i = klen > 7 && key[7] == '.' ? 9 : 21; 2003 break; 2004 case 'u': /* uri.* */ 2005 default: 2006 i = 4; 2007 break; 2008 case 'p': /* physical.* */ 2009 i = 0; 2010 break; 2011 } 2012 for (; i < (int)(sizeof(magnet_env)/sizeof(*magnet_env)); ++i) { 2013 if (klen == magnet_env[i].nlen 2014 && 0 == memcmp(key, magnet_env[i].name, klen)) 2015 return magnet_env[i].type; 2016 } 2017 return MAGNET_ENV_UNSET; 2018 } 2019 2020 static int magnet_env_get(lua_State *L) { 2021 size_t klen; 2022 const char * const k = luaL_checklstring(L, 2, &klen); 2023 const int env_id = magnet_env_get_id(k, klen); 2024 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 2025 magnet_push_buffer(L, magnet_env_get_buffer_by_id(r, env_id)); 2026 return 1; 2027 } 2028 2029 __attribute_cold__ 2030 static int 2031 magnet_env_set_raddr_by_id (lua_State *L, request_st * const r, const int id, 2032 const const_buffer * const val) 2033 { 2034 connection * const con = r->con; 2035 switch (id) { 2036 case MAGNET_ENV_REQUEST_REMOTE_ADDR: 2037 #ifdef HAVE_SYS_UN_H 2038 if (val->len && *val->ptr == '/' 2039 && 0 == sock_addr_assign(&con->dst_addr, AF_UNIX, 0, val->ptr)) { 2040 } 2041 else 2042 #endif 2043 { 2044 sock_addr saddr; 2045 saddr.plain.sa_family = AF_UNSPEC; 2046 if (1 == sock_addr_from_str_numeric(&saddr, val->ptr, r->conf.errh) 2047 && saddr.plain.sa_family != AF_UNSPEC) { 2048 sock_addr_set_port(&saddr, 0); 2049 memcpy(&con->dst_addr, &saddr, sizeof(sock_addr)); 2050 } 2051 else { 2052 return luaL_error(L, 2053 "r.req_attr['remote-addr'] invalid addr: %s", 2054 val->ptr); 2055 } 2056 } 2057 buffer_copy_string_len(&con->dst_addr_buf, val->ptr, val->len); 2058 config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP); 2059 break; 2060 case MAGNET_ENV_REQUEST_REMOTE_PORT: 2061 sock_addr_set_port(&con->dst_addr, (unsigned short)atoi(val->ptr)); 2062 break; 2063 default: 2064 break; 2065 } 2066 return 0; 2067 } 2068 2069 #if 0 2070 __attribute_cold__ 2071 static int 2072 magnet_env_set_server_name (request_st * const r, 2073 const const_buffer * const val) 2074 { 2075 r->server_name = &r->server_name_buf; 2076 buffer_copy_string_len(&r->server_name_buf, val->ptr, val->len); 2077 return 0; 2078 } 2079 #endif 2080 2081 static int magnet_env_set(lua_State *L) { 2082 size_t klen; 2083 const char * const key = luaL_checklstring(L, 2, &klen); 2084 const_buffer val = magnet_checkconstbuffer(L, 3); 2085 2086 const int env_id = magnet_env_get_id(key, klen); 2087 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 2088 2089 switch (env_id) { 2090 default: 2091 break; 2092 case MAGNET_ENV_URI_PATH_RAW: 2093 return magnet_env_set_uri_path_raw(r, &val); 2094 case MAGNET_ENV_REQUEST_REMOTE_ADDR: 2095 case MAGNET_ENV_REQUEST_REMOTE_PORT: 2096 return magnet_env_set_raddr_by_id(L, r, env_id, &val); 2097 #if 0 /*(leave read-only for now; change attempts silently ignored)*/ 2098 case MAGNET_ENV_REQUEST_SERVER_NAME: 2099 return magnet_env_set_server_name(r, &val); 2100 #endif 2101 /*case MAGNET_ENV_REQUEST_STAGE:*//*(change attempts silently ignored)*/ 2102 } 2103 2104 buffer * const dest = magnet_env_get_buffer_by_id(r, env_id); 2105 if (NULL == dest) 2106 return luaL_error(L, "couldn't store '%s' in r.req_attr[]", key); 2107 2108 if (lua_isnoneornil(L, 3)) { 2109 if (env_id==MAGNET_ENV_URI_QUERY || env_id==MAGNET_ENV_PHYSICAL_PATH) 2110 buffer_clear(dest); 2111 else 2112 buffer_blank(dest); 2113 } 2114 else { 2115 buffer_copy_string_len(dest, val.ptr, val.len); 2116 /* NB: setting r->uri.query does not modify query-part in r->target */ 2117 } 2118 2119 switch (env_id) { 2120 case MAGNET_ENV_URI_SCHEME: 2121 buffer_to_lower(dest); 2122 config_cond_cache_reset_item(r, COMP_HTTP_SCHEME); 2123 break; 2124 case MAGNET_ENV_URI_AUTHORITY: 2125 r->server_name = dest; 2126 buffer_to_lower(dest); 2127 config_cond_cache_reset_item(r, COMP_HTTP_HOST); 2128 break; 2129 case MAGNET_ENV_URI_PATH: 2130 config_cond_cache_reset_item(r, COMP_HTTP_URL); 2131 break; 2132 case MAGNET_ENV_URI_QUERY: 2133 config_cond_cache_reset_item(r, COMP_HTTP_QUERY_STRING); 2134 break; 2135 default: 2136 break; 2137 } 2138 2139 return 0; 2140 } 2141 2142 static int magnet_env_next(lua_State *L) { 2143 /* ignore previous key: use upvalue for current pos */ 2144 lua_settop(L, 0); 2145 const int pos = lua_tointeger(L, lua_upvalueindex(1)); 2146 2147 if (NULL == magnet_env[pos].name) return 0; /* end of list */ 2148 /* Update our positional upval to reflect our new current position */ 2149 lua_pushinteger(L, pos + 1); 2150 lua_replace(L, lua_upvalueindex(1)); 2151 2152 /* key to return */ 2153 lua_pushlstring(L, magnet_env[pos].name, magnet_env[pos].nlen); 2154 2155 /* get value */ 2156 request_st * const r = lua_touserdata(L, lua_upvalueindex(2)); 2157 magnet_push_buffer(L, magnet_env_get_buffer_by_id(r, magnet_env[pos].type)); 2158 2159 /* return 2 items on the stack (key, value) */ 2160 return 2; 2161 } 2162 2163 static int magnet_env_pairs(lua_State *L) { 2164 lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */ 2165 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 2166 lua_pushlightuserdata(L, r); /* Push request_st *r into upval 2 */ 2167 lua_pushcclosure(L, magnet_env_next, 2); /* Push new closure with 2 upvals*/ 2168 return 1; 2169 } 2170 2171 static int magnet_envvar_get(lua_State *L) { 2172 size_t klen; 2173 const char * const k = luaL_checklstring(L, 2, &klen); 2174 const request_st * const r = **(request_st ***)lua_touserdata(L, 1); 2175 magnet_push_buffer(L, http_header_env_get(r, k, klen)); 2176 return 1; 2177 } 2178 2179 static int magnet_envvar_set(lua_State *L) { 2180 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 2181 const_buffer key = magnet_checkconstbuffer(L, 2); 2182 if (__builtin_expect( (lua_isnil(L, 3)), 0)) { 2183 buffer * const v = http_header_env_get(r, key.ptr, key.len); 2184 if (v) buffer_clear(v); /*(unset)*/ 2185 return 0; 2186 } 2187 const_buffer val = magnet_checkconstbuffer(L, 3); 2188 http_header_env_set(r, key.ptr, key.len, val.ptr, val.len); 2189 return 0; 2190 } 2191 2192 static int magnet_envvar_pairs(lua_State *L) { 2193 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 2194 return magnet_array_pairs(L, &r->env); 2195 } 2196 2197 2198 static int magnet_respbody_add(lua_State *L) { 2199 request_st * const r = lua_touserdata(L, lua_upvalueindex(1)); 2200 if (lua_isstring(L, -1)) { 2201 const_buffer data = magnet_checkconstbuffer(L, -1); 2202 http_chunk_append_mem(r, data.ptr, data.len); 2203 return 1; /* boolean true */ 2204 } 2205 else if (!lua_istable(L, -1)) 2206 return 0; /* boolean false */ 2207 2208 /* note: differs from magnet_attach_content(); 2209 * magnet_attach_content() has misnamed 'length' param which 2210 * is treated as 0-offset pos one after end of range to send. 2211 * Here, 'length' means 'length', as one would expect */ 2212 for (int i=1, end=0, n=(int)lua_rawlen(L,-1); !end && i <= n; ++i) { 2213 lua_rawgeti(L, -1, i); 2214 2215 if (lua_isstring(L, -1)) { 2216 const_buffer data = magnet_checkconstbuffer(L, -1); 2217 http_chunk_append_mem(r, data.ptr, data.len); 2218 } 2219 else if (lua_istable(L, -1)) { 2220 lua_getfield(L, -1, "filename"); 2221 lua_getfield(L, -2, "length"); 2222 lua_getfield(L, -3, "offset"); 2223 2224 if (lua_isstring(L, -3)) { /* filename has to be a string */ 2225 off_t off = (off_t) luaL_optinteger(L, -1, 0); 2226 off_t len = (off_t) luaL_optinteger(L, -2, -1); 2227 /*(-1 len as flag to use file size minus offset (below))*/ 2228 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/ 2229 const buffer * const fn = magnet_checkbuffer(L, -3, &stor); 2230 stat_cache_entry * const sce = (!buffer_is_blank(fn)) 2231 ? stat_cache_get_entry_open(fn, r->conf.follow_symlink) 2232 : NULL; 2233 if (sce && (sce->fd >= 0 || sce->st.st_size == 0)) { 2234 /* treat negative offset as bytes from end of file */ 2235 /* treat negative len as bytes from offset to end of file */ 2236 if (off > sce->st.st_size) 2237 off = sce->st.st_size; 2238 else if (off < 0) { 2239 off = sce->st.st_size - off; 2240 if (off < 0) off = 0; 2241 } 2242 if (len < 0 || sce->st.st_size - off < len) 2243 len = sce->st.st_size - off; 2244 if (len) 2245 http_chunk_append_file_ref_range(r, sce, off, len); 2246 } 2247 else { 2248 log_error(r->conf.errh, __FILE__, __LINE__, 2249 "error opening file '%s'", fn->ptr); 2250 end = 1; 2251 } 2252 } 2253 else { 2254 log_error(r->conf.errh, __FILE__, __LINE__, 2255 "body[%d] table field \"filename\" must be a string", i); 2256 end = 1; 2257 } 2258 2259 lua_pop(L, 3); 2260 } 2261 else if (lua_isnil(L, -1)) { /* end of list */ 2262 end = 1; 2263 } 2264 else { 2265 log_error(r->conf.errh, __FILE__, __LINE__, 2266 "body[%d] is neither a string nor a table", i); 2267 end = 1; 2268 } 2269 2270 lua_pop(L, 1); /* pop the content[...] entry value */ 2271 } 2272 2273 return 1; /* boolean true */ 2274 } 2275 2276 2277 static int magnet_respbody(lua_State *L) { 2278 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 2279 size_t klen; 2280 const char * const k = luaL_checklstring(L, 2, &klen); 2281 switch (k[0]) { 2282 case 'a': /* add; r.resp_body.add */ 2283 if (k[1] == 'd' && k[2] == 'd' && k[3] == '\0') { 2284 lua_pushlightuserdata(L, r); 2285 lua_pushcclosure(L, magnet_respbody_add, 1); 2286 return 1; 2287 } 2288 break; 2289 case 'b': 2290 if (klen == 8 && 0 == memcmp(k, "bytes_in", 8)) { 2291 lua_pushinteger(L, r->write_queue.bytes_in); 2292 return 1; 2293 } 2294 if (klen == 9 && 0 == memcmp(k, "bytes_out", 9)) { 2295 lua_pushinteger(L, r->write_queue.bytes_out); 2296 return 1; 2297 } 2298 break; 2299 #if 0 /*(future: provide pairs() interface to iterate over chunkqueue)*/ 2300 /*(might convert chunks into table of strings, {filename="..."})*/ 2301 /*(what about c->offset into chunk?)*/ 2302 #endif 2303 case 'g': /* get; r.resp_body.get */ 2304 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') { 2305 if (r->resp_body_finished) { 2306 chunkqueue * const cq = &r->write_queue; 2307 chunkqueue_length(cq) 2308 ? magnet_push_buffer(L, 2309 chunkqueue_read_squash(cq,r->conf.errh)) 2310 : (void)lua_pushlstring(L, "", 0); 2311 } 2312 else 2313 lua_pushnil(L); /*(?maybe return -1 instead if len unknown?)*/ 2314 return 1; 2315 } 2316 break; 2317 case 'l': /* len; r.resp_body.len */ 2318 if (k[1] == 'e' && k[2] == 'n' && k[3] == '\0') { 2319 if (r->resp_body_finished) 2320 lua_pushinteger(L, chunkqueue_length(&r->write_queue)); 2321 else 2322 lua_pushnil(L); /*(?maybe return -1 instead if len unknown?)*/ 2323 return 1; 2324 } 2325 break; 2326 case 's': /* set; r.resp_body.set */ 2327 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') { 2328 http_response_body_clear(r, 0); /* clear respbody, then add */ 2329 lua_pushlightuserdata(L, r); 2330 lua_pushcclosure(L, magnet_respbody_add, 1); 2331 return 1; 2332 } 2333 break; 2334 default: 2335 break; 2336 } 2337 lua_pushliteral(L, "r.resp_body invalid method or param"); 2338 lua_error(L); 2339 return 0; 2340 } 2341 2342 2343 static int magnet_reqbody_add(lua_State *L) { 2344 request_st * const r = lua_touserdata(L, lua_upvalueindex(1)); 2345 chunkqueue * const cq = &r->reqbody_queue; 2346 const int tempfile = (cq->last && cq->last->file.is_temp); 2347 if (lua_isstring(L, -1)) { 2348 const_buffer data = magnet_checkconstbuffer(L, -1); 2349 r->reqbody_length += data.len; 2350 if (r->reqbody_length <= 65536 && !tempfile) 2351 chunkqueue_append_mem(cq, data.ptr, data.len); 2352 else if (chunkqueue_append_mem_to_tempfile(cq, data.ptr, data.len, 2353 r->conf.errh)) 2354 return 0; /* boolean false */ 2355 return 1; /* boolean true */ 2356 } 2357 else if (!lua_istable(L, -1)) 2358 return 0; /* boolean false */ 2359 2360 for (int i=1, end=0, n=(int)lua_rawlen(L,-1); !end && i <= n; ++i) { 2361 lua_rawgeti(L, -1, i); 2362 2363 if (lua_isstring(L, -1)) { 2364 const_buffer data = magnet_checkconstbuffer(L, -1); 2365 r->reqbody_length += data.len; 2366 if (r->reqbody_length <= 65536 && !tempfile) 2367 chunkqueue_append_mem(cq, data.ptr, data.len); 2368 else if (chunkqueue_append_mem_to_tempfile(cq, data.ptr, data.len, 2369 r->conf.errh)) 2370 return 0; /* boolean false */ 2371 } 2372 else if (lua_isnil(L, -1)) { /* end of list */ 2373 end = 1; 2374 } 2375 else { 2376 log_error(r->conf.errh, __FILE__, __LINE__, 2377 "body[%d] table must contain strings", i); 2378 end = 1; 2379 } 2380 2381 lua_pop(L, 1); /* pop the content[...] entry value */ 2382 } 2383 2384 return 1; /* boolean true */ 2385 } 2386 2387 2388 static int magnet_reqbody(lua_State *L) { 2389 request_st * const r = **(request_st ***)lua_touserdata(L, 1); 2390 size_t klen; 2391 const char * const k = luaL_checklstring(L, 2, &klen); 2392 switch (k[0]) { 2393 case 'a': /* add; r.req_body.add */ 2394 if (k[1] == 'd' && k[2] == 'd' && k[3] == '\0') { 2395 chunkqueue * const cq = &r->reqbody_queue; 2396 if (cq->bytes_in == (off_t)r->reqbody_length) { 2397 lua_pushlightuserdata(L, r); 2398 lua_pushcclosure(L, magnet_reqbody_add, 1); 2399 } 2400 else /* reqbody not yet collected */ 2401 lua_pushnil(L); 2402 return 1; 2403 } 2404 break; 2405 case 'b': 2406 if (klen == 8 && 0 == memcmp(k, "bytes_in", 8)) { 2407 lua_pushinteger(L, r->reqbody_queue.bytes_in); 2408 return 1; 2409 } 2410 if (klen == 9 && 0 == memcmp(k, "bytes_out", 9)) { 2411 lua_pushinteger(L, r->reqbody_queue.bytes_out); 2412 return 1; 2413 } 2414 break; 2415 case 'c': /* collect; r.req_body.collect */ 2416 if (klen == 7 && 0 == memcmp(k, "collect", 7)) { 2417 chunkqueue * const cq = &r->reqbody_queue; 2418 if (cq->bytes_in == (off_t)r->reqbody_length) 2419 lua_pushboolean(L, 1); 2420 else if (NULL == r->handler_module) { 2421 r->conf.stream_request_body &= 2422 ~(FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN); 2423 r->handler_module = plugin_data_singleton->self; 2424 lua_pushboolean(L, 0); 2425 } 2426 else if (0 == strcmp(r->handler_module->name, "security3")) { 2427 /*(mod_security3 uses similar technique to collect req body)*/ 2428 lua_pushboolean(L, 0); 2429 } 2430 else { 2431 log_error(r->conf.errh, __FILE__, __LINE__, 2432 "unable to collect request body (handler already set); " 2433 "(perhaps load mod_magnet earlier in server.modules, " 2434 "before mod_%s; or require r.req_env['REMOTE_USER'] before " 2435 "attempting r.req_body.collect?)", r->handler_module->name); 2436 lua_pushnil(L); 2437 } 2438 return 1; 2439 } 2440 break; 2441 case 'g': /* get; r.req_body.get */ 2442 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') { 2443 chunkqueue * const cq = &r->reqbody_queue; 2444 if (cq->bytes_in == (off_t)r->reqbody_length) 2445 chunkqueue_length(cq) 2446 ? magnet_push_buffer(L, 2447 chunkqueue_read_squash(cq, r->conf.errh)) 2448 : (void)lua_pushlstring(L, "", 0); 2449 else 2450 lua_pushnil(L); /*(?maybe return -1 instead if len unknown?)*/ 2451 return 1; 2452 } 2453 break; 2454 case 'l': /* len */ 2455 if (k[1] == 'e' && k[2] == 'n' && k[3] == '\0') { 2456 lua_pushinteger(L, r->reqbody_length); 2457 return 1; 2458 } 2459 break; 2460 case 's': /* set; r.req_body.set */ 2461 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') { 2462 chunkqueue * const cq = &r->reqbody_queue; 2463 if (cq->bytes_in == (off_t)r->reqbody_length) { 2464 r->reqbody_length = 0; 2465 chunkqueue_reset(&r->reqbody_queue); 2466 lua_pushlightuserdata(L, r); 2467 lua_pushcclosure(L, magnet_reqbody_add, 1); 2468 } 2469 else /* reqbody not yet collected */ 2470 lua_pushnil(L); 2471 return 1; 2472 } 2473 break; 2474 default: 2475 break; 2476 } 2477 lua_pushliteral(L, "r.req_body invalid method or param"); 2478 lua_error(L); 2479 return 0; 2480 } 2481 2482 2483 __attribute_cold__ 2484 static int magnet_lighty_result_get(lua_State *L) { 2485 /* __index: param 1 is the lighty table the value was not found in */ 2486 lua_pushvalue(L, 2); 2487 lua_rawget(L, lua_upvalueindex(1)); 2488 if (lua_isnil(L, -1)) { 2489 const_buffer k = magnet_checkconstbuffer(L, 2); 2490 if ( (k.len == 6 && 0 == memcmp(k.ptr, "header", 6)) 2491 || (k.len == 7 && 0 == memcmp(k.ptr, "content", 7))) { 2492 lua_pop(L, 1); /* pop nil */ 2493 lua_createtable(L, 0, 0); /* create "header","content" on demand */ 2494 lua_pushvalue(L, 2); /* k: "header" or "content" */ 2495 lua_pushvalue(L, -2); /* v: table */ 2496 lua_rawset(L, lua_upvalueindex(1)); /* set in result table */ 2497 } 2498 } 2499 return 1; 2500 } 2501 2502 __attribute_cold__ 2503 static int magnet_lighty_result_set(lua_State *L) { 2504 /* __newindex: param 1 is lighty table the value is supposed to be set in */ 2505 /* assign value to alternate table; replacing existing value, if any */ 2506 lua_rawset(L, lua_upvalueindex(1)); /* set in result table */ 2507 return 0; 2508 } 2509 2510 2511 __attribute_cold__ 2512 __attribute_noinline__ 2513 static void magnet_copy_response_header(lua_State * const L, request_st * const r) { 2514 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { 2515 if (lua_isstring(L, -1) && lua_isstring(L, -2)) 2516 magnet_resphdr_set_kv(L, r); 2517 } 2518 } 2519 2520 /** 2521 * (deprecated API) 2522 * walk through the content array set by lua script, e.g. 2523 * lighty.header["Content-Type"] = "text/html" 2524 * lighty.content = 2525 * { "<html><body><pre>", { file = "/content" } , "</pre></body></html>" } 2526 * return 200 2527 */ 2528 __attribute_cold__ 2529 __attribute_noinline__ 2530 static void magnet_attach_content(lua_State * const L, request_st * const r) { 2531 http_response_body_clear(r, 0); 2532 for (int i=1, end=0, n=(int)lua_rawlen(L,-1); !end && i <= n; ++i) { 2533 lua_rawgeti(L, -1, i); 2534 2535 /* -1 is the value and should be the value ... aka a table */ 2536 if (lua_isstring(L, -1)) { 2537 const_buffer data = magnet_checkconstbuffer(L, -1); 2538 http_chunk_append_mem(r, data.ptr, data.len); 2539 } else if (lua_istable(L, -1)) { 2540 lua_getfield(L, -1, "filename"); 2541 lua_getfield(L, -2, "length"); /* (0-based) end of range (not actually "length") */ 2542 lua_getfield(L, -3, "offset"); /* (0-based) start of range */ 2543 2544 if (lua_isstring(L, -3)) { /* filename has to be a string */ 2545 /*(luaL_optinteger might raise error, which we want to avoid)*/ 2546 /*off_t off = (off_t) luaL_optinteger(L, -1, 0);*/ 2547 /*off_t len = (off_t) luaL_optinteger(L, -2, -1);*/ /*(-1 as flag to use file size minus offset (below))*/ 2548 int isnum = 1; 2549 off_t off = lua_isnil(L, -1) ? 0 : (off_t) lua_tointegerx(L, -1, &isnum); 2550 if (!isnum) { 2551 off = 0; 2552 log_error(r->conf.errh, __FILE__, __LINE__, 2553 "content[%d] is a table and field \"offset\" must be an integer", i); 2554 } 2555 isnum = 1; 2556 off_t len = lua_isnil(L, -2) ? -1 : (off_t) lua_tointegerx(L, -2, &isnum); 2557 /*(-1 len as flag to use file size minus offset (below))*/ 2558 if (!isnum) { 2559 len = -1; 2560 log_error(r->conf.errh, __FILE__, __LINE__, 2561 "content[%d] is a table and field \"length\" must be an integer", i); 2562 } 2563 if (off < 0) { 2564 log_error(r->conf.errh, __FILE__, __LINE__, 2565 "offset for '%s' is negative", lua_tostring(L, -3)); 2566 end = 1; 2567 } else if (len >= off) { 2568 len -= off; 2569 } else if (-1 != len) { 2570 log_error(r->conf.errh, __FILE__, __LINE__, 2571 "offset > length for '%s'", lua_tostring(L, -3)); 2572 end = 1; 2573 } 2574 2575 if (!end && 0 != len) { 2576 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/ 2577 const buffer * const fn = magnet_checkbuffer(L, -3, &stor); 2578 stat_cache_entry * const sce = (!buffer_is_blank(fn)) 2579 ? stat_cache_get_entry_open(fn, r->conf.follow_symlink) 2580 : NULL; 2581 if (sce && (sce->fd >= 0 || sce->st.st_size == 0)) { 2582 if (len == -1 || sce->st.st_size - off < len) 2583 len = sce->st.st_size - off; 2584 if (len > 0) 2585 http_chunk_append_file_ref_range(r, sce, off, len); 2586 } else { 2587 log_error(r->conf.errh, __FILE__, __LINE__, 2588 "error opening file content '%s' at offset %lld", 2589 lua_tostring(L, -3), (long long)off); 2590 end = 1; 2591 } 2592 } 2593 } else { 2594 log_error(r->conf.errh, __FILE__, __LINE__, 2595 "content[%d] is a table and field \"filename\" must be a string", i); 2596 end = 1; 2597 } 2598 2599 lua_pop(L, 3); 2600 } else if (lua_isnil(L, -1)) { 2601 /* end of list */ 2602 end = 1; 2603 } else { 2604 log_error(r->conf.errh, __FILE__, __LINE__, 2605 "content[%d] is neither a string nor a table", i); 2606 end = 1; 2607 } 2608 2609 lua_pop(L, 1); /* pop the content[...] entry value */ 2610 } 2611 } 2612 2613 __attribute_cold__ 2614 static void magnet_mainenv_metatable(lua_State * const L) { 2615 if (luaL_newmetatable(L, "li.mainenv")) { /* (sp += 1) */ 2616 lua_pushglobaltable(L); /* (sp += 1) */ 2617 lua_setfield(L, -2, "__index"); /* { __index = _G } (sp -= 1) */ 2618 lua_pushboolean(L, 0); /* (sp += 1) */ 2619 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2620 } 2621 } 2622 2623 static void 2624 magnet_request_userdata_method (lua_State * const L, request_st ** const rr, const char *meta) 2625 { 2626 /*(meta is name of cached metatable; meta must start w/ "li." prefix)*/ 2627 *(request_st ***)lua_newuserdata0(L, sizeof(request_st **)) = rr; 2628 #ifdef __COVERITY__ /* shut up coverity; read the comment below */ 2629 if (luaL_newmetatable(L, meta)) { } 2630 #else 2631 luaL_newmetatable(L, meta); /*(should not fail; init'd in script setup)*/ 2632 #endif 2633 lua_setmetatable(L, -2); 2634 lua_setfield(L, -2, meta+3); /*(meta+3 to skip over "li." prefix)*/ 2635 } 2636 2637 static void 2638 magnet_request_table (lua_State * const L, request_st ** const rr) 2639 { 2640 /* r table 2641 * 2642 * r.req_header[] HTTP request headers 2643 * r.req_attr[] HTTP request attributes / components (strings) 2644 * r.req_item[] HTTP request items (struct members, statistics) 2645 * r.req_env[] HTTP request environment variables 2646 * r.req_body.* HTTP request body accessors 2647 * r.req_body.bytes_in HTTP request body chunkqueue bytes_in 2648 * r.req_body.bytes_out HTTP request body chunkqueue bytes_out 2649 * r.resp_header[] HTTP response headers 2650 * r.resp_body.* HTTP response body accessors 2651 * r.resp_body.len HTTP response body length 2652 * r.resp_body.add() HTTP response body add (string or table) 2653 * r.resp_body.set() HTTP response body set (string or table) 2654 * r.resp_body.bytes_in HTTP response body chunkqueue bytes_in 2655 * r.resp_body.bytes_out HTTP response body chunkqueue bytes_out 2656 */ 2657 lua_createtable(L, 0, 7); /* (sp += 1) */ 2658 2659 /* userdata methods share ptr-ptr to external object userdata rr 2660 * (similar functionality to that provided w/ luaL_setfuncs() in lua 5.2+)*/ 2661 magnet_request_userdata_method(L, rr, "li.req_header"); /* req_header */ 2662 magnet_request_userdata_method(L, rr, "li.req_attr"); /* req_attr */ 2663 magnet_request_userdata_method(L, rr, "li.req_item"); /* req_item */ 2664 magnet_request_userdata_method(L, rr, "li.req_env"); /* req_env */ 2665 magnet_request_userdata_method(L, rr, "li.resp_header");/* resp_header */ 2666 magnet_request_userdata_method(L, rr, "li.resp_body"); /* resp_body */ 2667 magnet_request_userdata_method(L, rr, "li.req_body"); /* req_body */ 2668 2669 lua_createtable(L, 0, 2); /* metatable for r table (sp += 1) */ 2670 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 2671 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2672 lua_pushboolean(L, 0); /* (sp += 1) */ 2673 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2674 lua_setmetatable(L, -2); /* tie the metatable to r (sp -= 1) */ 2675 } 2676 2677 static int 2678 magnet_request_iter (lua_State *L) 2679 { 2680 /* upvalue 1: (connection *) in linked list 2681 * upvalue 2: index into h2con->r[] 2682 * upvalue 3: request userdata 2683 * upvalue 4: request table (references r in userdata) */ 2684 connection *con = lua_touserdata(L, lua_upvalueindex(1)); 2685 2686 /* skip over HTTP/2 connections with no active requests */ 2687 while (con && con->h2 && 0 == con->h2->rused) 2688 con = con->next; 2689 if (NULL == con) 2690 return 0; 2691 2692 /* set (request_st *)r */ 2693 int32_t i = -1; 2694 if (con->h2) { 2695 /* get index into h2con->r[] */ 2696 i = lua_tointeger(L, lua_upvalueindex(2)); 2697 /* set (request_st *)r in userdata */ 2698 if (-1 == i) 2699 *(request_st **)lua_touserdata(L,lua_upvalueindex(3))=&con->request; 2700 else 2701 *(request_st **)lua_touserdata(L,lua_upvalueindex(3))=con->h2->r[i]; 2702 /* step to next index into h2con->r[] */ 2703 if ((uint32_t)++i == con->h2->rused) 2704 i = -1; 2705 lua_pushinteger(L, i); 2706 lua_replace(L, lua_upvalueindex(2)); 2707 } 2708 else { 2709 /* set (request_st *) in userdata */ 2710 *(request_st **)lua_touserdata(L, lua_upvalueindex(3)) = &con->request; 2711 } 2712 2713 if (-1 == i) { 2714 /* step to next connection */ 2715 con = con->next; 2716 lua_pushlightuserdata(L, con); 2717 lua_replace(L, lua_upvalueindex(1)); 2718 } 2719 2720 /* return request object (which references (request_st *)r in userdata) */ 2721 lua_pushvalue(L, lua_upvalueindex(4)); 2722 return 1; 2723 } 2724 2725 static int 2726 magnet_irequests (lua_State *L) 2727 { 2728 /* NB: iterator request object *is invalid* outside of iteration 2729 * For efficiency, r is stored in userdata as upvalue to iteration 2730 * and is invalid (and may be cleaned up) outside of iterator loop. 2731 * A C pointer into userdata is stored in iterator request object methods. 2732 * The iterator request object is *reused* for each iteration loop and the 2733 * upvalue to the userdata is changed each iteration to point to next r. 2734 * The iterator request object *must not* be saved for use outside 2735 * the iteration loop. Extract data that must be saved and store data 2736 * in a persistent object if data is to be used outside iterator loop. 2737 * (Were it desirable to produce a persistent request object for use outside 2738 * the iteration loop, a future enhancement would be to add a method such 2739 * as lighty.server.irequests_clone(r) which creates a new request object 2740 * pointing into a new userdata, and saving that new userdata in the new 2741 * request object table as '_r_userdata') 2742 * NB: iterator request objects should generally be treated read-only. 2743 * Modifications may in some cases be unsafe and cause lighttpd to crash. */ 2744 request_st *r = magnet_get_request(L); 2745 lua_pushlightuserdata(L, r->con->srv->conns); 2746 lua_pushinteger(L, -1); 2747 request_st ** const r_userdata = 2748 (request_st **)lua_newuserdata0(L, sizeof(request_st *)); 2749 magnet_request_table(L, r_userdata); 2750 lua_pushcclosure(L, magnet_request_iter, 4); 2751 return 1; 2752 } 2753 2754 static int 2755 magnet_server_stats_get (lua_State *L) 2756 { 2757 size_t klen; 2758 const char * const k = luaL_checklstring(L, 2, &klen); 2759 const request_st * const r = magnet_get_request(L); 2760 const server * const srv = r->con->srv; 2761 switch (klen) { 2762 case 6: 2763 if (0 == memcmp(k, "uptime", 6)) { 2764 lua_pushinteger(L, (lua_Integer)(log_epoch_secs - srv->startup_ts)); 2765 return 1; 2766 } 2767 break; 2768 case 7: 2769 if (0 == memcmp(k, "version", 7)) { 2770 lua_pushlstring(L, BUF_PTR_LEN(srv->default_server_tag)); 2771 return 1; 2772 } 2773 break; 2774 case 12: 2775 /*(could calculate from irequests: ++count on remote-addr/port change)*/ 2776 if (0 == memcmp(k, "clients_open", 12)) { 2777 lua_pushinteger(L, (lua_Integer) 2778 (srv->srvconf.max_conns - srv->lim_conns)); 2779 return 1; 2780 } 2781 break; 2782 default: 2783 break; 2784 } 2785 return luaL_error(L, "server.stats['%s'] invalid", k); 2786 } 2787 2788 2789 __attribute_cold__ 2790 static void 2791 magnet_req_header_metatable (lua_State * const L) 2792 { 2793 if (luaL_newmetatable(L, "li.req_header") == 0) /* (sp += 1) */ 2794 return; 2795 2796 lua_pushcfunction(L, magnet_reqhdr_get); /* (sp += 1) */ 2797 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2798 lua_pushcfunction(L, magnet_reqhdr_set); /* (sp += 1) */ 2799 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2800 lua_pushcfunction(L, magnet_reqhdr_pairs); /* (sp += 1) */ 2801 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2802 lua_pushboolean(L, 0); /* (sp += 1) */ 2803 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2804 } 2805 2806 __attribute_cold__ 2807 static void 2808 magnet_req_attr_metatable (lua_State * const L) 2809 { 2810 if (luaL_newmetatable(L, "li.req_attr") == 0) /* (sp += 1) */ 2811 return; 2812 2813 lua_pushcfunction(L, magnet_env_get); /* (sp += 1) */ 2814 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2815 lua_pushcfunction(L, magnet_env_set); /* (sp += 1) */ 2816 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2817 lua_pushcfunction(L, magnet_env_pairs); /* (sp += 1) */ 2818 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2819 lua_pushboolean(L, 0); /* (sp += 1) */ 2820 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2821 } 2822 2823 __attribute_cold__ 2824 static void 2825 magnet_req_item_metatable (lua_State * const L) 2826 { 2827 if (luaL_newmetatable(L, "li.req_item") == 0) /* (sp += 1) */ 2828 return; 2829 2830 lua_pushcfunction(L, magnet_req_item_get); /* (sp += 1) */ 2831 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2832 lua_pushcfunction(L, magnet_req_item_set); /* (sp += 1) */ 2833 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2834 lua_pushboolean(L, 0); /* (sp += 1) */ 2835 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2836 } 2837 2838 __attribute_cold__ 2839 static void 2840 magnet_req_env_metatable (lua_State * const L) 2841 { 2842 if (luaL_newmetatable(L, "li.req_env") == 0) /* (sp += 1) */ 2843 return; 2844 2845 lua_pushcfunction(L, magnet_envvar_get); /* (sp += 1) */ 2846 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2847 lua_pushcfunction(L, magnet_envvar_set); /* (sp += 1) */ 2848 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2849 lua_pushcfunction(L, magnet_envvar_pairs); /* (sp += 1) */ 2850 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2851 lua_pushboolean(L, 0); /* (sp += 1) */ 2852 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2853 } 2854 2855 __attribute_cold__ 2856 static void 2857 magnet_resp_header_metatable (lua_State * const L) 2858 { 2859 if (luaL_newmetatable(L, "li.resp_header") == 0) /* (sp += 1) */ 2860 return; 2861 2862 lua_pushcfunction(L, magnet_resphdr_get); /* (sp += 1) */ 2863 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2864 lua_pushcfunction(L, magnet_resphdr_set); /* (sp += 1) */ 2865 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2866 lua_pushcfunction(L, magnet_resphdr_pairs); /* (sp += 1) */ 2867 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2868 lua_pushboolean(L, 0); /* (sp += 1) */ 2869 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2870 } 2871 2872 __attribute_cold__ 2873 static void 2874 magnet_resp_body_metatable (lua_State * const L) 2875 { 2876 if (luaL_newmetatable(L, "li.resp_body") == 0) /* (sp += 1) */ 2877 return; 2878 2879 lua_pushcfunction(L, magnet_respbody); /* (sp += 1) */ 2880 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2881 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 2882 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2883 lua_pushboolean(L, 0); /* (sp += 1) */ 2884 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2885 } 2886 2887 __attribute_cold__ 2888 static void 2889 magnet_req_body_metatable (lua_State * const L) 2890 { 2891 if (luaL_newmetatable(L, "li.req_body") == 0) /* (sp += 1) */ 2892 return; 2893 2894 lua_pushcfunction(L, magnet_reqbody); /* (sp += 1) */ 2895 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2896 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 2897 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2898 lua_pushboolean(L, 0); /* (sp += 1) */ 2899 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2900 } 2901 2902 __attribute_cold__ 2903 static int magnet_atpanic(lua_State *L) { 2904 request_st * const r = magnet_get_request(L); 2905 log_error(r->conf.errh, __FILE__, __LINE__, "(lua-atpanic) %s", 2906 lua_isstring(L, 1) ? lua_tostring(L, 1) : ""); 2907 /*longjmp(exceptionjmp, 1);*//*(must be init with setjmp() elsewhere)*/ 2908 return 0; 2909 } 2910 2911 __attribute_cold__ 2912 static int magnet_print(lua_State *L) { 2913 const_buffer cb = magnet_checkconstbuffer(L, 1); 2914 request_st * const r = magnet_get_request(L); 2915 log_error(r->conf.errh, __FILE__, __LINE__, "(lua-print) %s", cb.ptr); 2916 return 0; 2917 } 2918 2919 __attribute_cold__ 2920 static void 2921 magnet_plugin_stats_table (lua_State * const L) 2922 { 2923 lua_createtable(L, 0, 0); /* {} (sp += 1) */ 2924 lua_createtable(L, 0, 4); /* metatable for plugin_stats (sp += 1) */ 2925 lua_pushcfunction(L, magnet_plugin_stats_get); /* (sp += 1) */ 2926 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2927 lua_pushcfunction(L, magnet_plugin_stats_set); /* (sp += 1) */ 2928 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2929 lua_pushcfunction(L, magnet_plugin_stats_pairs); /* (sp += 1) */ 2930 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ 2931 lua_pushboolean(L, 0); /* (sp += 1) */ 2932 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2933 lua_setmetatable(L, -2); /* (sp -= 1) */ 2934 } 2935 2936 __attribute_cold__ 2937 static void 2938 magnet_server_stats_table (lua_State * const L) 2939 { 2940 lua_createtable(L, 0, 0); /* {} (sp += 1) */ 2941 lua_createtable(L, 0, 3); /* metatable for stats (sp += 1) */ 2942 lua_pushcfunction(L, magnet_server_stats_get); /* (sp += 1) */ 2943 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 2944 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 2945 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2946 lua_pushboolean(L, 0); /* (sp += 1) */ 2947 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2948 lua_setmetatable(L, -2); /* (sp -= 1) */ 2949 } 2950 2951 __attribute_cold__ 2952 static void 2953 magnet_server_table (lua_State * const L) 2954 { 2955 lua_createtable(L, 0, 3); /* {} (sp += 1) */ 2956 lua_pushcfunction(L, magnet_irequests); /* (sp += 1) */ 2957 lua_setfield(L, -2, "irequests"); /* iterate over requests (sp -= 1) */ 2958 magnet_plugin_stats_table(L); /* (sp += 1) */ 2959 lua_setfield(L, -2, "plugin_stats"); /* (sp -= 1) */ 2960 magnet_server_stats_table(L); /* (sp += 1) */ 2961 lua_setfield(L, -2, "stats"); /* (sp -= 1) */ 2962 lua_createtable(L, 0, 2); /* metatable for server table (sp += 1) */ 2963 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 2964 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 2965 lua_pushboolean(L, 0); /* (sp += 1) */ 2966 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 2967 lua_setmetatable(L, -2); /* tie the metatable to server (sp -= 1) */ 2968 } 2969 2970 __attribute_cold__ 2971 static void 2972 magnet_script_setup_global_state (lua_State * const L) 2973 { 2974 lua_atpanic(L, magnet_atpanic); 2975 2976 lua_pushglobaltable(L); /* (sp += 1) */ 2977 /* override default print() function */ 2978 lua_pushcfunction(L, magnet_print); /* (sp += 1) */ 2979 lua_setfield(L, -2, "print"); /* (sp -= 1) */ 2980 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 2981 /* override default pairs() function to our __pairs capable version */ 2982 lua_getglobal(L, "pairs"); /* push original pairs() (sp += 1) */ 2983 lua_pushcclosure(L, magnet_pairs, 1); /* (sp -= 1; sp += 1) */ 2984 lua_setfield(L, -2, "pairs"); /* (sp -= 1) */ 2985 #endif 2986 lua_pop(L, 1); /* pop global table */ /* (sp -= 1) */ 2987 2988 magnet_req_header_metatable(L); /* init for mem locality (sp += 1) */ 2989 magnet_req_attr_metatable(L); /* init for mem locality (sp += 1) */ 2990 magnet_req_item_metatable(L); /* init for mem locality (sp += 1) */ 2991 magnet_req_env_metatable(L); /* init for mem locality (sp += 1) */ 2992 magnet_resp_header_metatable(L); /* init for mem locality (sp += 1) */ 2993 magnet_resp_body_metatable(L); /* init for mem locality (sp += 1) */ 2994 magnet_req_body_metatable(L); /* init for mem locality (sp += 1) */ 2995 magnet_stat_metatable(L); /* init table for mem locality (sp += 1) */ 2996 magnet_readdir_metatable(L); /* init table for mem locality (sp += 1) */ 2997 lua_pop(L, 9); /* pop init'd metatables (sp -= 9) */ 2998 } 2999 3000 __attribute_cold__ 3001 static void 3002 magnet_init_lighty_table (lua_State * const L, request_st **rr, 3003 const int result_ndx) 3004 { 3005 /* lighty table 3006 * 3007 * lighty.r.* HTTP request object methods 3008 * lighty.c.* lighttpd C methods callable from lua 3009 * lighty.server.* lighttpd server object methods 3010 * 3011 * (older interface) 3012 * 3013 * lighty.request[] HTTP request headers 3014 * lighty.req_env[] environment variables 3015 * lighty.env[] lighttpd request metadata, 3016 * various url components, 3017 * physical file paths; 3018 * might contain nil values 3019 * 3020 * lighty.header[] (script) HTTP response headers 3021 * lighty.content[] (script) HTTP response body (table of string/file) 3022 * 3023 * lighty.status[] lighttpd status counters 3024 */ 3025 3026 /*(adjust the preallocation if more entries are added)*/ 3027 lua_createtable(L, 0, 9); /* lighty.* (returned on stack) (sp += 1) */ 3028 3029 magnet_request_table(L, rr); /* lighty.r (sp += 1) */ 3030 lua_setfield(L, -2, "r"); /* lighty.r = {} (sp -= 1) */ 3031 3032 magnet_server_table(L); /* lighty.server (sp += 1) */ 3033 lua_setfield(L, -2, "server"); /* server = {} (sp -= 1) */ 3034 3035 /* compatibility with previous mod_magnet interfaces in top of lighty.* */ 3036 lua_getfield(L, -1, "r"); /* (sp += 1) */ 3037 /* alias lighty.request -> lighty.r.req_header */ 3038 lua_getfield(L, -1, "req_header"); /* (sp += 1) */ 3039 lua_setfield(L, -3, "request"); /* request = {} (sp -= 1) */ 3040 /* alias lighty.env -> lighty.r.req_attr */ 3041 lua_getfield(L, -1, "req_attr"); /* (sp += 1) */ 3042 lua_setfield(L, -3, "env"); /* env = {} (sp -= 1) */ 3043 /* alias lighty.req_env -> lighty.r.req_env */ 3044 lua_getfield(L, -1, "req_env"); /* (sp += 1) */ 3045 lua_setfield(L, -3, "req_env"); /* req_env = {} (sp -= 1) */ 3046 lua_pop(L, 1); /* (sp -= 1) */ 3047 3048 /* alias lighty.server.stats -> lighty.status */ 3049 lua_getfield(L, -1, "server"); /* (sp += 1) */ 3050 lua_getfield(L, -1, "plugin_stats"); /* (sp += 1) */ 3051 lua_setfield(L, -3, "status"); /* status = {} (sp -= 1) */ 3052 lua_pop(L, 1); /* (sp -= 1) */ 3053 3054 lua_pushinteger(L, MAGNET_RESTART_REQUEST); 3055 lua_setfield(L, -2, "RESTART_REQUEST"); 3056 3057 /* alias lighty.c.stat -> lighty.stat */ 3058 lua_pushcfunction(L, magnet_stat); /* (sp += 1) */ 3059 lua_setfield(L, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */ 3060 3061 static const luaL_Reg cmethods[] = { 3062 { "stat", magnet_stat } 3063 ,{ "time", magnet_time } 3064 ,{ "hrtime", magnet_hrtime } 3065 ,{ "rand", magnet_rand } 3066 ,{ "md", magnet_md_once } /* message digest */ 3067 ,{ "hmac", magnet_hmac_once } /* HMAC */ 3068 ,{ "digest_eq", magnet_digest_eq } /* timing-safe eq fixed len */ 3069 ,{ "secret_eq", magnet_secret_eq } /* timing-safe eq variable len */ 3070 ,{ "b64urldec", magnet_b64urldec } /* validate and decode base64url */ 3071 ,{ "b64urlenc", magnet_b64urlenc } /* base64url encode, no padding */ 3072 ,{ "b64dec", magnet_b64stddec } /* validate and decode base64 */ 3073 ,{ "b64enc", magnet_b64stdenc } /* base64 encode, no padding */ 3074 ,{ "hexdec", magnet_hexdec } /* validate and decode hex str */ 3075 ,{ "hexenc", magnet_hexenc } /* uc; lc w/ lua s = s:lower() */ 3076 ,{ "xmlenc", magnet_xmlenc } /* xml-encode/html-encode: <>&'\" */ 3077 ,{ "urldec", magnet_urldec } /* url-decode (path) */ 3078 ,{ "urlenc", magnet_urlenc } /* url-encode (path) */ 3079 ,{ "urldec_query", magnet_urldec_query } /* url-decode query-string */ 3080 ,{ "urlenc_query", magnet_urlenc_query } /* url-encode query-string */ 3081 ,{ "urlenc_normalize", magnet_urlenc_normalize }/* url-enc normalization */ 3082 ,{ "fspath_simplify", magnet_fspath_simplify } /* simplify fspath */ 3083 ,{ "cookie_tokens", magnet_cookie_tokens } /* parse cookie tokens */ 3084 ,{ "header_tokens", magnet_header_tokens } /* parse header tokens seq */ 3085 ,{ "readdir", magnet_readdir } /* dir walk */ 3086 ,{ "quoteddec", magnet_quoteddec } /* quoted-string decode */ 3087 ,{ "quotedenc", magnet_quotedenc } /* quoted-string encode */ 3088 ,{ "bsdec", magnet_bsdec } /* backspace-escape decode */ 3089 ,{ "bsenc", magnet_bsenc_default } /* backspace-escape encode */ 3090 ,{ "bsenc_json", magnet_bsenc_json } /* backspace-escape encode json */ 3091 ,{ NULL, NULL } 3092 }; 3093 3094 lua_createtable(L, 0, sizeof(cmethods)/sizeof(luaL_Reg)-1);/*(sp += 1) */ 3095 luaL_setfuncs(L, cmethods, 0); 3096 lua_createtable(L, 0, 2); /* metatable for c table (sp += 1) */ 3097 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */ 3098 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 3099 lua_pushboolean(L, 0); /* (sp += 1) */ 3100 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 3101 lua_setmetatable(L, -2); /* tie the metatable to c (sp -= 1) */ 3102 lua_setfield(L, -2, "c"); /* c = {} (sp -= 1) */ 3103 3104 /* lighty.* table is read-only; 3105 * provide alternative scratch table for legacy API, historical (mis)use */ 3106 lua_createtable(L, 0, 3); /* metatable for lighty table (sp += 1) */ 3107 lua_pushvalue(L, result_ndx); /* (sp += 1) */ 3108 lua_pushcclosure(L, magnet_lighty_result_get, 1);/* (sp -= 1; sp += 1) */ 3109 lua_setfield(L, -2, "__index"); /* (sp -= 1) */ 3110 lua_pushvalue(L, result_ndx); /* (sp += 1) */ 3111 lua_pushcclosure(L, magnet_lighty_result_set, 1);/* (sp -= 1; sp += 1) */ 3112 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ 3113 lua_pushboolean(L, 0); /* (sp += 1) */ 3114 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */ 3115 lua_setmetatable(L, -2); /* tie the metatable to lighty (sp -= 1) */ 3116 3117 /* lighty table (returned on stack) */ 3118 } 3119 3120 static void magnet_clear_table(lua_State * const L, int ndx) { 3121 /*(avoid lua_absindex() func call since expecting empty result tables for 3122 * legacy interfaces to lighty.header and .content, though not script-env)*/ 3123 /*ndx = lua_absindex(ndx);*//*(lua 5.2+)*/ 3124 if (ndx < 0) --ndx; 3125 for (lua_pushnil(L); lua_next(L, ndx); ) { 3126 lua_pop(L, 1); 3127 lua_pushvalue(L, -1); 3128 lua_pushnil(L); 3129 lua_rawset(L, ndx < 0 ? ndx - 2 : ndx); 3130 } 3131 } 3132 3133 __attribute_cold__ 3134 static int magnet_traceback(lua_State *L) { 3135 if (!lua_isstring(L, 1)) /* 'message' not a string? */ 3136 return 1; /* keep it intact */ 3137 if (lua_getglobal_and_type(L, "debug") != LUA_TTABLE) { 3138 lua_pop(L, 1); 3139 return 1; 3140 } 3141 if (lua_getfield_and_type(L, -1, "traceback") != LUA_TFUNCTION) { 3142 lua_pop(L, 2); 3143 return 1; 3144 } 3145 lua_pushvalue(L, 1); /* pass error message */ 3146 lua_pushinteger(L, 2); /* skip this function and traceback */ 3147 lua_call(L, 2, 1); /* call debug.traceback */ 3148 return 1; 3149 } 3150 3151 __attribute_cold__ 3152 __attribute_noinline__ 3153 static int 3154 magnet_script_setup (request_st * const r, plugin_data * const p, script * const sc) 3155 { 3156 lua_State * const L = sc->L; 3157 const int func_ndx = 1; 3158 if (lua_isfunction(L, func_ndx)) { 3159 /* initial setup for global lua_State */ 3160 magnet_script_setup_global_state(L); 3161 /*force_assert(lua_gettop(L) == 1);*/ /* errfunc_ndx = 2 */ 3162 lua_pushcfunction(L, magnet_traceback);/*errfunc*//* (sp += 1) */ 3163 /* create empty table for script environment (reused) 3164 * setmetatable({}, {__index = _G}) 3165 * if a function symbol is not defined in our env, 3166 * __index will lookup in the global env. */ 3167 lua_createtable(L, 0, 1); /* env_ndx = 3 */ /* (sp += 1) */ 3168 magnet_mainenv_metatable(L); /* (sp += 1) */ 3169 lua_setmetatable(L, -2); /* (sp -= 1) */ 3170 /* set script env in first upvalue (_ENV upvalue) for func */ 3171 lua_pushvalue(L, -1); /* (sp += 1) */ 3172 magnet_setfenv_mainfn(L, func_ndx); /* (sp -= 1) */ 3173 /* result table (modifiable) (for mod_magnet legacy API) */ 3174 /* (prefer lighty.r.resp_header(), lighty.r.resp_body()) */ 3175 lua_createtable(L, 0, 2); /* (result_ndx = 4) */ /* (sp += 1) */ 3176 const int result_ndx = 4; 3177 /* shared userdata (ud_ndx = 5) */ 3178 request_st ** const r_userdata = /* (sp += 1) */ 3179 (request_st **)lua_newuserdata0(L, sizeof(request_st *)); 3180 /* insert lighty table (lighty_table_ndx = 6) */ 3181 magnet_init_lighty_table(L,r_userdata,result_ndx);/* (sp += 1) */ 3182 return 1; 3183 } 3184 else { 3185 if (lua_isstring(L, func_ndx)) 3186 log_error(r->conf.errh, __FILE__, __LINE__, 3187 "loading script %s failed: %s", sc->name.ptr, 3188 lua_tostring(L, func_ndx)); 3189 else /*(lua_gettop(L) == 0)*/ 3190 log_perror(r->conf.errh, __FILE__, __LINE__, 3191 "loading script %s failed", sc->name.ptr); 3192 lua_settop(L, 0); 3193 3194 if (p->conf.stage >= 0) { /*(before response_start)*/ 3195 r->http_status = 500; 3196 r->handler_module = NULL; 3197 } 3198 3199 return 0; 3200 } 3201 } 3202 3203 static handler_t 3204 magnet_attract (request_st * const r, plugin_data * const p, script * const sc) 3205 { 3206 lua_State * const L = sc->L; 3207 const int func_ndx = 1; 3208 const int errfunc_ndx = 2; 3209 const int env_ndx = 3; 3210 const int result_ndx = 4; 3211 const int ud_ndx = 5; 3212 const int lighty_table_ndx = 6; 3213 3214 if (__builtin_expect( (lua_gettop(L) != lighty_table_ndx), 0)) { 3215 if (!magnet_script_setup(r, p, sc)) 3216 return HANDLER_FINISHED; 3217 } 3218 3219 /* set r in global state for L for out-of-band access to r 3220 * (r-conf.errh and others) */ 3221 magnet_set_request(L, r); 3222 /* set r in userdata shared by lighty.r object methods */ 3223 *(request_st **)lua_touserdata(L, ud_ndx) = r; 3224 3225 /* add lighty table to script-env 3226 * script-env is cleared at the end of each script run */ 3227 lua_pushvalue(L, lighty_table_ndx); /* (sp += 1) */ 3228 lua_setfield(L, env_ndx, "lighty"); /* lighty.* (sp -= 1) */ 3229 3230 /* push script func; pcall will consume the func value */ 3231 lua_pushvalue(L, func_ndx); /* (sp += 1) */ 3232 int ret = lua_pcall(L, 0, 1, errfunc_ndx); /* (sp -= 1; sp += 1) */ 3233 3234 handler_t result = HANDLER_GO_ON; 3235 if (0 != ret) { 3236 size_t errlen; 3237 const char * const err = lua_tolstring(L, -1, &errlen); 3238 log_error_multiline(r->conf.errh, __FILE__, __LINE__, 3239 err, errlen, "lua: "); 3240 /*lua_pop(L, 1);*/ /* pop error msg */ /* defer to later */ 3241 if (p->conf.stage >= 0) { /*(before response_start)*/ 3242 r->http_status = 500; 3243 r->handler_module = NULL; 3244 result = HANDLER_FINISHED; 3245 } 3246 } 3247 else do { 3248 /*(luaL_optinteger might raise error, which we want to avoid)*/ 3249 /*lua_return_value = (int) luaL_optinteger(L, -1, -1);*/ 3250 int isnum = 1; 3251 int lua_return_value = lua_isnil(L, -1) 3252 ? 0 3253 : (int) lua_tointegerx(L, -1, &isnum); 3254 if (!isnum) { 3255 log_error(r->conf.errh, __FILE__, __LINE__, 3256 "lua_pcall(): unexpected non-integer return type: %s", 3257 luaL_typename(L, -1)); 3258 break; 3259 } 3260 /*lua_pop(L, 1);*/ /* pop return value */ /* defer to later */ 3261 /*force_assert(lua_istable(sc->L, -1));*/ 3262 3263 if (lua_getfield_and_type(L, result_ndx, "header") == LUA_TTABLE) { 3264 magnet_copy_response_header(L, r); /* deprecated legacy API */ 3265 } 3266 /*lua_pop(L, 1);*//* defer to later */ 3267 3268 if (lua_return_value >= 200) { 3269 r->http_status = lua_return_value; 3270 /*(note: body may already have been set via lighty.r.resp_body.*)*/ 3271 if (lua_getfield_and_type(L, result_ndx, "content") == LUA_TTABLE) { 3272 magnet_attach_content(L, r); /* deprecated legacy API */ 3273 } 3274 /*lua_pop(L, 1);*//* defer to later */ 3275 if (!chunkqueue_is_empty(&r->write_queue)) { 3276 r->handler_module = p->self; 3277 } 3278 r->resp_body_finished = 1; 3279 result = HANDLER_FINISHED; 3280 } else if (lua_return_value >= 100) { 3281 /*(skip for response-start; send response as-is w/ added headers)*/ 3282 if (p->conf.stage < 0) break; 3283 /*(custom lua code should not return 101 Switching Protocols)*/ 3284 r->http_status = lua_return_value; 3285 result = http_response_send_1xx(r) 3286 ? HANDLER_GO_ON 3287 : HANDLER_ERROR; 3288 } else if (MAGNET_RESTART_REQUEST == lua_return_value) { 3289 /*(could detect restart loops in same way as is done in mod_rewrite, 3290 * but using r->env means that we do not need to reset plugin state 3291 * at end of every request, as is done in mod_rewrite. mod_rewrite 3292 * always restarts the request processing (if request is rewritten), 3293 * whereas mod_magnet can be used in many other ways)*/ 3294 buffer *vb = 3295 http_header_env_get(r, CONST_STR_LEN("_L_MAGNET_RESTART")); 3296 if (NULL == vb) { 3297 vb = 3298 http_header_env_set_ptr(r,CONST_STR_LEN("_L_MAGNET_RESTART")); 3299 buffer_append_char(vb, '0'); 3300 } 3301 result = HANDLER_COMEBACK; 3302 if (++*vb->ptr-'0' >= 10) { 3303 log_error(r->conf.errh, __FILE__, __LINE__, 3304 "too many request restarts (infinite loop?) for %s", 3305 sc->name.ptr); 3306 result = HANDLER_ERROR; 3307 } 3308 } 3309 } while (0); 3310 magnet_clear_table(L, env_ndx); 3311 magnet_clear_table(L, result_ndx); 3312 /* reset stack to reuse stack up to lighty table; pop the excess */ 3313 lua_settop(L, lighty_table_ndx); /*(handle deferred lua_pop()s)*/ 3314 return result; 3315 } 3316 3317 static handler_t magnet_attract_array(request_st * const r, plugin_data * const p, int stage) { 3318 mod_magnet_patch_config(r, p); 3319 p->conf.stage = stage; 3320 3321 script * const *scripts; 3322 switch (stage) { 3323 case 1: scripts = p->conf.url_raw; break; 3324 case 0: scripts = p->conf.physical_path; break; 3325 case -1: scripts = p->conf.response_start; break; 3326 default: scripts = NULL; break; 3327 } 3328 if (NULL == scripts) return HANDLER_GO_ON; /* no scripts set */ 3329 3330 /*(always check at least mtime and size to trigger script reload)*/ 3331 const int etag_flags = r->conf.etag_flags | ETAG_USE_MTIME | ETAG_USE_SIZE; 3332 int req_env_inited = 0; 3333 3334 /* execute scripts sequentially while HANDLER_GO_ON */ 3335 handler_t rc = HANDLER_GO_ON; 3336 do { 3337 script_cache_check_script(*scripts, etag_flags); 3338 if ((*scripts)->req_env_init && !req_env_inited) { 3339 /*(request env init is deferred until needed)*/ 3340 req_env_inited = 1; 3341 r->con->srv->request_env(r); 3342 } 3343 rc = magnet_attract(r, p, *scripts); 3344 } while (rc == HANDLER_GO_ON && *++scripts); 3345 3346 if (r->error_handler_saved_status) { 3347 /* retrieve (possibly modified) REDIRECT_STATUS and store as number */ 3348 int x; 3349 const buffer * const vb = http_header_env_get(r, CONST_STR_LEN("REDIRECT_STATUS")); 3350 if (vb && (x = http_header_str_to_code(vb->ptr)) != -1) 3351 r->error_handler_saved_status = 3352 r->error_handler_saved_status > 0 ? (int)x : -(int)x; 3353 } 3354 3355 return rc; 3356 } 3357 3358 URIHANDLER_FUNC(mod_magnet_uri_handler) { 3359 return magnet_attract_array(r, p_d, 1); 3360 } 3361 3362 URIHANDLER_FUNC(mod_magnet_physical) { 3363 return magnet_attract_array(r, p_d, 0); 3364 } 3365 3366 URIHANDLER_FUNC(mod_magnet_response_start) { 3367 return magnet_attract_array(r, p_d, -1); 3368 } 3369 3370 SUBREQUEST_FUNC(mod_magnet_handle_subrequest) { 3371 /* read entire request body from network and then restart request */ 3372 UNUSED(p_d); 3373 3374 if (r->state == CON_STATE_READ_POST) { 3375 /*(streaming flags were removed when magnet installed this handler)*/ 3376 handler_t rc = r->con->reqbody_read(r); 3377 if (rc != HANDLER_GO_ON) return rc; 3378 if (r->state == CON_STATE_READ_POST) 3379 return HANDLER_WAIT_FOR_EVENT; 3380 } 3381 3382 buffer_clear(&r->physical.path); 3383 r->handler_module = NULL; 3384 return HANDLER_COMEBACK; 3385 } 3386 3387 3388 __attribute_cold__ 3389 int mod_magnet_plugin_init(plugin *p); 3390 int mod_magnet_plugin_init(plugin *p) { 3391 p->version = LIGHTTPD_VERSION_ID; 3392 p->name = "magnet"; 3393 3394 p->init = mod_magnet_init; 3395 p->handle_uri_clean = mod_magnet_uri_handler; 3396 p->handle_physical = mod_magnet_physical; 3397 p->handle_response_start = mod_magnet_response_start; 3398 p->handle_subrequest = mod_magnet_handle_subrequest; 3399 p->set_defaults = mod_magnet_set_defaults; 3400 p->cleanup = mod_magnet_free; 3401 3402 return 0; 3403 } 3404