"Fossies" - the Fresh Open Source Software Archive

Member "lighttpd-1.4.54/src/mod_cml_lua.c" (27 May 2019, 8338 Bytes) of package /linux/www/lighttpd-1.4.54.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mod_cml_lua.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.4.53_vs_1.4.54.

    1 #include "first.h"
    2 
    3 #include <lua.h>
    4 #include <lualib.h>
    5 #include <lauxlib.h>
    6 
    7 #include <errno.h>
    8 #include <time.h>
    9 #include <string.h>
   10 
   11 #include "mod_cml_funcs.h"
   12 #include "mod_cml.h"
   13 
   14 #include "base.h"
   15 #include "chunk.h"
   16 #include "log.h"
   17 #include "http_header.h"
   18 #include "response.h"
   19 #include "stat_cache.h"
   20 
   21 #define HASHLEN 16
   22 typedef unsigned char HASH[HASHLEN];
   23 #define HASHHEXLEN 32
   24 typedef char HASHHEX[HASHHEXLEN+1];
   25 
   26 static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
   27     int curelem = lua_gettop(L);
   28     int result;
   29 
   30     lua_getglobal(L, varname);
   31 
   32     if (lua_isstring(L, curelem)) {
   33         buffer_copy_string(b, lua_tostring(L, curelem));
   34         result = 0;
   35     } else {
   36         result = -1;
   37     }
   38 
   39     lua_pop(L, 1);
   40     force_assert(curelem == lua_gettop(L));
   41     return result;
   42 }
   43 
   44 static int lua_to_c_is_table(lua_State *L, const char *varname) {
   45     int curelem = lua_gettop(L);
   46     int result;
   47 
   48     lua_getglobal(L, varname);
   49 
   50     result = lua_istable(L, curelem) ? 1 : 0;
   51 
   52     lua_pop(L, 1);
   53     force_assert(curelem == lua_gettop(L));
   54     return result;
   55 }
   56 
   57 static int c_to_lua_push(lua_State *L, int tbl, const char *key, size_t key_len, const char *val, size_t val_len) {
   58     lua_pushlstring(L, key, key_len);
   59     lua_pushlstring(L, val, val_len);
   60     lua_settable(L, tbl);
   61 
   62     return 0;
   63 }
   64 
   65 static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
   66     size_t is_key = 1;
   67     size_t i, len, klen = 0;
   68     char *key = NULL, *val = NULL;
   69 
   70     if (buffer_string_is_empty(qrystr)) return 0;
   71     key = qrystr->ptr;
   72 
   73     /* we need the \0 */
   74     len = buffer_string_length(qrystr);
   75     for (i = 0; i <= len; i++) {
   76         switch(qrystr->ptr[i]) {
   77         case '=':
   78             if (is_key) {
   79                 val = qrystr->ptr + i + 1;
   80                 klen = (size_t)(val - key - 1);
   81                 is_key = 0;
   82             }
   83 
   84             break;
   85         case '&':
   86         case '\0': /* fin symbol */
   87             if (!is_key) {
   88                 /* we need at least a = since the last & */
   89                 c_to_lua_push(L, tbl,
   90                     key, klen,
   91                     val, (size_t)(qrystr->ptr + i - val));
   92             }
   93 
   94             key = qrystr->ptr + i + 1;
   95             val = NULL;
   96             is_key = 1;
   97             break;
   98         }
   99     }
  100 
  101     return 0;
  102 }
  103 
  104 int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
  105     lua_State *L;
  106     int ret = -1;
  107     buffer *b;
  108 
  109     b = buffer_init();
  110     /* push the lua file to the interpreter and see what happends */
  111     L = luaL_newstate();
  112     luaL_openlibs(L);
  113 
  114     /* register functions */
  115     lua_register(L, "md5", f_crypto_md5);
  116     lua_register(L, "file_mtime", f_file_mtime);
  117     lua_register(L, "file_isreg", f_file_isreg);
  118     lua_register(L, "file_isdir", f_file_isreg);
  119     lua_register(L, "dir_files", f_dir_files);
  120 
  121 #ifdef USE_MEMCACHED
  122     lua_pushlightuserdata(L, p->conf.memc);
  123     lua_pushcclosure(L, f_memcache_get_long, 1);
  124     lua_setglobal(L, "memcache_get_long");
  125 
  126     lua_pushlightuserdata(L, p->conf.memc);
  127     lua_pushcclosure(L, f_memcache_get_string, 1);
  128     lua_setglobal(L, "memcache_get_string");
  129 
  130     lua_pushlightuserdata(L, p->conf.memc);
  131     lua_pushcclosure(L, f_memcache_exists, 1);
  132     lua_setglobal(L, "memcache_exists");
  133 #endif
  134 
  135     /* register CGI environment */
  136     lua_newtable(L);
  137     {
  138         int header_tbl = lua_gettop(L);
  139 
  140         c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
  141         c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
  142         c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
  143         c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
  144         if (!buffer_string_is_empty(con->request.pathinfo)) {
  145             c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
  146         }
  147 
  148         c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
  149         c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
  150     }
  151     lua_setglobal(L, "request");
  152 
  153     /* register GET parameter */
  154     lua_newtable(L);
  155     cache_export_get_params(L, lua_gettop(L), con->uri.query);
  156     lua_setglobal(L, "get");
  157 
  158     /* 2 default constants */
  159     lua_pushinteger(L, 0);
  160     lua_setglobal(L, "CACHE_HIT");
  161 
  162     lua_pushinteger(L, 1);
  163     lua_setglobal(L, "CACHE_MISS");
  164 
  165     /* load lua program */
  166     ret = luaL_loadfile(L, fn->ptr);
  167     if (0 != ret) {
  168         log_error_write(srv, __FILE__, __LINE__, "sbsS",
  169             "failed loading cml_lua script",
  170             fn,
  171             ":",
  172             lua_tostring(L, -1));
  173         goto error;
  174     }
  175 
  176     if (lua_pcall(L, 0, 1, 0)) {
  177         log_error_write(srv, __FILE__, __LINE__, "sbsS",
  178             "failed running cml_lua script",
  179             fn,
  180             ":",
  181             lua_tostring(L, -1));
  182         goto error;
  183     }
  184 
  185     /* get return value */
  186     ret = (int)lua_tointeger(L, -1);
  187     lua_pop(L, 1);
  188 
  189     /* fetch the data from lua */
  190     lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
  191 
  192     if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
  193         http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
  194     }
  195 
  196     if (ret == 0) {
  197         /* up to now it is a cache-hit, check if all files exist */
  198 
  199         int curelem;
  200         time_t mtime = 0;
  201 
  202         if (!lua_to_c_is_table(L, "output_include")) {
  203             log_error_write(srv, __FILE__, __LINE__, "s",
  204                 "output_include is missing or not a table");
  205             ret = -1;
  206 
  207             goto error;
  208         }
  209 
  210         lua_getglobal(L, "output_include");
  211         curelem = lua_gettop(L);
  212 
  213         /* HOW-TO build a etag ?
  214          * as we don't just have one file we have to take the stat()
  215          * from all base files, merge them and build the etag from
  216          * it later.
  217          *
  218          * The mtime of the content is the mtime of the freshest base file
  219          *
  220          * */
  221 
  222         lua_pushnil(L);  /* first key */
  223         while (lua_next(L, curelem) != 0) {
  224             /* key' is at index -2 and value' at index -1 */
  225 
  226             if (lua_isstring(L, -1)) {
  227                 const char *s = lua_tostring(L, -1);
  228                 struct stat st;
  229                 int fd;
  230 
  231                 /* the file is relative, make it absolute */
  232                 if (s[0] != '/') {
  233                     buffer_copy_buffer(b, p->basedir);
  234                     buffer_append_string(b, lua_tostring(L, -1));
  235                 } else {
  236                     buffer_copy_string(b, lua_tostring(L, -1));
  237                 }
  238 
  239                 fd = stat_cache_open_rdonly_fstat(b, &st, con->conf.follow_symlink);
  240                 if (fd < 0) {
  241                     /* stat failed */
  242 
  243                     switch(errno) {
  244                     case ENOENT:
  245                         /* a file is missing, call the handler to generate it */
  246                         if (!buffer_string_is_empty(p->trigger_handler)) {
  247                             ret = 1; /* cache-miss */
  248 
  249                             log_error_write(srv, __FILE__, __LINE__, "s",
  250                                     "a file is missing, calling handler");
  251 
  252                             break;
  253                         } else {
  254                             /* handler not set -> 500 */
  255                             ret = -1;
  256 
  257                             log_error_write(srv, __FILE__, __LINE__, "s",
  258                                     "a file missing and no handler set");
  259 
  260                             break;
  261                         }
  262                         break;
  263                     default:
  264                         break;
  265                     }
  266                 } else {
  267                     chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size);
  268                     if (st.st_mtime > mtime) mtime = st.st_mtime;
  269                 }
  270             } else {
  271                 /* not a string */
  272                 ret = -1;
  273                 log_error_write(srv, __FILE__, __LINE__, "s",
  274                         "not a string");
  275                 break;
  276             }
  277 
  278             lua_pop(L, 1);  /* removes value'; keeps key' for next iteration */
  279         }
  280 
  281         lua_settop(L, curelem - 1);
  282 
  283         if (ret == 0) {
  284             buffer *vb = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"));
  285             if (NULL == vb) { /* no Last-Modified specified */
  286                 char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
  287                 if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
  288                 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
  289                 http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
  290                 vb = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"));
  291                 force_assert(NULL != vb);
  292             }
  293 
  294             con->file_finished = 1;
  295 
  296             if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, vb)) {
  297                 /* ok, the client already has our content,
  298                  * no need to send it again */
  299 
  300                 chunkqueue_reset(con->write_queue);
  301                 ret = 0; /* cache-hit */
  302             }
  303         } else {
  304             chunkqueue_reset(con->write_queue);
  305         }
  306     }
  307 
  308     if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) {
  309         /* cache-miss */
  310         buffer_copy_buffer(con->uri.path, p->baseurl);
  311         buffer_append_string_buffer(con->uri.path, p->trigger_handler);
  312 
  313         buffer_copy_buffer(con->physical.path, p->basedir);
  314         buffer_append_string_buffer(con->physical.path, p->trigger_handler);
  315 
  316         chunkqueue_reset(con->write_queue);
  317     }
  318 
  319 error:
  320     lua_close(L);
  321 
  322     buffer_free(b);
  323 
  324     return ret /* cache-error */;
  325 }