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