Annotation of embedaddon/lighttpd/src/mod_cml_lua.c, revision 1.1.1.2

1.1       misho       1: #include "mod_cml.h"
                      2: #include "mod_cml_funcs.h"
                      3: #include "log.h"
                      4: #include "stream.h"
                      5: 
                      6: #include "stat_cache.h"
                      7: 
                      8: #include <assert.h>
                      9: #include <stdio.h>
                     10: #include <errno.h>
                     11: #include <time.h>
                     12: #include <string.h>
                     13: 
                     14: #define HASHLEN 16
                     15: typedef unsigned char HASH[HASHLEN];
                     16: #define HASHHEXLEN 32
                     17: typedef char HASHHEX[HASHHEXLEN+1];
                     18: #ifdef USE_OPENSSL
                     19: #define IN const
                     20: #else
                     21: #define IN
                     22: #endif
                     23: #define OUT
                     24: 
                     25: #ifdef HAVE_LUA_H
                     26: 
                     27: #include <lua.h>
                     28: #include <lualib.h>
                     29: #include <lauxlib.h>
                     30: 
                     31: typedef struct {
                     32:        stream st;
                     33:        int done;
                     34: } readme;
                     35: 
                     36: static const char * load_file(lua_State *L, void *data, size_t *size) {
                     37:        readme *rm = data;
                     38: 
                     39:        UNUSED(L);
                     40: 
                     41:        if (rm->done) return 0;
                     42: 
                     43:        *size = rm->st.size;
                     44:        rm->done = 1;
                     45:        return rm->st.start;
                     46: }
                     47: 
                     48: static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
                     49:        int curelem;
                     50: 
                     51:        lua_pushstring(L, varname);
                     52: 
                     53:        curelem = lua_gettop(L);
                     54:        lua_gettable(L, LUA_GLOBALSINDEX);
                     55: 
                     56:        /* it should be a table */
                     57:        if (!lua_isstring(L, curelem)) {
                     58:                lua_settop(L, curelem - 1);
                     59: 
                     60:                return -1;
                     61:        }
                     62: 
                     63:        buffer_copy_string(b, lua_tostring(L, curelem));
                     64: 
                     65:        lua_pop(L, 1);
                     66: 
1.1.1.2 ! misho      67:        force_assert(curelem - 1 == lua_gettop(L));
1.1       misho      68: 
                     69:        return 0;
                     70: }
                     71: 
                     72: static int lua_to_c_is_table(lua_State *L, const char *varname) {
                     73:        int curelem;
                     74: 
                     75:        lua_pushstring(L, varname);
                     76: 
                     77:        curelem = lua_gettop(L);
                     78:        lua_gettable(L, LUA_GLOBALSINDEX);
                     79: 
                     80:        /* it should be a table */
                     81:        if (!lua_istable(L, curelem)) {
                     82:                lua_settop(L, curelem - 1);
                     83: 
                     84:                return 0;
                     85:        }
                     86: 
                     87:        lua_settop(L, curelem - 1);
                     88: 
1.1.1.2 ! misho      89:        force_assert(curelem - 1 == lua_gettop(L));
1.1       misho      90: 
                     91:        return 1;
                     92: }
                     93: 
                     94: 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) {
                     95:        lua_pushlstring(L, key, key_len);
                     96:        lua_pushlstring(L, val, val_len);
                     97:        lua_settable(L, tbl);
                     98: 
                     99:        return 0;
                    100: }
                    101: 
                    102: 
                    103: static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
                    104:        size_t is_key = 1;
                    105:        size_t i;
                    106:        char *key = NULL, *val = NULL;
                    107: 
                    108:        key = qrystr->ptr;
                    109: 
                    110:        /* we need the \0 */
                    111:        for (i = 0; i < qrystr->used; i++) {
                    112:                switch(qrystr->ptr[i]) {
                    113:                case '=':
                    114:                        if (is_key) {
                    115:                                val = qrystr->ptr + i + 1;
                    116: 
                    117:                                qrystr->ptr[i] = '\0';
                    118: 
                    119:                                is_key = 0;
                    120:                        }
                    121: 
                    122:                        break;
                    123:                case '&':
                    124:                case '\0': /* fin symbol */
                    125:                        if (!is_key) {
                    126:                                /* we need at least a = since the last & */
                    127: 
                    128:                                /* terminate the value */
                    129:                                qrystr->ptr[i] = '\0';
                    130: 
                    131:                                c_to_lua_push(L, tbl,
                    132:                                              key, strlen(key),
                    133:                                              val, strlen(val));
                    134:                        }
                    135: 
                    136:                        key = qrystr->ptr + i + 1;
                    137:                        val = NULL;
                    138:                        is_key = 1;
                    139:                        break;
                    140:                }
                    141:        }
                    142: 
                    143:        return 0;
                    144: }
                    145: #if 0
                    146: int cache_export_cookie_params(server *srv, connection *con, plugin_data *p) {
                    147:        data_unset *d;
                    148: 
                    149:        UNUSED(srv);
                    150: 
                    151:        if (NULL != (d = array_get_element(con->request.headers, "Cookie"))) {
                    152:                data_string *ds = (data_string *)d;
                    153:                size_t key = 0, value = 0;
                    154:                size_t is_key = 1, is_sid = 0;
                    155:                size_t i;
                    156: 
                    157:                /* found COOKIE */
                    158:                if (!DATA_IS_STRING(d)) return -1;
                    159:                if (ds->value->used == 0) return -1;
                    160: 
                    161:                if (ds->value->ptr[0] == '\0' ||
                    162:                    ds->value->ptr[0] == '=' ||
                    163:                    ds->value->ptr[0] == ';') return -1;
                    164: 
                    165:                buffer_reset(p->session_id);
                    166:                for (i = 0; i < ds->value->used; i++) {
                    167:                        switch(ds->value->ptr[i]) {
                    168:                        case '=':
                    169:                                if (is_key) {
                    170:                                        if (0 == strncmp(ds->value->ptr + key, "PHPSESSID", i - key)) {
                    171:                                                /* found PHP-session-id-key */
                    172:                                                is_sid = 1;
                    173:                                        }
                    174:                                        value = i + 1;
                    175: 
                    176:                                        is_key = 0;
                    177:                                }
                    178: 
                    179:                                break;
                    180:                        case ';':
                    181:                                if (is_sid) {
                    182:                                        buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
                    183:                                }
                    184: 
                    185:                                is_sid = 0;
                    186:                                key = i + 1;
                    187:                                value = 0;
                    188:                                is_key = 1;
                    189:                                break;
                    190:                        case ' ':
                    191:                                if (is_key == 1 && key == i) key = i + 1;
                    192:                                if (is_key == 0 && value == i) value = i + 1;
                    193:                                break;
                    194:                        case '\0':
                    195:                                if (is_sid) {
                    196:                                        buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
                    197:                                }
                    198:                                /* fin */
                    199:                                break;
                    200:                        }
                    201:                }
                    202:        }
                    203: 
                    204:        return 0;
                    205: }
                    206: #endif
                    207: 
                    208: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
                    209:        lua_State *L;
                    210:        readme rm;
                    211:        int ret = -1;
1.1.1.2 ! misho     212:        buffer *b;
1.1       misho     213:        int header_tbl = 0;
                    214: 
                    215:        rm.done = 0;
1.1.1.2 ! misho     216:        if (-1 == stream_open(&rm.st, fn)) {
        !           217:                log_error_write(srv, __FILE__, __LINE__, "sbss",
        !           218:                                "opening lua cml file ", fn, "failed:", strerror(errno));
        !           219:                return -1;
        !           220:        }
1.1       misho     221: 
1.1.1.2 ! misho     222:        b = buffer_init();
1.1       misho     223:        /* push the lua file to the interpreter and see what happends */
                    224:        L = luaL_newstate();
                    225:        luaL_openlibs(L);
                    226: 
                    227:        /* register functions */
                    228:        lua_register(L, "md5", f_crypto_md5);
                    229:        lua_register(L, "file_mtime", f_file_mtime);
                    230:        lua_register(L, "file_isreg", f_file_isreg);
                    231:        lua_register(L, "file_isdir", f_file_isreg);
                    232:        lua_register(L, "dir_files", f_dir_files);
                    233: 
                    234: #ifdef HAVE_MEMCACHE_H
                    235:        lua_pushliteral(L, "memcache_get_long");
                    236:        lua_pushlightuserdata(L, p->conf.mc);
                    237:        lua_pushcclosure(L, f_memcache_get_long, 1);
                    238:        lua_settable(L, LUA_GLOBALSINDEX);
                    239: 
                    240:        lua_pushliteral(L, "memcache_get_string");
                    241:        lua_pushlightuserdata(L, p->conf.mc);
                    242:        lua_pushcclosure(L, f_memcache_get_string, 1);
                    243:        lua_settable(L, LUA_GLOBALSINDEX);
                    244: 
                    245:        lua_pushliteral(L, "memcache_exists");
                    246:        lua_pushlightuserdata(L, p->conf.mc);
                    247:        lua_pushcclosure(L, f_memcache_exists, 1);
                    248:        lua_settable(L, LUA_GLOBALSINDEX);
                    249: #endif
                    250:        /* register CGI environment */
                    251:        lua_pushliteral(L, "request");
                    252:        lua_newtable(L);
                    253:        lua_settable(L, LUA_GLOBALSINDEX);
                    254: 
                    255:        lua_pushliteral(L, "request");
                    256:        header_tbl = lua_gettop(L);
                    257:        lua_gettable(L, LUA_GLOBALSINDEX);
                    258: 
                    259:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
                    260:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
                    261:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
                    262:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
                    263:        if (!buffer_is_empty(con->request.pathinfo)) {
                    264:                c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
                    265:        }
                    266: 
                    267:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
                    268:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
                    269: 
                    270:        /* register GET parameter */
                    271:        lua_pushliteral(L, "get");
                    272:        lua_newtable(L);
                    273:        lua_settable(L, LUA_GLOBALSINDEX);
                    274: 
                    275:        lua_pushliteral(L, "get");
                    276:        header_tbl = lua_gettop(L);
                    277:        lua_gettable(L, LUA_GLOBALSINDEX);
                    278: 
                    279:        buffer_copy_string_buffer(b, con->uri.query);
                    280:        cache_export_get_params(L, header_tbl, b);
                    281:        buffer_reset(b);
                    282: 
                    283:        /* 2 default constants */
                    284:        lua_pushliteral(L, "CACHE_HIT");
                    285:        lua_pushnumber(L, 0);
                    286:        lua_settable(L, LUA_GLOBALSINDEX);
                    287: 
                    288:        lua_pushliteral(L, "CACHE_MISS");
                    289:        lua_pushnumber(L, 1);
                    290:        lua_settable(L, LUA_GLOBALSINDEX);
                    291: 
                    292:        /* load lua program */
                    293:        if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) {
                    294:                log_error_write(srv, __FILE__, __LINE__, "s",
                    295:                                lua_tostring(L,-1));
                    296: 
                    297:                goto error;
                    298:        }
                    299: 
                    300:        /* get return value */
                    301:        ret = (int)lua_tonumber(L, -1);
                    302:        lua_pop(L, 1);
                    303: 
                    304:        /* fetch the data from lua */
                    305:        lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
                    306: 
                    307:        if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
                    308:                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
                    309:        }
                    310: 
                    311:        if (ret == 0) {
                    312:                /* up to now it is a cache-hit, check if all files exist */
                    313: 
                    314:                int curelem;
                    315:                time_t mtime = 0;
                    316: 
                    317:                if (!lua_to_c_is_table(L, "output_include")) {
                    318:                        log_error_write(srv, __FILE__, __LINE__, "s",
                    319:                                "output_include is missing or not a table");
                    320:                        ret = -1;
                    321: 
                    322:                        goto error;
                    323:                }
                    324: 
                    325:                lua_pushstring(L, "output_include");
                    326: 
                    327:                curelem = lua_gettop(L);
                    328:                lua_gettable(L, LUA_GLOBALSINDEX);
                    329: 
                    330:                /* HOW-TO build a etag ?
                    331:                 * as we don't just have one file we have to take the stat()
                    332:                 * from all base files, merge them and build the etag from
                    333:                 * it later.
                    334:                 *
                    335:                 * The mtime of the content is the mtime of the freshest base file
                    336:                 *
                    337:                 * */
                    338: 
                    339:                lua_pushnil(L);  /* first key */
                    340:                while (lua_next(L, curelem) != 0) {
                    341:                        stat_cache_entry *sce = NULL;
                    342:                        /* key' is at index -2 and value' at index -1 */
                    343: 
                    344:                        if (lua_isstring(L, -1)) {
                    345:                                const char *s = lua_tostring(L, -1);
                    346: 
                    347:                                /* the file is relative, make it absolute */
                    348:                                if (s[0] != '/') {
                    349:                                        buffer_copy_string_buffer(b, p->basedir);
                    350:                                        buffer_append_string(b, lua_tostring(L, -1));
                    351:                                } else {
                    352:                                        buffer_copy_string(b, lua_tostring(L, -1));
                    353:                                }
                    354: 
                    355:                                if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
                    356:                                        /* stat failed */
                    357: 
                    358:                                        switch(errno) {
                    359:                                        case ENOENT:
                    360:                                                /* a file is missing, call the handler to generate it */
                    361:                                                if (!buffer_is_empty(p->trigger_handler)) {
                    362:                                                        ret = 1; /* cache-miss */
                    363: 
                    364:                                                        log_error_write(srv, __FILE__, __LINE__, "s",
                    365:                                                                        "a file is missing, calling handler");
                    366: 
                    367:                                                        break;
                    368:                                                } else {
                    369:                                                        /* handler not set -> 500 */
                    370:                                                        ret = -1;
                    371: 
                    372:                                                        log_error_write(srv, __FILE__, __LINE__, "s",
                    373:                                                                        "a file missing and no handler set");
                    374: 
                    375:                                                        break;
                    376:                                                }
                    377:                                                break;
                    378:                                        default:
                    379:                                                break;
                    380:                                        }
                    381:                                } else {
                    382:                                        chunkqueue_append_file(con->write_queue, b, 0, sce->st.st_size);
                    383:                                        if (sce->st.st_mtime > mtime) mtime = sce->st.st_mtime;
                    384:                                }
                    385:                        } else {
                    386:                                /* not a string */
                    387:                                ret = -1;
                    388:                                log_error_write(srv, __FILE__, __LINE__, "s",
                    389:                                                "not a string");
                    390:                                break;
                    391:                        }
                    392: 
                    393:                        lua_pop(L, 1);  /* removes value'; keeps key' for next iteration */
                    394:                }
                    395: 
                    396:                lua_settop(L, curelem - 1);
                    397: 
                    398:                if (ret == 0) {
                    399:                        data_string *ds;
                    400:                        char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
                    401:                        buffer tbuf;
                    402: 
                    403:                        con->file_finished = 1;
                    404: 
                    405:                        ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
1.1.1.2 ! misho     406:                        if (0 == mtime) mtime = time(NULL); /* default last-modified to now */
1.1       misho     407: 
                    408:                        /* no Last-Modified specified */
1.1.1.2 ! misho     409:                        if (NULL == ds) {
1.1       misho     410: 
                    411:                                strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
                    412: 
                    413:                                response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
                    414: 
                    415:                                tbuf.ptr = timebuf;
                    416:                                tbuf.used = sizeof(timebuf);
                    417:                                tbuf.size = sizeof(timebuf);
1.1.1.2 ! misho     418:                        } else {
1.1       misho     419:                                tbuf.ptr = ds->value->ptr;
                    420:                                tbuf.used = ds->value->used;
                    421:                                tbuf.size = ds->value->size;
                    422:                        }
                    423: 
                    424:                        if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) {
                    425:                                /* ok, the client already has our content,
                    426:                                 * no need to send it again */
                    427: 
                    428:                                chunkqueue_reset(con->write_queue);
                    429:                                ret = 0; /* cache-hit */
                    430:                        }
                    431:                } else {
                    432:                        chunkqueue_reset(con->write_queue);
                    433:                }
                    434:        }
                    435: 
                    436:        if (ret == 1 && !buffer_is_empty(p->trigger_handler)) {
                    437:                /* cache-miss */
                    438:                buffer_copy_string_buffer(con->uri.path, p->baseurl);
                    439:                buffer_append_string_buffer(con->uri.path, p->trigger_handler);
                    440: 
                    441:                buffer_copy_string_buffer(con->physical.path, p->basedir);
                    442:                buffer_append_string_buffer(con->physical.path, p->trigger_handler);
                    443: 
                    444:                chunkqueue_reset(con->write_queue);
                    445:        }
                    446: 
                    447: error:
                    448:        lua_close(L);
                    449: 
                    450:        stream_close(&rm.st);
                    451:        buffer_free(b);
                    452: 
                    453:        return ret /* cache-error */;
                    454: }
                    455: #else
                    456: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
                    457:        UNUSED(srv);
                    458:        UNUSED(con);
                    459:        UNUSED(p);
                    460:        UNUSED(fn);
                    461:        /* error */
                    462:        return -1;
                    463: }
                    464: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>