1/* This file is part of the CivetWeb web server. 2 * See https://github.com/civetweb/civetweb/ 3 */ 4 5#if !defined(_WIN32) 6#include <dlfcn.h> 7#endif 8#include "civetweb_lua.h" 9#include "civetweb_private_lua.h" 10 11#if defined(_WIN32) 12static void * 13mmap(void *addr, int64_t len, int prot, int flags, int fd, int offset) 14{ 15 /* TODO (low): This is an incomplete implementation of mmap for windows. 16 * Currently it is sufficient, but there are a lot of unused parameters. 17 * Better use a function "mg_map" which only has the required parameters, 18 * and implement it using mmap in Linux and CreateFileMapping in Windows. 19 * No one should expect a full mmap for Windows here. 20 */ 21 HANDLE fh = (HANDLE)_get_osfhandle(fd); 22 HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0); 23 void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t)len); 24 CloseHandle(mh); 25 26 /* unused parameters */ 27 (void)addr; 28 (void)prot; 29 (void)flags; 30 (void)offset; 31 32 return p; 33} 34 35static void 36munmap(void *addr, int64_t length) 37{ 38 /* unused parameters */ 39 (void)length; 40 41 UnmapViewOfFile(addr); 42} 43 44#define MAP_FAILED (NULL) 45#define MAP_PRIVATE (0) 46#define PROT_READ (0) 47#else 48#include <sys/mman.h> 49#endif 50 51static const char *const LUASOCKET = "luasocket"; 52static const char lua_regkey_ctx = 1; 53static const char lua_regkey_connlist = 2; 54static const char lua_regkey_lsp_include_history = 3; 55static const char *const LUABACKGROUNDPARAMS = "mg"; 56 57/* Limit nesting depth of mg.include. 58 * This takes a lot of stack (~10 kB per recursion), 59 * so do not use a too high limit. */ 60#if !defined(LSP_INCLUDE_MAX_DEPTH) 61#define LSP_INCLUDE_MAX_DEPTH (10) 62#endif 63 64 65/* Forward declarations */ 66static void handle_request(struct mg_connection *); 67static int handle_lsp_request(struct mg_connection *, 68 const char *, 69 struct mg_file *, 70 struct lua_State *); 71 72static void 73reg_lstring(struct lua_State *L, 74 const char *name, 75 const void *buffer, 76 size_t buflen) 77{ 78 if (name != NULL && buffer != NULL) { 79 lua_pushstring(L, name); 80 lua_pushlstring(L, (const char *)buffer, buflen); 81 lua_rawset(L, -3); 82 } 83} 84 85static void 86reg_llstring(struct lua_State *L, 87 const void *buffer1, 88 size_t buflen1, 89 const void *buffer2, 90 size_t buflen2) 91{ 92 if (buffer1 != NULL && buffer2 != NULL) { 93 lua_pushlstring(L, (const char *)buffer1, buflen1); 94 lua_pushlstring(L, (const char *)buffer2, buflen2); 95 lua_rawset(L, -3); 96 } 97} 98 99#define reg_string(L, name, val) \ 100 reg_lstring(L, name, val, val ? strlen(val) : 0) 101 102static void 103reg_int(struct lua_State *L, const char *name, int val) 104{ 105 if (name != NULL) { 106 lua_pushstring(L, name); 107 lua_pushinteger(L, val); 108 lua_rawset(L, -3); 109 } 110} 111 112static void 113reg_boolean(struct lua_State *L, const char *name, int val) 114{ 115 if (name != NULL) { 116 lua_pushstring(L, name); 117 lua_pushboolean(L, val != 0); 118 lua_rawset(L, -3); 119 } 120} 121 122static void 123reg_conn_function(struct lua_State *L, 124 const char *name, 125 lua_CFunction func, 126 struct mg_connection *conn) 127{ 128 if (name != NULL && func != NULL && conn != NULL) { 129 lua_pushstring(L, name); 130 lua_pushlightuserdata(L, conn); 131 lua_pushcclosure(L, func, 1); 132 lua_rawset(L, -3); 133 } 134} 135 136static void 137reg_function(struct lua_State *L, const char *name, lua_CFunction func) 138{ 139 if (name != NULL && func != NULL) { 140 lua_pushstring(L, name); 141 lua_pushcclosure(L, func, 0); 142 lua_rawset(L, -3); 143 } 144} 145 146static void 147lua_cry(struct mg_connection *conn, 148 int err, 149 lua_State *L, 150 const char *lua_title, 151 const char *lua_operation) 152{ 153 DEBUG_TRACE("lua_cry (err=%i): %s: %s", err, lua_title, lua_operation); 154 155 switch (err) { 156 case LUA_OK: 157 case LUA_YIELD: 158 break; 159 case LUA_ERRRUN: 160 mg_cry_internal(conn, 161 "%s: %s failed: runtime error: %s", 162 lua_title, 163 lua_operation, 164 lua_tostring(L, -1)); 165 break; 166 case LUA_ERRSYNTAX: 167 mg_cry_internal(conn, 168 "%s: %s failed: syntax error: %s", 169 lua_title, 170 lua_operation, 171 lua_tostring(L, -1)); 172 break; 173 case LUA_ERRMEM: 174 mg_cry_internal(conn, 175 "%s: %s failed: out of memory", 176 lua_title, 177 lua_operation); 178 break; 179 case LUA_ERRGCMM: 180 mg_cry_internal(conn, 181 "%s: %s failed: error during garbage collection", 182 lua_title, 183 lua_operation); 184 break; 185 case LUA_ERRERR: 186 mg_cry_internal(conn, 187 "%s: %s failed: error in error handling: %s", 188 lua_title, 189 lua_operation, 190 lua_tostring(L, -1)); 191 break; 192 default: 193 mg_cry_internal( 194 conn, "%s: %s failed: error %i", lua_title, lua_operation, err); 195 break; 196 } 197} 198 199static int 200lsp_sock_close(lua_State *L) 201{ 202 int num_args = lua_gettop(L); 203 size_t s; 204 SOCKET *psock; 205 206 if ((num_args == 1) && lua_istable(L, -1)) { 207 lua_getfield(L, -1, "sock"); 208 psock = (SOCKET *)lua_tolstring(L, -1, &s); 209 if (s != sizeof(SOCKET)) { 210 return luaL_error(L, "invalid internal state in :close() call"); 211 } 212 /* Do not closesocket(*psock); here, close it in __gc */ 213 (void)psock; 214 } else { 215 return luaL_error(L, "invalid :close() call"); 216 } 217 return 0; 218} 219 220static int 221lsp_sock_recv(lua_State *L) 222{ 223 int num_args = lua_gettop(L); 224 char buf[2000]; 225 int n; 226 size_t s; 227 SOCKET *psock; 228 229 if ((num_args == 1) && lua_istable(L, -1)) { 230 lua_getfield(L, -1, "sock"); 231 psock = (SOCKET *)lua_tolstring(L, -1, &s); 232 if (s != sizeof(SOCKET)) { 233 return luaL_error(L, "invalid internal state in :recv() call"); 234 } 235 n = recv(*psock, buf, sizeof(buf), 0); 236 if (n <= 0) { 237 lua_pushnil(L); 238 } else { 239 lua_pushlstring(L, buf, n); 240 } 241 } else { 242 return luaL_error(L, "invalid :recv() call"); 243 } 244 return 1; 245} 246 247static int 248lsp_sock_send(lua_State *L) 249{ 250 int num_args = lua_gettop(L); 251 const char *buf; 252 size_t len, sent = 0; 253 int n = 0; 254 size_t s; 255 SOCKET *psock; 256 257 if ((num_args == 2) && lua_istable(L, -2) && lua_isstring(L, -1)) { 258 buf = lua_tolstring(L, -1, &len); 259 lua_getfield(L, -2, "sock"); 260 psock = (SOCKET *)lua_tolstring(L, -1, &s); 261 if (s != sizeof(SOCKET)) { 262 return luaL_error(L, "invalid internal state in :close() call"); 263 } 264 265 while (sent < len) { 266 if ((n = send(*psock, buf + sent, (int)(len - sent), 0)) <= 0) { 267 break; 268 } 269 sent += n; 270 } 271 lua_pushnumber(L, n); 272 } else { 273 return luaL_error(L, "invalid :close() call"); 274 } 275 return 1; 276} 277 278static int 279lsp_sock_gc(lua_State *L) 280{ 281 int num_args = lua_gettop(L); 282 size_t s; 283 SOCKET *psock; 284 285 if ((num_args == 1) && lua_istable(L, -1)) { 286 lua_getfield(L, -1, "sock"); 287 psock = (SOCKET *)lua_tolstring(L, -1, &s); 288 if (s != sizeof(SOCKET)) { 289 return luaL_error( 290 L, 291 "invalid internal state in __gc for object created by connect"); 292 } 293 closesocket(*psock); 294 } else { 295 return luaL_error(L, "__gc for object created by connect failed"); 296 } 297 return 0; 298} 299 300/* Methods and meta-methods supported by the object returned by connect. 301 * For meta-methods, see http://lua-users.org/wiki/MetatableEvents */ 302static const struct luaL_Reg luasocket_methods[] = {{"close", lsp_sock_close}, 303 {"send", lsp_sock_send}, 304 {"recv", lsp_sock_recv}, 305 {"__gc", lsp_sock_gc}, 306 {NULL, NULL}}; 307 308static int 309lsp_connect(lua_State *L) 310{ 311 int num_args = lua_gettop(L); 312 char ebuf[100]; 313 SOCKET sock; 314 union usa sa; 315 int ok; 316 317 if ((num_args == 3) && lua_isstring(L, -3) && lua_isnumber(L, -2) 318 && lua_isnumber(L, -1)) { 319 ok = connect_socket(NULL, 320 lua_tostring(L, -3), 321 (int)lua_tonumber(L, -2), 322 (int)lua_tonumber(L, -1), 323 ebuf, 324 sizeof(ebuf), 325 &sock, 326 &sa); 327 if (!ok) { 328 return luaL_error(L, ebuf); 329 } else { 330 set_blocking_mode(sock); 331 lua_newtable(L); 332 reg_lstring(L, "sock", (const char *)&sock, sizeof(SOCKET)); 333 reg_string(L, "host", lua_tostring(L, -4)); 334 luaL_getmetatable(L, LUASOCKET); 335 lua_setmetatable(L, -2); 336 } 337 } else { 338 return luaL_error( 339 L, "connect(host,port,is_ssl): invalid parameter given."); 340 } 341 return 1; 342} 343 344static int 345lsp_error(lua_State *L) 346{ 347 DEBUG_TRACE("%s", "lsp_error"); 348 lua_getglobal(L, "mg"); 349 lua_getfield(L, -1, "onerror"); 350 lua_pushvalue(L, -3); 351 lua_pcall(L, 1, 0, 0); 352 return 0; 353} 354 355/* Silently stop processing chunks. */ 356static void 357lsp_abort(lua_State *L) 358{ 359 int top = lua_gettop(L); 360 DEBUG_TRACE("%s", "lsp_abort"); 361 lua_getglobal(L, "mg"); 362 lua_pushnil(L); 363 lua_setfield(L, -2, "onerror"); 364 lua_settop(L, top); 365 lua_pushstring(L, "aborting"); 366 lua_error(L); 367} 368 369 370struct lsp_var_reader_data { 371 int64_t len; 372 int64_t consumed; 373 const char *begin; 374 unsigned char state; 375 char tag; 376}; 377 378 379/* Helper function to read the content of variable values */ 380static const char * 381lsp_var_reader(lua_State *L, void *ud, size_t *sz) 382{ 383 struct lsp_var_reader_data *reader = (struct lsp_var_reader_data *)ud; 384 const char *ret; 385 (void)(L); /* unused */ 386 387 /* This reader is called multiple times, to fetch the full Lua script */ 388 switch (reader->state) { 389 case 0: 390 /* First call: what function to call */ 391 reader->consumed = 0; 392 ret = "mg.write("; 393 *sz = strlen(ret); 394 break; 395 case 1: 396 /* Second call: forward variable name */ 397 ret = reader->begin; 398 *sz = (size_t)reader->len; 399 reader->consumed += reader->len; 400 break; 401 case 2: 402 /* Third call: close function call */ 403 ret = ")"; 404 *sz = strlen(ret); 405 break; 406 default: 407 /* Forth/Final call: tell Lua we got the entire script */ 408 ret = 0; 409 *sz = 0; 410 } 411 412 /* Step to the next state for the next call */ 413 reader->state++; 414 return ret; 415} 416 417 418static const char * 419lsp_kepler_reader(lua_State *L, void *ud, size_t *sz) 420{ 421 struct lsp_var_reader_data *reader = (struct lsp_var_reader_data *)ud; 422 const char *ret; 423 int64_t i; 424 int64_t left; 425 426 (void)(L); /* unused */ 427 428 /* This reader is called multiple times, to fetch the full Lua script */ 429 430 if (reader->state == 0) { 431 /* First call: Send opening tag - what function to call */ 432 ret = "mg.write([=======["; 433 *sz = strlen(ret); 434 reader->state = 1; 435 reader->consumed = 0; 436 return ret; 437 } 438 439 if (reader->state == 4) { 440 /* Final call: Tell Lua reader, we reached the end */ 441 *sz = 0; 442 return 0; 443 } 444 445 left = reader->len - reader->consumed; 446 if (left == 0) { 447 /* We reached the end of the file/available data. */ 448 /* Send closing tag. */ 449 ret = "]=======]);\n"; 450 *sz = strlen(ret); 451 reader->state = 4; /* Next will be the final call */ 452 return ret; 453 } 454 if (left > MG_BUF_LEN / 100) { 455 left = MG_BUF_LEN / 100; /* TODO XXX */ 456 } 457 i = 0; 458 459 if (reader->state == 1) { 460 /* State 1: plain text - put inside mg.write(...) */ 461 for (;;) { 462 /* Find next tag */ 463 while ((i < left) && (reader->begin[i + reader->consumed] != '<')) { 464 i++; 465 } 466 if (i > 0) { 467 /* Forward all data until the next tag */ 468 int64_t j = reader->consumed; 469 reader->consumed += i; 470 *sz = (size_t)i; /* cast is ok, i is limited to MG_BUF_LEN */ 471 return reader->begin + j; 472 } 473 474 /* assert (reader->begin[reader->state] == '<') */ 475 /* assert (i == 0) */ 476 if (0 == memcmp(reader->begin + reader->consumed, "<?lua", 5)) { 477 /* kepler <?lua syntax */ 478 i = 5; 479 reader->tag = '?'; 480 break; 481 } else if (0 == memcmp(reader->begin + reader->consumed, "<%", 2)) { 482 /* kepler <% syntax */ 483 i = 2; 484 reader->tag = '%'; 485 break; 486 } else if (0 == memcmp(reader->begin + reader->consumed, "<?", 2)) { 487 /* civetweb <? syntax */ 488 i = 2; 489 reader->tag = '?'; 490 break; 491 } else { 492 i = 1; 493 } 494 } 495 /* We found an opening or closing tag, or we reached the end of the 496 * file/data block */ 497 if (reader->begin[reader->consumed + i] == '=') { 498 /* Lua= tag - Lua expression to print */ 499 ret = "]=======]);\nmg.write("; 500 reader->state = 3; 501 i++; 502 } else { 503 /* Normal Lua tag - Lua chunk */ 504 ret = "]=======]);\n"; 505 reader->state = 2; 506 } 507 *sz = strlen(ret); 508 reader->consumed += i; /* length of <?lua or <% tag */ 509 return ret; 510 } 511 512 if ((reader->state == 2) || (reader->state == 3)) { 513 /* State 2: Lua chunkg - keep outside mg.write(...) */ 514 /* State 3: Lua expression - inside mg.write(...) */ 515 516 for (;;) { 517 int close_tag_found = 0; 518 519 /* Find end tag */ 520 while ((i < left) 521 && (reader->begin[i + reader->consumed] != reader->tag)) { 522 i++; 523 } 524 if (i > 0) { 525 /* Forward all data inside the Lua script tag */ 526 int64_t j = reader->consumed; 527 reader->consumed += i; 528 *sz = (size_t)i; /* cast is ok, i is limited to MG_BUF_LEN */ 529 530 return reader->begin + j; 531 } 532 533 /* Is this the closing tag we are looking for? */ 534 close_tag_found = 535 ((i + 1 < left) 536 && (reader->begin[i + 1 + reader->consumed] == '>')); 537 538 if (close_tag_found) { 539 /* Drop close tag */ 540 reader->consumed += 2; 541 542 if (reader->state == 2) { 543 /* Send a new opening tag to Lua */ 544 ret = ";\nmg.write([=======["; 545 } else { 546 ret = ");\nmg.write([=======["; 547 } 548 *sz = strlen(ret); 549 reader->state = 1; 550 return ret; 551 } else { 552 /* Not a close tag, continue searching */ 553 i++; 554 } 555 } 556 } 557 558 559 /* Must never be reached */ 560 *sz = 0; 561 return 0; 562} 563 564 565static int 566run_lsp_kepler(struct mg_connection *conn, 567 const char *path, 568 const char *p, 569 int64_t len, 570 lua_State *L, 571 int depth) 572{ 573 574 int lua_ok; 575 struct lsp_var_reader_data data; 576 char date[64]; 577 time_t curtime = time(NULL); 578 579 gmt_time_string(date, sizeof(date), &curtime); 580 581 if (depth == 1) { 582 /* Top level page assumes keep_alive is disabled. 583 * Do not overwrite this setting for included pages. */ 584 conn->must_close = 1; 585 586 /* Only send a HTML header, if this is the top level page. 587 * If this page is included by some mg.include calls, do not add a 588 * header. */ 589 mg_printf(conn, "HTTP/1.1 200 OK\r\n"); 590 send_no_cache_header(conn); 591 send_additional_header(conn); 592 mg_printf(conn, 593 "Date: %s\r\n" 594 "Connection: close\r\n" 595 "Content-Type: text/html; charset=utf-8\r\n\r\n", 596 date); 597 } 598 599 data.begin = p; 600 data.len = len; 601 data.state = 0; 602 data.consumed = 0; 603 data.tag = 0; 604 lua_ok = mg_lua_load(L, lsp_kepler_reader, &data, path, NULL); 605 606 if (lua_ok) { 607 /* Syntax error or OOM. 608 * Error message is pushed on stack. */ 609 lua_pcall(L, 1, 0, 0); 610 lua_cry(conn, lua_ok, L, "LSP", "execute"); /* XXX TODO: everywhere ! */ 611 612 } else { 613 /* Success loading chunk. Call it. */ 614 lua_pcall(L, 0, 0, 1); 615 } 616 return 0; 617} 618 619 620static int 621run_lsp_civetweb(struct mg_connection *conn, 622 const char *path, 623 const char *p, 624 int64_t len, 625 lua_State *L, 626 int depth) 627{ 628 int i, j, s, pos = 0, lines = 1, lualines = 0, is_var, lua_ok; 629 char chunkname[MG_BUF_LEN]; 630 struct lsp_var_reader_data data; 631 const char lsp_mark1 = '?'; /* Use <? code ?> */ 632 const char lsp_mark2 = '%'; /* Use <% code %> */ 633 634 if (depth == 1) { 635 /* Assume the script does not support keep_alive. The script may change 636 * this by calling mg.keep_alive(true). */ 637 conn->must_close = 1; 638 } 639 640 for (i = 0; i < len; i++) { 641 if (p[i] == '\n') { 642 lines++; 643 } 644 645 /* Lua pages are normal text, unless there is a "<?" or "<%" tag. */ 646 if (((i + 1) < len) && (p[i] == '<') 647 && ((p[i + 1] == lsp_mark1) || (p[i + 1] == lsp_mark2))) { 648 649 /* Opening tag way "<?" or "<%", closing tag must be the same. */ 650 char lsp_mark_used = p[i + 1]; 651 652 /* <?= var ?> or <%= var %> means a variable is enclosed and its 653 * value should be printed */ 654 if (0 == memcmp("lua", p + i + 2, 3)) { 655 /* Syntax: <?lua code ?> or <?lua= var ?> */ 656 /* This is added for compatibility to other LSP syntax 657 * definitions. */ 658 /* Skip 3 letters ("lua"). */ 659 s = 3; 660 } else { 661 /* no additional letters to skip, only "<?" */ 662 s = 0; 663 } 664 665 /* Check for '=' in "<?= ..." or "<%= ..." or "<?lua= ..." */ 666 is_var = (((i + s + 2) < len) && (p[i + s + 2] == '=')); 667 if (is_var) { 668 /* use variable value (print it later) */ 669 j = i + 2; 670 } else { 671 /* execute script code */ 672 j = i + 1; 673 } 674 675 while (j < len) { 676 677 if (p[j] == '\n') { 678 /* Add line (for line number offset) */ 679 lualines++; 680 } 681 682 /* Check for closing tag. */ 683 if (((j + 1) < len) && (p[j] == lsp_mark_used) 684 && (p[j + 1] == '>')) { 685 /* We found the closing tag of the Lua tag. */ 686 687 /* Print everything before the Lua opening tag. */ 688 mg_write(conn, p + pos, i - pos); 689 690 /* Set a name for debugging purposes */ 691 mg_snprintf(conn, 692 NULL, /* ignore truncation for debugging */ 693 chunkname, 694 sizeof(chunkname), 695 "@%s+%i", 696 path, 697 lines); 698 699 /* Prepare data for Lua C functions */ 700 lua_pushlightuserdata(L, conn); 701 lua_pushcclosure(L, lsp_error, 1); 702 703 /* Distinguish between <? script ?> (is_var == 0) 704 * and <?= expression ?> (is_var != 0). */ 705 if (is_var) { 706 /* For variables: Print the value */ 707 /* Note: <?= expression ?> is equivalent to 708 * <? mg.write( expression ) ?> */ 709 data.begin = p + (i + 3 + s); 710 data.len = j - (i + 3 + s); 711 data.state = 0; 712 data.consumed = 0; 713 data.tag = 0; 714 lua_ok = mg_lua_load( 715 L, lsp_var_reader, &data, chunkname, NULL); 716 } else { 717 /* For scripts: Execute them */ 718 lua_ok = luaL_loadbuffer(L, 719 p + (i + 2 + s), 720 j - (i + 2 + s), 721 chunkname); 722 } 723 724 if (lua_ok) { 725 /* Syntax error or OOM. 726 * Error message is pushed on stack. */ 727 lua_pcall(L, 1, 0, 0); 728 } else { 729 /* Success loading chunk. Call it. */ 730 lua_pcall(L, 0, 0, 1); 731 } 732 733 /* Progress until after the Lua closing tag. */ 734 pos = j + 2; 735 i = pos - 1; 736 break; 737 } 738 j++; 739 } 740 741 /* Line number for debugging/error logging. */ 742 if (lualines > 0) { 743 lines += lualines; 744 lualines = 0; 745 } 746 } 747 } 748 749 /* Print everything after the last Lua closing tag. */ 750 if (i > pos) { 751 mg_write(conn, p + pos, i - pos); 752 } 753 754 return 0; 755} 756 757 758/* mg.write: Send data to the client */ 759static int 760lsp_write(lua_State *L) 761{ 762 struct mg_connection *conn = 763 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 764 int num_args = lua_gettop(L); 765 const char *str; 766 size_t size; 767 int i; 768 int rv = 1; 769 770 for (i = 1; i <= num_args; i++) { 771 if (lua_isstring(L, i)) { 772 str = lua_tolstring(L, i, &size); 773 if (mg_write(conn, str, size) != (int)size) { 774 rv = 0; 775 } 776 } 777 } 778 lua_pushboolean(L, rv); 779 780 return 1; 781} 782 783 784/* mg.read: Read data from the client (e.g., from a POST request) */ 785static int 786lsp_read(lua_State *L) 787{ 788 struct mg_connection *conn = 789 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 790 char buf[1024]; 791 int len = mg_read(conn, buf, sizeof(buf)); 792 793 if (len <= 0) 794 return 0; 795 lua_pushlstring(L, buf, len); 796 797 return 1; 798} 799 800 801/* mg.keep_alive: Allow Lua pages to use the http keep-alive mechanism */ 802static int 803lsp_keep_alive(lua_State *L) 804{ 805 struct mg_connection *conn = 806 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 807 int num_args = lua_gettop(L); 808 809 /* This function may be called with one parameter (boolean) to set the 810 keep_alive state. 811 Or without a parameter to just query the current keep_alive state. */ 812 if ((num_args == 1) && lua_isboolean(L, 1)) { 813 conn->must_close = !lua_toboolean(L, 1); 814 } else if (num_args != 0) { 815 /* Syntax error */ 816 return luaL_error(L, "invalid keep_alive() call"); 817 } 818 819 /* Return the current "keep_alive" state. This may be false, even it 820 * keep_alive(true) has been called. */ 821 lua_pushboolean(L, should_keep_alive(conn)); 822 return 1; 823} 824 825 826/* Stack of includes */ 827struct lsp_include_history { 828 int depth; 829 const char *script[LSP_INCLUDE_MAX_DEPTH + 1]; 830}; 831 832 833/* mg.include: Include another .lp file */ 834static int 835lsp_include(lua_State *L) 836{ 837 struct mg_connection *conn = 838 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 839 int num_args = lua_gettop(L); 840 struct mg_file file = STRUCT_FILE_INITIALIZER; 841 const char *file_name = (num_args >= 1) ? lua_tostring(L, 1) : NULL; 842 const char *path_type = (num_args >= 2) ? lua_tostring(L, 2) : NULL; 843 struct lsp_include_history *include_history; 844 845 if (path_type == NULL) { 846 /* default to "absolute" */ 847 path_type = "a"; 848 } 849 850 if ((file_name != NULL) && (num_args <= 2)) { 851 852 lua_pushlightuserdata(L, (void *)&lua_regkey_lsp_include_history); 853 lua_gettable(L, LUA_REGISTRYINDEX); 854 include_history = (struct lsp_include_history *)lua_touserdata(L, -1); 855 856 if (include_history->depth >= ((int)(LSP_INCLUDE_MAX_DEPTH))) { 857 mg_cry_internal( 858 conn, 859 "lsp max include depth of %i reached while including %s", 860 (int)(LSP_INCLUDE_MAX_DEPTH), 861 file_name); 862 } else { 863 char file_name_path[512]; 864 char *p; 865 size_t len; 866 int truncated = 0; 867 868 file_name_path[511] = 0; 869 870 if (*path_type == 'v') { 871 /* "virtual" = relative to document root. */ 872 (void)mg_snprintf(conn, 873 &truncated, 874 file_name_path, 875 sizeof(file_name_path), 876 "%s/%s", 877 conn->dom_ctx->config[DOCUMENT_ROOT], 878 file_name); 879 880 } else if (*path_type == 'a') { 881 /* "absolute" = file name is relative to the 882 * webserver working directory 883 * or it is absolute system path. */ 884 /* path_type==NULL is the legacy use case with 1 argument */ 885 (void)mg_snprintf(conn, 886 &truncated, 887 file_name_path, 888 sizeof(file_name_path), 889 "%s", 890 file_name); 891 892 } else if ((*path_type == 'r') || (*path_type == 'f')) { 893 /* "relative" = file name is relative to the 894 * currect document */ 895 (void)mg_snprintf( 896 conn, 897 &truncated, 898 file_name_path, 899 sizeof(file_name_path), 900 "%s", 901 include_history->script[include_history->depth]); 902 903 if (!truncated) { 904 if ((p = strrchr(file_name_path, '/')) != NULL) { 905 p[1] = '\0'; 906 } 907 len = strlen(file_name_path); 908 (void)mg_snprintf(conn, 909 &truncated, 910 file_name_path + len, 911 sizeof(file_name_path) - len, 912 "%s", 913 file_name); 914 } 915 916 } else { 917 return luaL_error( 918 L, 919 "invalid path_type in include(file_name, path_type) call"); 920 } 921 922 if (handle_lsp_request(conn, file_name_path, &file, L)) { 923 /* handle_lsp_request returned an error code, meaning an error 924 * occurred in the included page and mg.onerror returned 925 * non-zero. 926 * Stop processing. 927 */ 928 929 lsp_abort(L); 930 } 931 } 932 933 } else { 934 /* Syntax error */ 935 return luaL_error(L, "invalid include() call"); 936 } 937 return 0; 938} 939 940 941/* mg.cry: Log an error. Default value for mg.onerror. */ 942static int 943lsp_cry(lua_State *L) 944{ 945 struct mg_connection *conn = 946 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 947 int num_args = lua_gettop(L); 948 const char *text = (num_args == 1) ? lua_tostring(L, 1) : NULL; 949 950 if (text) { 951 mg_cry_internal(conn, "%s", lua_tostring(L, -1)); 952 } else { 953 /* Syntax error */ 954 return luaL_error(L, "invalid cry() call"); 955 } 956 return 0; 957} 958 959 960/* mg.redirect: Redirect the request (internally). */ 961static int 962lsp_redirect(lua_State *L) 963{ 964 struct mg_connection *conn = 965 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 966 int num_args = lua_gettop(L); 967 const char *target = (num_args == 1) ? lua_tostring(L, 1) : NULL; 968 969 if (target) { 970 conn->request_info.local_uri = target; 971 handle_request(conn); 972 lsp_abort(L); 973 } else { 974 /* Syntax error */ 975 return luaL_error(L, "invalid redirect() call"); 976 } 977 return 0; 978} 979 980 981/* mg.send_file */ 982static int 983lsp_send_file(lua_State *L) 984{ 985 struct mg_connection *conn = 986 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 987 int num_args = lua_gettop(L); 988 const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL; 989 990 if (filename) { 991 mg_send_file(conn, filename); 992 } else { 993 /* Syntax error */ 994 return luaL_error(L, "invalid send_file() call"); 995 } 996 return 0; 997} 998 999 1000/* mg.mg_send_file_body */ 1001static int 1002lsp_send_file_body(lua_State *L) 1003{ 1004 struct mg_connection *conn = 1005 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 1006 int num_args = lua_gettop(L); 1007 const char *filename = (num_args == 1) ? lua_tostring(L, 1) : NULL; 1008 int ret; 1009 1010 if (filename) { 1011 ret = mg_send_file_body(conn, filename); 1012 } else { 1013 /* Syntax error */ 1014 return luaL_error(L, "invalid send_file() call"); 1015 } 1016 1017 lua_pushboolean(L, ret >= 0); 1018 return 1; 1019} 1020 1021 1022/* mg.get_time */ 1023static int 1024lsp_get_time(lua_State *L) 1025{ 1026 int num_args = lua_gettop(L); 1027 int monotonic = (num_args > 0) ? lua_toboolean(L, 1) : 0; 1028 struct timespec ts; 1029 double d; 1030 1031 clock_gettime(monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ts); 1032 d = (double)ts.tv_sec + ((double)ts.tv_nsec * 1.0E-9); 1033 lua_pushnumber(L, d); 1034 return 1; 1035} 1036 1037 1038/* mg.get_var */ 1039static int 1040lsp_get_var(lua_State *L) 1041{ 1042 int num_args = lua_gettop(L); 1043 const char *data, *var_name; 1044 size_t data_len, occurrence; 1045 int ret; 1046 struct mg_context *ctx; 1047 1048 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1049 lua_gettable(L, LUA_REGISTRYINDEX); 1050 ctx = (struct mg_context *)lua_touserdata(L, -1); 1051 1052 if (num_args >= 2 && num_args <= 3) { 1053 char *dst; 1054 data = lua_tolstring(L, 1, &data_len); 1055 var_name = lua_tostring(L, 2); 1056 occurrence = (num_args > 2) ? (long)lua_tonumber(L, 3) : 0; 1057 1058 /* Allocate dynamically, so there is no internal limit for get_var */ 1059 dst = (char *)mg_malloc_ctx(data_len + 1, ctx); 1060 if (!dst) { 1061 return luaL_error(L, "out of memory in get_var() call"); 1062 } 1063 1064 ret = mg_get_var2(data, data_len, var_name, dst, data_len, occurrence); 1065 if (ret >= 0) { 1066 /* Variable found: return value to Lua */ 1067 lua_pushstring(L, dst); 1068 } else { 1069 /* Variable not found */ 1070 lua_pushnil(L); 1071 } 1072 mg_free(dst); 1073 } else { 1074 /* Syntax error */ 1075 return luaL_error(L, "invalid get_var() call"); 1076 } 1077 return 1; 1078} 1079 1080 1081/* mg.get_mime_type */ 1082static int 1083lsp_get_mime_type(lua_State *L) 1084{ 1085 int num_args = lua_gettop(L); 1086 struct vec mime_type = {0, 0}; 1087 const char *text; 1088 1089 struct mg_connection *conn = 1090 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 1091 1092 if (num_args == 1) { 1093 text = lua_tostring(L, 1); 1094 if (text) { 1095 if (conn) { 1096 get_mime_type(conn, text, &mime_type); 1097 lua_pushlstring(L, mime_type.ptr, mime_type.len); 1098 } else { 1099 text = mg_get_builtin_mime_type(text); 1100 lua_pushstring(L, text); 1101 } 1102 } else { 1103 /* Syntax error */ 1104 return luaL_error(L, "invalid argument for get_mime_type() call"); 1105 } 1106 } else { 1107 /* Syntax error */ 1108 return luaL_error(L, "invalid get_mime_type() call"); 1109 } 1110 return 1; 1111} 1112 1113 1114/* mg.get_cookie */ 1115static int 1116lsp_get_cookie(lua_State *L) 1117{ 1118 int num_args = lua_gettop(L); 1119 const char *cookie; 1120 const char *var_name; 1121 int ret; 1122 struct mg_context *ctx; 1123 1124 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1125 lua_gettable(L, LUA_REGISTRYINDEX); 1126 ctx = (struct mg_context *)lua_touserdata(L, -1); 1127 1128 if (num_args == 2) { 1129 /* Correct number of arguments */ 1130 size_t data_len; 1131 char *dst; 1132 1133 cookie = lua_tolstring(L, 1, &data_len); 1134 var_name = lua_tostring(L, 2); 1135 1136 if (cookie == NULL || var_name == NULL) { 1137 /* Syntax error */ 1138 return luaL_error(L, "invalid get_cookie() call"); 1139 } 1140 1141 dst = (char *)mg_malloc_ctx(data_len + 1, ctx); 1142 if (!dst) { 1143 return luaL_error(L, "out of memory in get_cookie() call"); 1144 } 1145 1146 ret = mg_get_cookie(cookie, var_name, dst, data_len); 1147 1148 if (ret >= 0) { 1149 lua_pushlstring(L, dst, ret); 1150 } else { 1151 lua_pushnil(L); 1152 } 1153 mg_free(dst); 1154 1155 } else { 1156 /* Syntax error */ 1157 return luaL_error(L, "invalid get_cookie() call"); 1158 } 1159 return 1; 1160} 1161 1162 1163/* mg.md5 */ 1164static int 1165lsp_md5(lua_State *L) 1166{ 1167 int num_args = lua_gettop(L); 1168 const char *text; 1169 md5_byte_t hash[16]; 1170 md5_state_t ctx; 1171 size_t text_len; 1172 char buf[40]; 1173 1174 if (num_args == 1) { 1175 text = lua_tolstring(L, 1, &text_len); 1176 if (text) { 1177 md5_init(&ctx); 1178 md5_append(&ctx, (const md5_byte_t *)text, text_len); 1179 md5_finish(&ctx, hash); 1180 bin2str(buf, hash, sizeof(hash)); 1181 lua_pushstring(L, buf); 1182 } else { 1183 lua_pushnil(L); 1184 } 1185 } else { 1186 /* Syntax error */ 1187 return luaL_error(L, "invalid md5() call"); 1188 } 1189 return 1; 1190} 1191 1192 1193/* mg.url_encode */ 1194static int 1195lsp_url_encode(lua_State *L) 1196{ 1197 int num_args = lua_gettop(L); 1198 const char *text; 1199 size_t text_len; 1200 char *dst; 1201 int dst_len; 1202 struct mg_context *ctx; 1203 1204 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1205 lua_gettable(L, LUA_REGISTRYINDEX); 1206 ctx = (struct mg_context *)lua_touserdata(L, -1); 1207 1208 if (num_args == 1) { 1209 text = lua_tolstring(L, 1, &text_len); 1210 if (text) { 1211 dst_len = 3 * (int)text_len + 1; 1212 dst = ((text_len < 0x2AAAAAAA) ? (char *)mg_malloc_ctx(dst_len, ctx) 1213 : (char *)NULL); 1214 if (dst) { 1215 mg_url_encode(text, dst, dst_len); 1216 lua_pushstring(L, dst); 1217 mg_free(dst); 1218 } else { 1219 return luaL_error(L, "out of memory in url_decode() call"); 1220 } 1221 } else { 1222 lua_pushnil(L); 1223 } 1224 } else { 1225 /* Syntax error */ 1226 return luaL_error(L, "invalid url_encode() call"); 1227 } 1228 return 1; 1229} 1230 1231 1232/* mg.url_decode */ 1233static int 1234lsp_url_decode(lua_State *L) 1235{ 1236 int num_args = lua_gettop(L); 1237 const char *text; 1238 size_t text_len; 1239 int is_form; 1240 char *dst; 1241 int dst_len; 1242 struct mg_context *ctx; 1243 1244 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1245 lua_gettable(L, LUA_REGISTRYINDEX); 1246 ctx = (struct mg_context *)lua_touserdata(L, -1); 1247 1248 if (num_args == 1 || (num_args == 2 && lua_isboolean(L, 2))) { 1249 text = lua_tolstring(L, 1, &text_len); 1250 is_form = (num_args == 2) ? lua_isboolean(L, 2) : 0; 1251 if (text) { 1252 dst_len = (int)text_len + 1; 1253 dst = ((text_len < 0x7FFFFFFF) ? (char *)mg_malloc_ctx(dst_len, ctx) 1254 : (char *)NULL); 1255 if (dst) { 1256 mg_url_decode(text, (int)text_len, dst, dst_len, is_form); 1257 lua_pushstring(L, dst); 1258 mg_free(dst); 1259 } else { 1260 return luaL_error(L, "out of memory in url_decode() call"); 1261 } 1262 } else { 1263 lua_pushnil(L); 1264 } 1265 } else { 1266 /* Syntax error */ 1267 return luaL_error(L, "invalid url_decode() call"); 1268 } 1269 return 1; 1270} 1271 1272 1273/* mg.base64_encode */ 1274static int 1275lsp_base64_encode(lua_State *L) 1276{ 1277 int num_args = lua_gettop(L); 1278 const char *text; 1279 size_t text_len; 1280 char *dst; 1281 struct mg_context *ctx; 1282 1283 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1284 lua_gettable(L, LUA_REGISTRYINDEX); 1285 ctx = (struct mg_context *)lua_touserdata(L, -1); 1286 1287 if (num_args == 1) { 1288 text = lua_tolstring(L, 1, &text_len); 1289 if (text) { 1290 dst = (char *)mg_malloc_ctx(text_len * 8 / 6 + 4, ctx); 1291 if (dst) { 1292 base64_encode((const unsigned char *)text, (int)text_len, dst); 1293 lua_pushstring(L, dst); 1294 mg_free(dst); 1295 } else { 1296 return luaL_error(L, "out of memory in base64_encode() call"); 1297 } 1298 } else { 1299 lua_pushnil(L); 1300 } 1301 } else { 1302 /* Syntax error */ 1303 return luaL_error(L, "invalid base64_encode() call"); 1304 } 1305 return 1; 1306} 1307 1308 1309/* mg.base64_encode */ 1310static int 1311lsp_base64_decode(lua_State *L) 1312{ 1313 int num_args = lua_gettop(L); 1314 const char *text; 1315 size_t text_len, dst_len; 1316 int ret; 1317 char *dst; 1318 struct mg_context *ctx; 1319 1320 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1321 lua_gettable(L, LUA_REGISTRYINDEX); 1322 ctx = (struct mg_context *)lua_touserdata(L, -1); 1323 1324 if (num_args == 1) { 1325 text = lua_tolstring(L, 1, &text_len); 1326 if (text) { 1327 dst = (char *)mg_malloc_ctx(text_len, ctx); 1328 if (dst) { 1329 ret = base64_decode((const unsigned char *)text, 1330 (int)text_len, 1331 dst, 1332 &dst_len); 1333 if (ret != -1) { 1334 mg_free(dst); 1335 return luaL_error( 1336 L, "illegal character in lsp_base64_decode() call"); 1337 } else { 1338 lua_pushlstring(L, dst, dst_len); 1339 mg_free(dst); 1340 } 1341 } else { 1342 return luaL_error(L, 1343 "out of memory in lsp_base64_decode() call"); 1344 } 1345 } else { 1346 lua_pushnil(L); 1347 } 1348 } else { 1349 /* Syntax error */ 1350 return luaL_error(L, "invalid lsp_base64_decode() call"); 1351 } 1352 return 1; 1353} 1354 1355 1356/* mg.get_response_code_text */ 1357static int 1358lsp_get_response_code_text(lua_State *L) 1359{ 1360 int num_args = lua_gettop(L); 1361 int type1; 1362 double code; 1363 const char *text; 1364 1365 if (num_args == 1) { 1366 type1 = lua_type(L, 1); 1367 if (type1 == LUA_TNUMBER) { 1368 /* If the first argument is a number, 1369 convert it to the corresponding text. */ 1370 code = lua_tonumber(L, 1); 1371 text = mg_get_response_code_text(NULL, (int)code); 1372 if (text) { /* <-- should be always true */ 1373 lua_pushstring(L, text); 1374 } 1375 return text ? 1 : 0; 1376 } 1377 } 1378 1379 /* Syntax error */ 1380 return luaL_error(L, "invalid get_response_code_text() call"); 1381} 1382 1383 1384/* mg.random - might be better than math.random on some systems */ 1385static int 1386lsp_random(lua_State *L) 1387{ 1388 int num_args = lua_gettop(L); 1389 if (num_args == 0) { 1390 /* The civetweb internal random number generator will generate 1391 * a 64 bit random number. */ 1392 uint64_t r = get_random(); 1393 /* Lua "number" is a IEEE 754 double precission float: 1394 * https://en.wikipedia.org/wiki/Double-precision_floating-point_format 1395 * Thus, mask with 2^53-1 to get an integer with the maximum 1396 * precission available. */ 1397 r &= ((((uint64_t)1) << 53) - 1); 1398 lua_pushnumber(L, (double)r); 1399 return 1; 1400 } 1401 1402 /* Syntax error */ 1403 return luaL_error(L, "invalid random() call"); 1404} 1405 1406 1407/* mg.get_info */ 1408static int 1409lsp_get_info(lua_State *L) 1410{ 1411 int num_args = lua_gettop(L); 1412 int type1, type2; 1413 const char *arg1; 1414 double arg2; 1415 int len; 1416 char *buf; 1417 1418 if (num_args == 1) { 1419 type1 = lua_type(L, 1); 1420 if (type1 == LUA_TSTRING) { 1421 arg1 = lua_tostring(L, 1); 1422 /* Get info according to argument */ 1423 if (!mg_strcasecmp(arg1, "system")) { 1424 /* Get system info */ 1425 len = mg_get_system_info(NULL, 0); 1426 if (len > 0) { 1427 buf = (char *)mg_malloc(len + 64); 1428 if (!buf) { 1429 return luaL_error(L, "OOM in get_info() call"); 1430 } 1431 len = mg_get_system_info(buf, len + 63); 1432 lua_pushlstring(L, buf, len); 1433 mg_free(buf); 1434 } else { 1435 lua_pushstring(L, ""); 1436 } 1437 return 1; 1438 } 1439 if (!mg_strcasecmp(arg1, "context")) { 1440 /* Get context */ 1441 struct mg_context *ctx; 1442 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1443 lua_gettable(L, LUA_REGISTRYINDEX); 1444 ctx = (struct mg_context *)lua_touserdata(L, -1); 1445 1446 /* Get context info for server context */ 1447 len = mg_get_context_info(ctx, NULL, 0); 1448 if (len > 0) { 1449 buf = (char *)mg_malloc(len + 64); 1450 if (!buf) { 1451 return luaL_error(L, "OOM in get_info() call"); 1452 } 1453 len = mg_get_context_info(ctx, buf, len + 63); 1454 lua_pushlstring(L, buf, len); 1455 mg_free(buf); 1456 } else { 1457 lua_pushstring(L, ""); 1458 } 1459 return 1; 1460 } 1461 if (!mg_strcasecmp(arg1, "common")) { 1462 /* Get context info for NULL context */ 1463 len = mg_get_context_info(NULL, NULL, 0); 1464 if (len > 0) { 1465 buf = (char *)mg_malloc(len + 64); 1466 if (!buf) { 1467 return luaL_error(L, "OOM in get_info() call"); 1468 } 1469 len = mg_get_context_info(NULL, buf, len + 63); 1470 lua_pushlstring(L, buf, len); 1471 mg_free(buf); 1472 } else { 1473 lua_pushstring(L, ""); 1474 } 1475 return 1; 1476 } 1477 return 0; 1478 } 1479 } 1480 1481 if (num_args == 2) { 1482 type1 = lua_type(L, 1); 1483 type2 = lua_type(L, 2); 1484 if ((type1 == LUA_TSTRING) && (type2 == LUA_TNUMBER)) { 1485 arg1 = lua_tostring(L, 1); 1486 arg2 = lua_tonumber(L, 2); 1487 1488 /* Get info according to argument */ 1489 if (!mg_strcasecmp(arg1, "connection")) { 1490 int idx; 1491 1492 /* Get context */ 1493 struct mg_context *ctx; 1494 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1495 lua_gettable(L, LUA_REGISTRYINDEX); 1496 ctx = (struct mg_context *)lua_touserdata(L, -1); 1497 1498 /* Get connection info for connection idx */ 1499 idx = (int)(arg2 + 0.5); 1500 1501 /* Lua uses 1 based index, C uses 0 based index */ 1502 idx--; 1503 1504#if defined(MG_EXPERIMENTAL_INTERFACES) 1505 len = mg_get_connection_info(ctx, idx, NULL, 0); 1506 if (len > 0) { 1507 buf = (char *)mg_malloc(len + 64); 1508 if (!buf) { 1509 return luaL_error(L, "OOM in get_info() call"); 1510 } 1511 len = mg_get_connection_info(ctx, idx, buf, len + 63); 1512 lua_pushlstring(L, buf, len); 1513 mg_free(buf); 1514 } else { 1515 lua_pushstring(L, ""); 1516 } 1517#else 1518 (void)ctx; 1519 (void)idx; 1520 lua_pushstring(L, ""); 1521#endif 1522 1523 return 1; 1524 } 1525 return 0; 1526 } 1527 } 1528 1529 /* Syntax error */ 1530 return luaL_error(L, "invalid get_info() call"); 1531} 1532 1533 1534/* mg.get_option */ 1535static int 1536lsp_get_option(lua_State *L) 1537{ 1538 int num_args = lua_gettop(L); 1539 int type1; 1540 const char *arg1; 1541 const char *data; 1542 int optidx; 1543 1544 /* Get connection */ 1545 struct mg_connection *conn = 1546 (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1)); 1547 1548 if (num_args == 0) { 1549 const struct mg_option *opts = mg_get_valid_options(); 1550 1551 if (!opts) { /* <-- should be always false */ 1552 return 0; 1553 } 1554 1555 lua_newtable(L); 1556 while (opts->name) { 1557 optidx = get_option_index(opts->name); 1558 if (optidx >= 0) { 1559 data = conn->dom_ctx->config[optidx]; 1560 if (data) { 1561 reg_string(L, opts->name, data); 1562 } 1563 } 1564 opts++; 1565 } 1566 1567 return 1; 1568 } 1569 1570 if (num_args == 1) { 1571 type1 = lua_type(L, 1); 1572 if (type1 == LUA_TSTRING) { 1573 arg1 = lua_tostring(L, 1); 1574 /* Get option according to argument */ 1575 optidx = get_option_index(arg1); 1576 if (optidx >= 0) { 1577 data = conn->dom_ctx->config[optidx]; 1578 if (data) { 1579 lua_pushstring(L, data); 1580 return 1; 1581 } 1582 } 1583 return 0; 1584 } 1585 } 1586 1587 /* Syntax error */ 1588 return luaL_error(L, "invalid get_option() call"); 1589} 1590 1591 1592/* UUID library and function pointer */ 1593union { 1594 void *p; 1595 void (*f)(unsigned char uuid[16]); 1596} pf_uuid_generate; 1597 1598 1599/* mg.uuid */ 1600static int 1601lsp_uuid(lua_State *L) 1602{ 1603 union { 1604 unsigned char uuid_array[16]; 1605 struct uuid_struct_type { 1606 uint32_t data1; 1607 uint16_t data2; 1608 uint16_t data3; 1609 uint8_t data4[8]; 1610 } uuid_struct; 1611 } uuid; 1612 1613 char uuid_str[40]; 1614 int num_args = lua_gettop(L); 1615 1616 memset(&uuid, 0, sizeof(uuid)); 1617 memset(uuid_str, 0, sizeof(uuid_str)); 1618 1619 if (num_args == 0) { 1620 1621 pf_uuid_generate.f(uuid.uuid_array); 1622 1623 sprintf(uuid_str, 1624 "{%08lX-%04X-%04X-%02X%02X-" 1625 "%02X%02X%02X%02X%02X%02X}", 1626 (unsigned long)uuid.uuid_struct.data1, 1627 (unsigned)uuid.uuid_struct.data2, 1628 (unsigned)uuid.uuid_struct.data3, 1629 (unsigned)uuid.uuid_struct.data4[0], 1630 (unsigned)uuid.uuid_struct.data4[1], 1631 (unsigned)uuid.uuid_struct.data4[2], 1632 (unsigned)uuid.uuid_struct.data4[3], 1633 (unsigned)uuid.uuid_struct.data4[4], 1634 (unsigned)uuid.uuid_struct.data4[5], 1635 (unsigned)uuid.uuid_struct.data4[6], 1636 (unsigned)uuid.uuid_struct.data4[7]); 1637 1638 lua_pushstring(L, uuid_str); 1639 return 1; 1640 } 1641 1642 /* Syntax error */ 1643 return luaL_error(L, "invalid random() call"); 1644} 1645 1646 1647#if defined(USE_WEBSOCKET) 1648struct lua_websock_data { 1649 lua_State *state; 1650 char *script; 1651 unsigned references; 1652 struct mg_connection *conn[MAX_WORKER_THREADS]; 1653 pthread_mutex_t ws_mutex; 1654}; 1655#endif 1656 1657 1658/* mg.write for websockets */ 1659static int 1660lwebsock_write(lua_State *L) 1661{ 1662#if defined(USE_WEBSOCKET) 1663 int num_args = lua_gettop(L); 1664 struct lua_websock_data *ws; 1665 const char *str; 1666 size_t size; 1667 int opcode = -1; 1668 unsigned i; 1669 struct mg_connection *client = NULL; 1670 1671 lua_pushlightuserdata(L, (void *)&lua_regkey_connlist); 1672 lua_gettable(L, LUA_REGISTRYINDEX); 1673 ws = (struct lua_websock_data *)lua_touserdata(L, -1); 1674 1675 (void)pthread_mutex_lock(&(ws->ws_mutex)); 1676 1677 if (num_args == 1) { 1678 /* just one text: send it to all client */ 1679 if (lua_isstring(L, 1)) { 1680 opcode = MG_WEBSOCKET_OPCODE_TEXT; 1681 } 1682 } else if (num_args == 2) { 1683 if (lua_isnumber(L, 1)) { 1684 /* opcode number and message text */ 1685 opcode = (int)lua_tointeger(L, 1); 1686 } else if (lua_isstring(L, 1)) { 1687 /* opcode string and message text */ 1688 str = lua_tostring(L, 1); 1689 if (!mg_strncasecmp(str, "text", 4)) 1690 opcode = MG_WEBSOCKET_OPCODE_TEXT; 1691 else if (!mg_strncasecmp(str, "bin", 3)) 1692 opcode = MG_WEBSOCKET_OPCODE_BINARY; 1693 else if (!mg_strncasecmp(str, "close", 5)) 1694 opcode = MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE; 1695 else if (!mg_strncasecmp(str, "ping", 4)) 1696 opcode = MG_WEBSOCKET_OPCODE_PING; 1697 else if (!mg_strncasecmp(str, "pong", 4)) 1698 opcode = MG_WEBSOCKET_OPCODE_PONG; 1699 else if (!mg_strncasecmp(str, "cont", 4)) 1700 opcode = MG_WEBSOCKET_OPCODE_CONTINUATION; 1701 } else if (lua_isuserdata(L, 1)) { 1702 /* client id and message text */ 1703 client = (struct mg_connection *)lua_touserdata(L, 1); 1704 opcode = MG_WEBSOCKET_OPCODE_TEXT; 1705 } 1706 } else if (num_args == 3) { 1707 if (lua_isuserdata(L, 1)) { 1708 client = (struct mg_connection *)lua_touserdata(L, 1); 1709 if (lua_isnumber(L, 2)) { 1710 /* client id, opcode number and message text */ 1711 opcode = (int)lua_tointeger(L, 2); 1712 } else if (lua_isstring(L, 2)) { 1713 /* client id, opcode string and message text */ 1714 str = lua_tostring(L, 2); 1715 if (!mg_strncasecmp(str, "text", 4)) 1716 opcode = MG_WEBSOCKET_OPCODE_TEXT; 1717 else if (!mg_strncasecmp(str, "bin", 3)) 1718 opcode = MG_WEBSOCKET_OPCODE_BINARY; 1719 else if (!mg_strncasecmp(str, "close", 5)) 1720 opcode = MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE; 1721 else if (!mg_strncasecmp(str, "ping", 4)) 1722 opcode = MG_WEBSOCKET_OPCODE_PING; 1723 else if (!mg_strncasecmp(str, "pong", 4)) 1724 opcode = MG_WEBSOCKET_OPCODE_PONG; 1725 else if (!mg_strncasecmp(str, "cont", 4)) 1726 opcode = MG_WEBSOCKET_OPCODE_CONTINUATION; 1727 } 1728 } 1729 } 1730 1731 if (opcode >= 0 && opcode < 16 && lua_isstring(L, num_args)) { 1732 str = lua_tolstring(L, num_args, &size); 1733 if (client) { 1734 for (i = 0; i < ws->references; i++) { 1735 if (client == ws->conn[i]) { 1736 mg_lock_connection(ws->conn[i]); 1737 mg_websocket_write(ws->conn[i], opcode, str, size); 1738 mg_unlock_connection(ws->conn[i]); 1739 } 1740 } 1741 } else { 1742 for (i = 0; i < ws->references; i++) { 1743 mg_lock_connection(ws->conn[i]); 1744 mg_websocket_write(ws->conn[i], opcode, str, size); 1745 mg_unlock_connection(ws->conn[i]); 1746 } 1747 } 1748 } else { 1749 (void)pthread_mutex_unlock(&(ws->ws_mutex)); 1750 return luaL_error(L, "invalid websocket write() call"); 1751 } 1752 1753 (void)pthread_mutex_unlock(&(ws->ws_mutex)); 1754 1755#else 1756 (void)(L); /* unused */ 1757#endif 1758 return 0; 1759} 1760 1761 1762struct laction_arg { 1763 lua_State *state; 1764 const char *script; 1765 pthread_mutex_t *pmutex; 1766 char txt[1]; 1767}; 1768 1769 1770static int 1771lua_action(struct laction_arg *arg) 1772{ 1773 int err, ok; 1774 struct mg_context *ctx; 1775 1776 (void)pthread_mutex_lock(arg->pmutex); 1777 1778 lua_pushlightuserdata(arg->state, (void *)&lua_regkey_ctx); 1779 lua_gettable(arg->state, LUA_REGISTRYINDEX); 1780 ctx = (struct mg_context *)lua_touserdata(arg->state, -1); 1781 lua_pop(arg->state, 1); 1782 1783 err = luaL_loadstring(arg->state, arg->txt); 1784 if (err != 0) { 1785 struct mg_connection fc; 1786 lua_cry( 1787 fake_connection(&fc, ctx), err, arg->state, arg->script, "timer"); 1788 (void)pthread_mutex_unlock(arg->pmutex); 1789 mg_free(arg); 1790 return 0; 1791 } 1792 err = lua_pcall(arg->state, 0, 1, 0); 1793 if (err != 0) { 1794 struct mg_connection fc; 1795 lua_cry( 1796 fake_connection(&fc, ctx), err, arg->state, arg->script, "timer"); 1797 (void)pthread_mutex_unlock(arg->pmutex); 1798 mg_free(arg); 1799 return 0; 1800 } 1801 1802 ok = lua_type(arg->state, -1); 1803 if (lua_isboolean(arg->state, -1)) { 1804 ok = lua_toboolean(arg->state, -1); 1805 } else { 1806 ok = 0; 1807 } 1808 lua_pop(arg->state, 1); 1809 1810 (void)pthread_mutex_unlock(arg->pmutex); 1811 1812 if (!ok) { 1813 mg_free(arg); 1814 } 1815 return ok; 1816} 1817 1818 1819static int 1820lua_action_free(struct laction_arg *arg) 1821{ 1822 if (lua_action(arg)) { 1823 mg_free(arg); 1824 } 1825 return 0; 1826} 1827 1828 1829static int 1830lwebsocket_set_timer(lua_State *L, int is_periodic) 1831{ 1832#if defined(USE_TIMERS) && defined(USE_WEBSOCKET) 1833 int num_args = lua_gettop(L); 1834 struct lua_websock_data *ws; 1835 int type1, type2, ok = 0; 1836 double timediff; 1837 struct mg_context *ctx; 1838 struct laction_arg *arg; 1839 const char *txt; 1840 size_t txt_len; 1841 1842 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 1843 lua_gettable(L, LUA_REGISTRYINDEX); 1844 ctx = (struct mg_context *)lua_touserdata(L, -1); 1845 1846 lua_pushlightuserdata(L, (void *)&lua_regkey_connlist); 1847 lua_gettable(L, LUA_REGISTRYINDEX); 1848 ws = (struct lua_websock_data *)lua_touserdata(L, -1); 1849 1850 if (num_args < 2) { 1851 return luaL_error(L, 1852 "not enough arguments for set_timer/interval() call"); 1853 } 1854 1855 type1 = lua_type(L, 1); 1856 type2 = lua_type(L, 2); 1857 1858 if (type1 == LUA_TSTRING && type2 == LUA_TNUMBER && num_args == 2) { 1859 timediff = (double)lua_tonumber(L, 2); 1860 txt = lua_tostring(L, 1); 1861 txt_len = strlen(txt); 1862 arg = (struct laction_arg *)mg_malloc_ctx(sizeof(struct laction_arg) 1863 + txt_len + 10, 1864 ctx); 1865 if (!arg) { 1866 return luaL_error(L, "out of memory"); 1867 } 1868 1869 arg->state = L; 1870 arg->script = ws->script; 1871 arg->pmutex = &(ws->ws_mutex); 1872 memcpy(arg->txt, "return(", 7); 1873 memcpy(arg->txt + 7, txt, txt_len); 1874 arg->txt[txt_len + 7] = ')'; 1875 arg->txt[txt_len + 8] = 0; 1876 ok = 1877 (0 1878 == timer_add(ctx, 1879 timediff, 1880 is_periodic, 1881 1, 1882 (taction)(is_periodic ? lua_action : lua_action_free), 1883 (void *)arg)); 1884 } else if (type1 == LUA_TFUNCTION && type2 == LUA_TNUMBER) { 1885 /* TODO (mid): not implemented yet */ 1886 return luaL_error(L, "invalid arguments for set_timer/interval() call"); 1887 } else { 1888 return luaL_error(L, "invalid arguments for set_timer/interval() call"); 1889 } 1890 1891 lua_pushboolean(L, ok); 1892 return 1; 1893 1894#else 1895 (void)(L); /* unused */ 1896 (void)(is_periodic); /* unused */ 1897 return 0; 1898#endif 1899} 1900 1901 1902/* mg.set_timeout for websockets */ 1903static int 1904lwebsocket_set_timeout(lua_State *L) 1905{ 1906 return lwebsocket_set_timer(L, 0); 1907} 1908 1909 1910/* mg.set_interval for websockets */ 1911static int 1912lwebsocket_set_interval(lua_State *L) 1913{ 1914 return lwebsocket_set_timer(L, 1); 1915} 1916 1917 1918/* Debug hook */ 1919static void 1920lua_debug_hook(lua_State *L, lua_Debug *ar) 1921{ 1922 int i; 1923 int stack_len = lua_gettop(L); 1924 1925 lua_getinfo(L, "nSlu", ar); 1926 1927 if (ar->event == LUA_HOOKCALL) { 1928 printf("call\n"); 1929 } else if (ar->event == LUA_HOOKRET) { 1930 printf("ret\n"); 1931#if defined(LUA_HOOKTAILRET) 1932 } else if (ar->event == LUA_HOOKTAILRET) { 1933 printf("tail ret\n"); 1934#endif 1935#if defined(LUA_HOOKTAILCALL) 1936 } else if (ar->event == LUA_HOOKTAILCALL) { 1937 printf("tail call\n"); 1938#endif 1939 } else if (ar->event == LUA_HOOKLINE) { 1940 printf("line\n"); 1941 } else if (ar->event == LUA_HOOKCOUNT) { 1942 printf("count\n"); 1943 } else { 1944 printf("unknown (%i)\n", ar->event); 1945 } 1946 1947 if (ar->currentline >= 0) { 1948 printf("%s:%i\n", ar->source, ar->currentline); 1949 } 1950 1951 printf("%s (%s)\n", ar->name, ar->namewhat); 1952 1953 1954 for (i = 1; i <= stack_len; i++) { /* repeat for each level */ 1955 int val_type = lua_type(L, i); 1956 const char *s; 1957 size_t n; 1958 1959 switch (val_type) { 1960 1961 case LUA_TNIL: 1962 /* nil value on the stack */ 1963 printf("nil\n"); 1964 break; 1965 1966 case LUA_TBOOLEAN: 1967 /* boolean (true / false) */ 1968 printf("boolean: %s\n", lua_toboolean(L, i) ? "true" : "false"); 1969 break; 1970 1971 case LUA_TNUMBER: 1972 /* number */ 1973 printf("number: %g\n", lua_tonumber(L, i)); 1974 break; 1975 1976 case LUA_TSTRING: 1977 /* string with limited length */ 1978 s = lua_tolstring(L, i, &n); 1979 printf("string: '%.*s%s\n", 1980 ((n > 30) ? 28 : (int)n), 1981 s, 1982 ((n > 30) ? ".." : "'")); 1983 break; 1984 1985 default: 1986 /* other values */ 1987 printf("%s\n", lua_typename(L, val_type)); 1988 break; 1989 } 1990 } 1991 1992 printf("\n"); 1993} 1994 1995 1996/* Lua Environment */ 1997enum { 1998 LUA_ENV_TYPE_LUA_SERVER_PAGE = 0, 1999 LUA_ENV_TYPE_PLAIN_LUA_PAGE = 1, 2000 LUA_ENV_TYPE_LUA_WEBSOCKET = 2, 2001}; 2002 2003 2004static void 2005prepare_lua_request_info(struct mg_connection *conn, lua_State *L) 2006{ 2007 const char *s; 2008 int i; 2009 2010 /* Export mg.request_info */ 2011 lua_pushstring(L, "request_info"); 2012 lua_newtable(L); 2013 reg_string(L, "request_method", conn->request_info.request_method); 2014 reg_string(L, "request_uri", conn->request_info.request_uri); 2015 reg_string(L, "uri", conn->request_info.local_uri); 2016 reg_string(L, "http_version", conn->request_info.http_version); 2017 reg_string(L, "query_string", conn->request_info.query_string); 2018 reg_string(L, "remote_addr", conn->request_info.remote_addr); 2019 /* TODO (high): ip version */ 2020 reg_int(L, "remote_port", conn->request_info.remote_port); 2021 reg_int(L, "num_headers", conn->request_info.num_headers); 2022 reg_int(L, "server_port", ntohs(conn->client.lsa.sin.sin_port)); 2023 2024 if (conn->path_info != NULL) { 2025 reg_string(L, "path_info", conn->path_info); 2026 } 2027 2028 if (conn->request_info.content_length >= 0) { 2029 /* reg_int64: content_length */ 2030 lua_pushstring(L, "content_length"); 2031 lua_pushnumber( 2032 L, 2033 (lua_Number)conn->request_info 2034 .content_length); /* lua_Number may be used as 52 bit integer */ 2035 lua_rawset(L, -3); 2036 } 2037 if ((s = mg_get_header(conn, "Content-Type")) != NULL) { 2038 reg_string(L, "content_type", s); 2039 } 2040 2041 if (conn->request_info.remote_user != NULL) { 2042 reg_string(L, "remote_user", conn->request_info.remote_user); 2043 reg_string(L, "auth_type", "Digest"); 2044 } 2045 2046 reg_boolean(L, "https", conn->ssl != NULL); 2047 2048 if (conn->status_code > 0) { 2049 /* Lua error handler should show the status code */ 2050 reg_int(L, "status", conn->status_code); 2051 } 2052 2053 /* Table "HTTP headers" */ 2054 lua_pushstring(L, "http_headers"); 2055 lua_newtable(L); 2056 for (i = 0; i < conn->request_info.num_headers; i++) { 2057 reg_string(L, 2058 conn->request_info.http_headers[i].name, 2059 conn->request_info.http_headers[i].value); 2060 } 2061 lua_rawset(L, -3); 2062 2063 /* Table "Client Certificate" */ 2064 if (conn->request_info.client_cert) { 2065 lua_pushstring(L, "client_cert"); 2066 lua_newtable(L); 2067 reg_string(L, "subject", conn->request_info.client_cert->subject); 2068 reg_string(L, "issuer", conn->request_info.client_cert->issuer); 2069 reg_string(L, "serial", conn->request_info.client_cert->serial); 2070 reg_string(L, "finger", conn->request_info.client_cert->finger); 2071 lua_rawset(L, -3); 2072 } 2073 2074 /* End of request_info */ 2075 lua_rawset(L, -3); 2076} 2077 2078 2079static void * 2080lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize) 2081{ 2082 (void)osize; /* not used */ 2083 2084 if (nsize == 0) { 2085 mg_free(ptr); 2086 return NULL; 2087 } 2088 return mg_realloc_ctx(ptr, nsize, (struct mg_context *)ud); 2089} 2090 2091 2092/* In CivetWeb, Lua-Shared is used as *.inl file */ 2093#define LUA_SHARED_INTERFACE static 2094#include "mod_lua_shared.inl" 2095 2096 2097static void 2098civetweb_open_lua_libs(lua_State *L) 2099{ 2100 { 2101 extern void luaL_openlibs(lua_State *); 2102 luaL_openlibs(L); 2103 } 2104 2105#if defined(USE_LUA_SQLITE3) 2106 { 2107 extern int luaopen_lsqlite3(lua_State *); 2108 luaopen_lsqlite3(L); 2109 } 2110#endif 2111#if defined(USE_LUA_LUAXML) 2112 { 2113 extern int luaopen_LuaXML_lib(lua_State *); 2114 luaopen_LuaXML_lib(L); 2115 } 2116#endif 2117#if defined(USE_LUA_FILE_SYSTEM) 2118 { 2119 extern int luaopen_lfs(lua_State *); 2120 luaopen_lfs(L); 2121 } 2122#endif 2123} 2124 2125 2126static void 2127prepare_lua_environment(struct mg_context *ctx, 2128 struct mg_connection *conn, 2129 struct lua_websock_data *ws_conn_list, 2130 lua_State *L, 2131 const char *script_name, 2132 int lua_env_type) 2133{ 2134 const char *preload_file_name = NULL; 2135 const char *debug_params = NULL; 2136 2137 civetweb_open_lua_libs(L); 2138 2139#if defined(MG_EXPERIMENTAL_INTERFACES) 2140 /* Check if debugging should be enabled */ 2141 if ((conn != NULL) && (conn->dom_ctx != NULL)) { 2142 debug_params = conn->dom_ctx->config[LUA_DEBUG_PARAMS]; 2143 } 2144#endif 2145 2146#if LUA_VERSION_NUM == 502 2147 /* Keep the "connect" method for compatibility, 2148 * but do not backport it to Lua 5.1. 2149 * TODO: Redesign the interface. 2150 */ 2151 luaL_newmetatable(L, LUASOCKET); 2152 /* self.__index = self */ 2153 lua_pushvalue(L, -1); 2154 lua_setfield(L, -2, "__index"); 2155 luaL_setfuncs(L, luasocket_methods, 0); 2156 lua_pop(L, 1); 2157 lua_register(L, "connect", lsp_connect); 2158#endif 2159 2160 /* Store context in the registry */ 2161 if (ctx != NULL) { 2162 lua_pushlightuserdata(L, (void *)&lua_regkey_ctx); 2163 lua_pushlightuserdata(L, (void *)ctx); 2164 lua_settable(L, LUA_REGISTRYINDEX); 2165 } 2166 if (ws_conn_list != NULL) { 2167 lua_pushlightuserdata(L, (void *)&lua_regkey_connlist); 2168 lua_pushlightuserdata(L, (void *)ws_conn_list); 2169 lua_settable(L, LUA_REGISTRYINDEX); 2170 } 2171 2172 /* Lua server pages store the depth of mg.include, in order 2173 * to detect recursions and prevent stack overflows. */ 2174 if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE) { 2175 struct lsp_include_history *h; 2176 lua_pushlightuserdata(L, (void *)&lua_regkey_lsp_include_history); 2177 h = (struct lsp_include_history *) 2178 lua_newuserdata(L, sizeof(struct lsp_include_history)); 2179 lua_settable(L, LUA_REGISTRYINDEX); 2180 memset(h, 0, sizeof(struct lsp_include_history)); 2181 } 2182 2183 /* Register mg module */ 2184 lua_newtable(L); 2185 2186 switch (lua_env_type) { 2187 case LUA_ENV_TYPE_LUA_SERVER_PAGE: 2188 reg_string(L, "lua_type", "page"); 2189 break; 2190 case LUA_ENV_TYPE_PLAIN_LUA_PAGE: 2191 reg_string(L, "lua_type", "script"); 2192 break; 2193 case LUA_ENV_TYPE_LUA_WEBSOCKET: 2194 reg_string(L, "lua_type", "websocket"); 2195 break; 2196 } 2197 2198 if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE 2199 || lua_env_type == LUA_ENV_TYPE_PLAIN_LUA_PAGE) { 2200 reg_conn_function(L, "cry", lsp_cry, conn); 2201 reg_conn_function(L, "read", lsp_read, conn); 2202 reg_conn_function(L, "write", lsp_write, conn); 2203 reg_conn_function(L, "keep_alive", lsp_keep_alive, conn); 2204 reg_conn_function(L, "send_file", lsp_send_file, conn); 2205 reg_conn_function(L, "send_file_body", lsp_send_file_body, conn); 2206 reg_conn_function(L, "redirect", lsp_redirect, conn); 2207 } 2208 2209 if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE) { 2210 reg_conn_function(L, "include", lsp_include, conn); 2211 } 2212 2213 if (lua_env_type == LUA_ENV_TYPE_LUA_WEBSOCKET) { 2214 reg_function(L, "write", lwebsock_write); 2215#if defined(USE_TIMERS) 2216 reg_function(L, "set_timeout", lwebsocket_set_timeout); 2217 reg_function(L, "set_interval", lwebsocket_set_interval); 2218#endif 2219 } 2220 2221 reg_conn_function(L, "get_mime_type", lsp_get_mime_type, conn); 2222 reg_conn_function(L, "get_option", lsp_get_option, conn); 2223 2224 reg_function(L, "time", lsp_get_time); 2225 reg_function(L, "get_var", lsp_get_var); 2226 reg_function(L, "get_cookie", lsp_get_cookie); 2227 reg_function(L, "md5", lsp_md5); 2228 reg_function(L, "url_encode", lsp_url_encode); 2229 reg_function(L, "url_decode", lsp_url_decode); 2230 reg_function(L, "base64_encode", lsp_base64_encode); 2231 reg_function(L, "base64_decode", lsp_base64_decode); 2232 reg_function(L, "get_response_code_text", lsp_get_response_code_text); 2233 reg_function(L, "random", lsp_random); 2234 reg_function(L, "get_info", lsp_get_info); 2235 2236 if (pf_uuid_generate.f) { 2237 reg_function(L, "uuid", lsp_uuid); 2238 } 2239 2240 reg_string(L, "version", CIVETWEB_VERSION); 2241 2242 reg_string(L, "script_name", script_name); 2243 2244 if ((conn != NULL) && (conn->dom_ctx != NULL)) { 2245 reg_string(L, "document_root", conn->dom_ctx->config[DOCUMENT_ROOT]); 2246 reg_string(L, 2247 "auth_domain", 2248 conn->dom_ctx->config[AUTHENTICATION_DOMAIN]); 2249#if defined(USE_WEBSOCKET) 2250 if (conn->dom_ctx->config[WEBSOCKET_ROOT]) { 2251 reg_string(L, 2252 "websocket_root", 2253 conn->dom_ctx->config[WEBSOCKET_ROOT]); 2254 } else { 2255 reg_string(L, 2256 "websocket_root", 2257 conn->dom_ctx->config[DOCUMENT_ROOT]); 2258 } 2259#endif 2260 2261 if ((ctx != NULL) && (ctx->systemName != NULL)) { 2262 reg_string(L, "system", ctx->systemName); 2263 } 2264 } 2265 2266 /* Export connection specific info */ 2267 if (conn != NULL) { 2268 prepare_lua_request_info(conn, L); 2269 } 2270 2271 /* Store as global table "mg" */ 2272 lua_setglobal(L, "mg"); 2273 2274 /* Register "shared" table */ 2275 lua_shared_register(L); 2276 2277 /* Register default mg.onerror function */ 2278 IGNORE_UNUSED_RESULT( 2279 luaL_dostring(L, 2280 "mg.onerror = function(e) mg.write('\\nLua error:\\n', " 2281 "debug.traceback(e, 1)) end")); 2282 2283 /* Check if a preload file is available */ 2284 if ((conn != NULL) && (conn->dom_ctx != NULL)) { 2285 preload_file_name = conn->dom_ctx->config[LUA_PRELOAD_FILE]; 2286 } 2287 2288 /* Preload file into new Lua environment */ 2289 if (preload_file_name) { 2290 IGNORE_UNUSED_RESULT(luaL_dofile(L, preload_file_name)); 2291 } 2292 2293 /* Call user init function */ 2294 if (ctx != NULL) { 2295 if (ctx->callbacks.init_lua != NULL) { 2296 ctx->callbacks.init_lua(conn, L); 2297 } 2298 } 2299 2300 /* If debugging is enabled, add a hook */ 2301 if (debug_params) { 2302 int mask = 0; 2303 if (0 != strchr(debug_params, 'c')) { 2304 mask |= LUA_MASKCALL; 2305 } 2306 if (0 != strchr(debug_params, 'r')) { 2307 mask |= LUA_MASKRET; 2308 } 2309 if (0 != strchr(debug_params, 'l')) { 2310 mask |= LUA_MASKLINE; 2311 } 2312 lua_sethook(L, lua_debug_hook, mask, 0); 2313 } 2314} 2315 2316 2317static int 2318lua_error_handler(lua_State *L) 2319{ 2320 const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n"; 2321 2322 lua_getglobal(L, "mg"); 2323 if (!lua_isnil(L, -1)) { 2324 lua_getfield(L, -1, "write"); /* call mg.write() */ 2325 lua_pushstring(L, error_msg); 2326 lua_pushliteral(L, "\n"); 2327 lua_call(L, 2, 0); 2328 IGNORE_UNUSED_RESULT( 2329 luaL_dostring(L, "mg.write(debug.traceback(), '\\n')")); 2330 } else { 2331 printf("Lua error: [%s]\n", error_msg); 2332 IGNORE_UNUSED_RESULT( 2333 luaL_dostring(L, "print(debug.traceback(), '\\n')")); 2334 } 2335 /* TODO(lsm, low): leave the stack balanced */ 2336 2337 return 0; 2338} 2339 2340 2341static void 2342mg_exec_lua_script(struct mg_connection *conn, 2343 const char *path, 2344 const void **exports) 2345{ 2346 int i; 2347 lua_State *L; 2348 2349 /* Assume the script does not support keep_alive. The script may change this 2350 * by calling mg.keep_alive(true). */ 2351 conn->must_close = 1; 2352 2353 /* Execute a plain Lua script. */ 2354 if (path != NULL 2355 && (L = lua_newstate(lua_allocator, (void *)(conn->phys_ctx))) 2356 != NULL) { 2357 prepare_lua_environment( 2358 conn->phys_ctx, conn, NULL, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE); 2359 lua_pushcclosure(L, &lua_error_handler, 0); 2360 2361 if (exports != NULL) { 2362#if LUA_VERSION_NUM > 501 2363 lua_pushglobaltable(L); 2364 for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) { 2365 lua_CFunction func; 2366 lua_pushstring(L, (const char *)(exports[i])); 2367 *(const void **)(&func) = exports[i + 1]; 2368 lua_pushcclosure(L, func, 0); 2369 lua_rawset(L, -3); 2370 } 2371#else 2372 for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) { 2373 lua_CFunction func; 2374 const char *name = (const char *)(exports[i]); 2375 *(const void **)(&func) = exports[i + 1]; 2376 lua_register(L, name, func); 2377 } 2378#endif 2379 } 2380 2381 if (luaL_loadfile(L, path) != 0) { 2382 lua_error_handler(L); 2383 } 2384 lua_pcall(L, 0, 0, -2); 2385 lua_close(L); 2386 } 2387} 2388 2389 2390static int 2391handle_lsp_request(struct mg_connection *conn, 2392 const char *path, 2393 struct mg_file *filep, 2394 struct lua_State *ls) 2395{ 2396 void *p = NULL; 2397 lua_State *L = NULL; 2398 struct lsp_include_history *include_history; 2399 int error = 1; 2400 void *file_in_memory; /* TODO(low): remove when removing "file in memory" */ 2401 int (*run_lsp)(struct mg_connection *, 2402 const char *, 2403 const char *, 2404 int64_t, 2405 lua_State *, 2406 int); 2407 const char *addr; 2408 2409 /* mg_fopen opens the file and sets the size accordingly */ 2410 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) { 2411 2412 /* File not found or not accessible */ 2413 if (ls == NULL) { 2414 mg_send_http_error(conn, 2415 500, 2416 "Error: Cannot open script file %s", 2417 path); 2418 } else { 2419 luaL_error(ls, "Cannot include [%s]: not found", path); 2420 } 2421 2422 goto cleanup_handle_lsp_request; 2423 } 2424 2425#if defined(MG_USE_OPEN_FILE) 2426 /* The "file in memory" feature is going to be removed. For details see 2427 * https://groups.google.com/forum/#!topic/civetweb/h9HT4CmeYqI */ 2428 file_in_memory = filep->access.membuf; 2429#else 2430 file_in_memory = NULL; 2431#endif 2432 2433 /* Map file in memory (size is known). */ 2434 if (file_in_memory == NULL 2435 && (p = mmap(NULL, 2436 (size_t)filep->stat.size, 2437 PROT_READ, 2438 MAP_PRIVATE, 2439 fileno(filep->access.fp), 2440 0)) 2441 == MAP_FAILED) { 2442 2443 /* File was not already in memory, and mmap failed now. 2444 * Since wi have no data, show an error. */ 2445 if (ls == NULL) { 2446 /* No open Lua state - use generic error function */ 2447 mg_send_http_error( 2448 conn, 2449 500, 2450 "Error: Cannot open script\nFile %s can not be mapped", 2451 path); 2452 } else { 2453 /* Lua state exists - use Lua error function */ 2454 luaL_error(ls, 2455 "mmap(%s, %zu, %d): %s", 2456 path, 2457 (size_t)filep->stat.size, 2458 fileno(filep->access.fp), 2459 strerror(errno)); 2460 } 2461 2462 goto cleanup_handle_lsp_request; 2463 } 2464 2465 /* File content is now memory mapped. Get mapping address */ 2466 addr = (file_in_memory == NULL) ? (const char *)p 2467 : (const char *)file_in_memory; 2468 2469 /* Get a Lua state */ 2470 if (ls != NULL) { 2471 /* We got a Lua state as argument. Use it! */ 2472 L = ls; 2473 } else { 2474 /* We need to create a Lua state. */ 2475 L = lua_newstate(lua_allocator, (void *)(conn->phys_ctx)); 2476 if (L == NULL) { 2477 /* We neither got a Lua state from the command line, 2478 * nor did we succeed in creating our own state. 2479 * Show an error, and stop further processing of this request. */ 2480 mg_send_http_error( 2481 conn, 2482 500, 2483 "%s", 2484 "Error: Cannot execute script\nlua_newstate failed"); 2485 2486 goto cleanup_handle_lsp_request; 2487 } 2488 2489 /* New Lua state needs CivetWeb functions (e.g., the "mg" library). */ 2490 prepare_lua_environment( 2491 conn->phys_ctx, conn, NULL, L, path, LUA_ENV_TYPE_LUA_SERVER_PAGE); 2492 } 2493 2494 /* Get LSP include history table */ 2495 lua_pushlightuserdata(L, (void *)&lua_regkey_lsp_include_history); 2496 lua_gettable(L, LUA_REGISTRYINDEX); 2497 include_history = (struct lsp_include_history *)lua_touserdata(L, -1); 2498 2499 /* Store script name and increment depth */ 2500 include_history->depth++; 2501 include_history->script[include_history->depth] = path; 2502 2503 /* Lua state is ready to use now. */ 2504 /* Currently we have two different syntax options: 2505 * Either "classic" CivetWeb syntax: 2506 * <? code ?> 2507 * <?= expression ?> 2508 * Or "Kepler Syntax" 2509 * https://keplerproject.github.io/cgilua/manual.html#templates 2510 * <?lua chunk ?> 2511 * <?lua= expression ?> 2512 * <% chunk %> 2513 * <%= expression %> 2514 * 2515 * Two important differences are: 2516 * - In the "classic" CivetWeb syntax, the Lua Page had to send the HTTP 2517 * response headers itself. So the first lines are usually something like 2518 * HTTP/1.0 200 OK 2519 * Content-Type: text/html 2520 * followed by additional headers and an empty line, before the actual 2521 * Lua page in HTML syntax with <? code ?> tags. 2522 * The "Kepler"Syntax" does not send any HTTP header from the Lua Server 2523 * Page, but starts directly with <html> code - so it cannot influence 2524 * the HTTP response code, e.g., to send a 301 Moved Permanently. 2525 * Due to this difference, the same *.lp file cannot be used with the 2526 * same algorithm. 2527 * - The "Kepler Syntax" used to allow mixtures of Lua and HTML inside an 2528 * incomplete Lua block, e.g.: 2529 * <lua? for i=1,10 do ?><li><%= key %></li><lua? end ?> 2530 * This was not provided in "classic" CivetWeb syntax, but you had to use 2531 * <? for i=1,10 do mg.write("<li>"..i.."</li>") end ?> 2532 * instead. The parsing algorithm for "Kepler syntax" is more complex 2533 * than for "classic" CivetWeb syntax - TODO: check timing/performance. 2534 * 2535 * CivetWeb now can use both parsing methods, but needs to know what 2536 * parsing algorithm should be used. 2537 * Idea: Files starting with '<' are HTML files in "Kepler Syntax", except 2538 * "<?" which means "classic CivetWeb Syntax". 2539 * 2540 */ 2541 run_lsp = run_lsp_civetweb; 2542 if ((addr[0] == '<') && (addr[1] != '?')) { 2543 run_lsp = run_lsp_kepler; 2544 } 2545 2546 /* We're not sending HTTP headers here, Lua page must do it. */ 2547 error = 2548 run_lsp(conn, path, addr, filep->stat.size, L, include_history->depth); 2549 2550cleanup_handle_lsp_request: 2551 2552 if (L != NULL && ls == NULL) 2553 lua_close(L); 2554 if (p != NULL) 2555 munmap(p, filep->stat.size); 2556 (void)mg_fclose(&filep->access); 2557 2558 return error; 2559} 2560 2561 2562#if defined(USE_WEBSOCKET) 2563struct mg_shared_lua_websocket_list { 2564 struct lua_websock_data ws; 2565 struct mg_shared_lua_websocket_list *next; 2566}; 2567 2568 2569static void * 2570lua_websocket_new(const char *script, struct mg_connection *conn) 2571{ 2572 struct mg_shared_lua_websocket_list **shared_websock_list = 2573 &(conn->dom_ctx->shared_lua_websockets); 2574 struct lua_websock_data *ws; 2575 int err, ok = 0; 2576 2577 DEBUG_ASSERT(conn->lua_websocket_state == NULL); 2578 2579 /* lock list (mg_context global) */ 2580 mg_lock_context(conn->phys_ctx); 2581 while (*shared_websock_list) { 2582 /* check if ws already in list */ 2583 if (0 == strcmp(script, (*shared_websock_list)->ws.script)) { 2584 break; 2585 } 2586 shared_websock_list = &((*shared_websock_list)->next); 2587 } 2588 2589 if (*shared_websock_list == NULL) { 2590 /* add ws to list */ 2591 *shared_websock_list = 2592 (struct mg_shared_lua_websocket_list *)mg_calloc_ctx( 2593 sizeof(struct mg_shared_lua_websocket_list), 1, conn->phys_ctx); 2594 if (*shared_websock_list == NULL) { 2595 conn->must_close = 1; 2596 mg_unlock_context(conn->phys_ctx); 2597 mg_cry_internal(conn, 2598 "%s", 2599 "Cannot create shared websocket struct, OOM"); 2600 return NULL; 2601 } 2602 /* init ws list element */ 2603 ws = &(*shared_websock_list)->ws; 2604 ws->script = mg_strdup_ctx(script, conn->phys_ctx); 2605 if (!ws->script) { 2606 conn->must_close = 1; 2607 mg_unlock_context(conn->phys_ctx); 2608 mg_cry_internal(conn, 2609 "%s", 2610 "Cannot create shared websocket script, OOM"); 2611 return NULL; 2612 } 2613 pthread_mutex_init(&(ws->ws_mutex), &pthread_mutex_attr); 2614 (void)pthread_mutex_lock(&(ws->ws_mutex)); 2615 ws->state = lua_newstate(lua_allocator, (void *)(conn->phys_ctx)); 2616 ws->conn[0] = conn; 2617 ws->references = 1; 2618 prepare_lua_environment(conn->phys_ctx, 2619 conn, 2620 ws, 2621 ws->state, 2622 script, 2623 LUA_ENV_TYPE_LUA_WEBSOCKET); 2624 err = luaL_loadfile(ws->state, script); 2625 if (err != 0) { 2626 lua_cry(conn, err, ws->state, script, "load"); 2627 } 2628 err = lua_pcall(ws->state, 0, 0, 0); 2629 if (err != 0) { 2630 lua_cry(conn, err, ws->state, script, "init"); 2631 } 2632 } else { 2633 /* inc ref count */ 2634 ws = &(*shared_websock_list)->ws; 2635 (void)pthread_mutex_lock(&(ws->ws_mutex)); 2636 (*shared_websock_list)->ws.conn[(ws->references)++] = conn; 2637 } 2638 mg_unlock_context(conn->phys_ctx); 2639 2640 /* call add */ 2641 lua_getglobal(ws->state, "open"); 2642 lua_newtable(ws->state); 2643 prepare_lua_request_info(conn, ws->state); 2644 lua_pushstring(ws->state, "client"); 2645 lua_pushlightuserdata(ws->state, (void *)conn); 2646 lua_rawset(ws->state, -3); 2647 2648 err = lua_pcall(ws->state, 1, 1, 0); 2649 if (err != 0) { 2650 lua_cry(conn, err, ws->state, script, "open handler"); 2651 } else { 2652 if (lua_isboolean(ws->state, -1)) { 2653 ok = lua_toboolean(ws->state, -1); 2654 } 2655 lua_pop(ws->state, 1); 2656 } 2657 if (!ok) { 2658 /* Remove from ws connection list. */ 2659 /* TODO (mid): Check if list entry and Lua state needs to be deleted 2660 * (see websocket_close). */ 2661 (*shared_websock_list)->ws.conn[--(ws->references)] = 0; 2662 } 2663 2664 (void)pthread_mutex_unlock(&(ws->ws_mutex)); 2665 2666 return ok ? (void *)ws : NULL; 2667} 2668 2669 2670static int 2671lua_websocket_data(struct mg_connection *conn, 2672 int bits, 2673 char *data, 2674 size_t data_len, 2675 void *ws_arg) 2676{ 2677 struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg); 2678 int err, ok = 0; 2679 2680 DEBUG_ASSERT(ws != NULL); 2681 DEBUG_ASSERT(ws->state != NULL); 2682 2683 (void)pthread_mutex_lock(&(ws->ws_mutex)); 2684 2685 lua_getglobal(ws->state, "data"); 2686 lua_newtable(ws->state); 2687 lua_pushstring(ws->state, "client"); 2688 lua_pushlightuserdata(ws->state, (void *)conn); 2689 lua_rawset(ws->state, -3); 2690 lua_pushstring(ws->state, "bits"); /* TODO: dont use "bits" but fields with 2691 a meaning according to 2692 http://tools.ietf.org/html/rfc6455, 2693 section 5.2 */ 2694 lua_pushnumber(ws->state, bits); 2695 lua_rawset(ws->state, -3); 2696 lua_pushstring(ws->state, "data"); 2697 lua_pushlstring(ws->state, data, data_len); 2698 lua_rawset(ws->state, -3); 2699 2700 err = lua_pcall(ws->state, 1, 1, 0); 2701 if (err != 0) { 2702 lua_cry(conn, err, ws->state, ws->script, "data handler"); 2703 } else { 2704 if (lua_isboolean(ws->state, -1)) { 2705 ok = lua_toboolean(ws->state, -1); 2706 } 2707 lua_pop(ws->state, 1); 2708 } 2709 (void)pthread_mutex_unlock(&(ws->ws_mutex)); 2710 2711 return ok; 2712} 2713 2714 2715static int 2716lua_websocket_ready(struct mg_connection *conn, void *ws_arg) 2717{ 2718 struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg); 2719 int err, ok = 0; 2720 2721 DEBUG_ASSERT(ws != NULL); 2722 DEBUG_ASSERT(ws->state != NULL); 2723 2724 (void)pthread_mutex_lock(&(ws->ws_mutex)); 2725 2726 lua_getglobal(ws->state, "ready"); 2727 lua_newtable(ws->state); 2728 lua_pushstring(ws->state, "client"); 2729 lua_pushlightuserdata(ws->state, (void *)conn); 2730 lua_rawset(ws->state, -3); 2731 err = lua_pcall(ws->state, 1, 1, 0); 2732 if (err != 0) { 2733 lua_cry(conn, err, ws->state, ws->script, "ready handler"); 2734 } else { 2735 if (lua_isboolean(ws->state, -1)) { 2736 ok = lua_toboolean(ws->state, -1); 2737 } 2738 lua_pop(ws->state, 1); 2739 } 2740 2741 (void)pthread_mutex_unlock(&(ws->ws_mutex)); 2742 2743 return ok; 2744} 2745 2746 2747static void 2748lua_websocket_close(struct mg_connection *conn, void *ws_arg) 2749{ 2750 struct lua_websock_data *ws = (struct lua_websock_data *)(ws_arg); 2751 struct mg_shared_lua_websocket_list **shared_websock_list = 2752 &(conn->dom_ctx->shared_lua_websockets); 2753 int err = 0; 2754 unsigned i; 2755 2756 DEBUG_ASSERT(ws != NULL); 2757 DEBUG_ASSERT(ws->state != NULL); 2758 2759 (void)pthread_mutex_lock(&(ws->ws_mutex)); 2760 2761 lua_getglobal(ws->state, "close"); 2762 lua_newtable(ws->state); 2763 lua_pushstring(ws->state, "client"); 2764 lua_pushlightuserdata(ws->state, (void *)conn); 2765 lua_rawset(ws->state, -3); 2766 2767 err = lua_pcall(ws->state, 1, 0, 0); 2768 if (err != 0) { 2769 lua_cry(conn, err, ws->state, ws->script, "close handler"); 2770 } 2771 for (i = 0; i < ws->references; i++) { 2772 if (ws->conn[i] == conn) { 2773 ws->references--; 2774 ws->conn[i] = ws->conn[ws->references]; 2775 } 2776 } 2777 /* TODO: Delete lua_websock_data and remove it from the websocket list. 2778 This must only be done, when all connections are closed, and all 2779 asynchronous operations and timers are completed/expired. */ 2780 (void)shared_websock_list; /* shared_websock_list unused (see open TODO) */ 2781 2782 (void)pthread_mutex_unlock(&(ws->ws_mutex)); 2783} 2784#endif 2785 2786 2787static lua_State * 2788mg_prepare_lua_context_script(const char *file_name, 2789 struct mg_context *ctx, 2790 char *ebuf, 2791 size_t ebuf_len) 2792{ 2793 struct lua_State *L; 2794 int lua_ret; 2795 const char *lua_err_txt; 2796 2797 (void)ctx; 2798 2799 L = luaL_newstate(); 2800 if (L == NULL) { 2801 mg_snprintf(NULL, 2802 NULL, /* No truncation check for ebuf */ 2803 ebuf, 2804 ebuf_len, 2805 "Error: %s", 2806 "Cannot create Lua state"); 2807 return 0; 2808 } 2809 civetweb_open_lua_libs(L); 2810 2811 lua_ret = luaL_loadfile(L, file_name); 2812 if (lua_ret != LUA_OK) { 2813 /* Error when loading the file (e.g. file not found, 2814 * out of memory, ...) 2815 */ 2816 lua_err_txt = lua_tostring(L, -1); 2817 mg_snprintf(NULL, 2818 NULL, /* No truncation check for ebuf */ 2819 ebuf, 2820 ebuf_len, 2821 "Error loading file %s: %s\n", 2822 file_name, 2823 lua_err_txt); 2824 return 0; 2825 } 2826 2827 /* The script file is loaded, now call it */ 2828 lua_ret = lua_pcall(L, 2829 /* no arguments */ 0, 2830 /* zero or one return value */ 1, 2831 /* errors as strint return value */ 0); 2832 2833 if (lua_ret != LUA_OK) { 2834 /* Error when executing the script */ 2835 lua_err_txt = lua_tostring(L, -1); 2836 mg_snprintf(NULL, 2837 NULL, /* No truncation check for ebuf */ 2838 ebuf, 2839 ebuf_len, 2840 "Error running file %s: %s\n", 2841 file_name, 2842 lua_err_txt); 2843 return 0; 2844 } 2845 /* lua_close(L); must be done somewhere else */ 2846 2847 return L; 2848} 2849 2850 2851int 2852run_lua(const char *file_name) 2853{ 2854 int func_ret = EXIT_FAILURE; 2855 char ebuf[512] = {0}; 2856 lua_State *L = 2857 mg_prepare_lua_context_script(file_name, NULL, ebuf, sizeof(ebuf)); 2858 if (L) { 2859 /* Script executed */ 2860 if (lua_type(L, -1) == LUA_TNUMBER) { 2861 func_ret = (int)lua_tonumber(L, -1); 2862 } else { 2863 func_ret = EXIT_SUCCESS; 2864 } 2865 lua_close(L); 2866 } else { 2867 fprintf(stderr, "%s\n", ebuf); 2868 } 2869 return func_ret; 2870} 2871 2872 2873static void *lib_handle_uuid = NULL; 2874 2875static void 2876lua_init_optional_libraries(void) 2877{ 2878 /* shared Lua state */ 2879 lua_shared_init(); 2880 2881/* UUID library */ 2882#if !defined(_WIN32) 2883 lib_handle_uuid = dlopen("libuuid.so", RTLD_LAZY); 2884 pf_uuid_generate.p = 2885 (lib_handle_uuid ? dlsym(lib_handle_uuid, "uuid_generate") : 0); 2886#else 2887 pf_uuid_generate.p = 0; 2888#endif 2889} 2890 2891 2892static void 2893lua_exit_optional_libraries(void) 2894{ 2895/* UUID library */ 2896#if !defined(_WIN32) 2897 if (lib_handle_uuid) { 2898 dlclose(lib_handle_uuid); 2899 } 2900#endif 2901 pf_uuid_generate.p = 0; 2902 lib_handle_uuid = NULL; 2903 2904 /* shared Lua state */ 2905 lua_shared_exit(); 2906} 2907 2908 2909/* End of mod_lua.inl */ 2910