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