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

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: 
                     67:        assert(curelem - 1 == lua_gettop(L));
                     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: 
                     89:        assert(curelem - 1 == lua_gettop(L));
                     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;
                    212:        buffer *b = buffer_init();
                    213:        int header_tbl = 0;
                    214: 
                    215:        rm.done = 0;
                    216:        stream_open(&rm.st, fn);
                    217: 
                    218:        /* push the lua file to the interpreter and see what happends */
                    219:        L = luaL_newstate();
                    220:        luaL_openlibs(L);
                    221: 
                    222:        /* register functions */
                    223:        lua_register(L, "md5", f_crypto_md5);
                    224:        lua_register(L, "file_mtime", f_file_mtime);
                    225:        lua_register(L, "file_isreg", f_file_isreg);
                    226:        lua_register(L, "file_isdir", f_file_isreg);
                    227:        lua_register(L, "dir_files", f_dir_files);
                    228: 
                    229: #ifdef HAVE_MEMCACHE_H
                    230:        lua_pushliteral(L, "memcache_get_long");
                    231:        lua_pushlightuserdata(L, p->conf.mc);
                    232:        lua_pushcclosure(L, f_memcache_get_long, 1);
                    233:        lua_settable(L, LUA_GLOBALSINDEX);
                    234: 
                    235:        lua_pushliteral(L, "memcache_get_string");
                    236:        lua_pushlightuserdata(L, p->conf.mc);
                    237:        lua_pushcclosure(L, f_memcache_get_string, 1);
                    238:        lua_settable(L, LUA_GLOBALSINDEX);
                    239: 
                    240:        lua_pushliteral(L, "memcache_exists");
                    241:        lua_pushlightuserdata(L, p->conf.mc);
                    242:        lua_pushcclosure(L, f_memcache_exists, 1);
                    243:        lua_settable(L, LUA_GLOBALSINDEX);
                    244: #endif
                    245:        /* register CGI environment */
                    246:        lua_pushliteral(L, "request");
                    247:        lua_newtable(L);
                    248:        lua_settable(L, LUA_GLOBALSINDEX);
                    249: 
                    250:        lua_pushliteral(L, "request");
                    251:        header_tbl = lua_gettop(L);
                    252:        lua_gettable(L, LUA_GLOBALSINDEX);
                    253: 
                    254:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
                    255:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
                    256:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
                    257:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
                    258:        if (!buffer_is_empty(con->request.pathinfo)) {
                    259:                c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
                    260:        }
                    261: 
                    262:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
                    263:        c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
                    264: 
                    265:        /* register GET parameter */
                    266:        lua_pushliteral(L, "get");
                    267:        lua_newtable(L);
                    268:        lua_settable(L, LUA_GLOBALSINDEX);
                    269: 
                    270:        lua_pushliteral(L, "get");
                    271:        header_tbl = lua_gettop(L);
                    272:        lua_gettable(L, LUA_GLOBALSINDEX);
                    273: 
                    274:        buffer_copy_string_buffer(b, con->uri.query);
                    275:        cache_export_get_params(L, header_tbl, b);
                    276:        buffer_reset(b);
                    277: 
                    278:        /* 2 default constants */
                    279:        lua_pushliteral(L, "CACHE_HIT");
                    280:        lua_pushnumber(L, 0);
                    281:        lua_settable(L, LUA_GLOBALSINDEX);
                    282: 
                    283:        lua_pushliteral(L, "CACHE_MISS");
                    284:        lua_pushnumber(L, 1);
                    285:        lua_settable(L, LUA_GLOBALSINDEX);
                    286: 
                    287:        /* load lua program */
                    288:        if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) {
                    289:                log_error_write(srv, __FILE__, __LINE__, "s",
                    290:                                lua_tostring(L,-1));
                    291: 
                    292:                goto error;
                    293:        }
                    294: 
                    295:        /* get return value */
                    296:        ret = (int)lua_tonumber(L, -1);
                    297:        lua_pop(L, 1);
                    298: 
                    299:        /* fetch the data from lua */
                    300:        lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
                    301: 
                    302:        if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
                    303:                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
                    304:        }
                    305: 
                    306:        if (ret == 0) {
                    307:                /* up to now it is a cache-hit, check if all files exist */
                    308: 
                    309:                int curelem;
                    310:                time_t mtime = 0;
                    311: 
                    312:                if (!lua_to_c_is_table(L, "output_include")) {
                    313:                        log_error_write(srv, __FILE__, __LINE__, "s",
                    314:                                "output_include is missing or not a table");
                    315:                        ret = -1;
                    316: 
                    317:                        goto error;
                    318:                }
                    319: 
                    320:                lua_pushstring(L, "output_include");
                    321: 
                    322:                curelem = lua_gettop(L);
                    323:                lua_gettable(L, LUA_GLOBALSINDEX);
                    324: 
                    325:                /* HOW-TO build a etag ?
                    326:                 * as we don't just have one file we have to take the stat()
                    327:                 * from all base files, merge them and build the etag from
                    328:                 * it later.
                    329:                 *
                    330:                 * The mtime of the content is the mtime of the freshest base file
                    331:                 *
                    332:                 * */
                    333: 
                    334:                lua_pushnil(L);  /* first key */
                    335:                while (lua_next(L, curelem) != 0) {
                    336:                        stat_cache_entry *sce = NULL;
                    337:                        /* key' is at index -2 and value' at index -1 */
                    338: 
                    339:                        if (lua_isstring(L, -1)) {
                    340:                                const char *s = lua_tostring(L, -1);
                    341: 
                    342:                                /* the file is relative, make it absolute */
                    343:                                if (s[0] != '/') {
                    344:                                        buffer_copy_string_buffer(b, p->basedir);
                    345:                                        buffer_append_string(b, lua_tostring(L, -1));
                    346:                                } else {
                    347:                                        buffer_copy_string(b, lua_tostring(L, -1));
                    348:                                }
                    349: 
                    350:                                if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
                    351:                                        /* stat failed */
                    352: 
                    353:                                        switch(errno) {
                    354:                                        case ENOENT:
                    355:                                                /* a file is missing, call the handler to generate it */
                    356:                                                if (!buffer_is_empty(p->trigger_handler)) {
                    357:                                                        ret = 1; /* cache-miss */
                    358: 
                    359:                                                        log_error_write(srv, __FILE__, __LINE__, "s",
                    360:                                                                        "a file is missing, calling handler");
                    361: 
                    362:                                                        break;
                    363:                                                } else {
                    364:                                                        /* handler not set -> 500 */
                    365:                                                        ret = -1;
                    366: 
                    367:                                                        log_error_write(srv, __FILE__, __LINE__, "s",
                    368:                                                                        "a file missing and no handler set");
                    369: 
                    370:                                                        break;
                    371:                                                }
                    372:                                                break;
                    373:                                        default:
                    374:                                                break;
                    375:                                        }
                    376:                                } else {
                    377:                                        chunkqueue_append_file(con->write_queue, b, 0, sce->st.st_size);
                    378:                                        if (sce->st.st_mtime > mtime) mtime = sce->st.st_mtime;
                    379:                                }
                    380:                        } else {
                    381:                                /* not a string */
                    382:                                ret = -1;
                    383:                                log_error_write(srv, __FILE__, __LINE__, "s",
                    384:                                                "not a string");
                    385:                                break;
                    386:                        }
                    387: 
                    388:                        lua_pop(L, 1);  /* removes value'; keeps key' for next iteration */
                    389:                }
                    390: 
                    391:                lua_settop(L, curelem - 1);
                    392: 
                    393:                if (ret == 0) {
                    394:                        data_string *ds;
                    395:                        char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
                    396:                        buffer tbuf;
                    397: 
                    398:                        con->file_finished = 1;
                    399: 
                    400:                        ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
                    401: 
                    402:                        /* no Last-Modified specified */
                    403:                        if ((mtime) && (NULL == ds)) {
                    404: 
                    405:                                strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
                    406: 
                    407:                                response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
                    408: 
                    409: 
                    410:                                tbuf.ptr = timebuf;
                    411:                                tbuf.used = sizeof(timebuf);
                    412:                                tbuf.size = sizeof(timebuf);
                    413:                        } else if (ds) {
                    414:                                tbuf.ptr = ds->value->ptr;
                    415:                                tbuf.used = ds->value->used;
                    416:                                tbuf.size = ds->value->size;
                    417:                        } else {
                    418:                                tbuf.size = 0;
                    419:                                tbuf.used = 0;
                    420:                                tbuf.ptr = NULL;
                    421:                        }
                    422: 
                    423:                        if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) {
                    424:                                /* ok, the client already has our content,
                    425:                                 * no need to send it again */
                    426: 
                    427:                                chunkqueue_reset(con->write_queue);
                    428:                                ret = 0; /* cache-hit */
                    429:                        }
                    430:                } else {
                    431:                        chunkqueue_reset(con->write_queue);
                    432:                }
                    433:        }
                    434: 
                    435:        if (ret == 1 && !buffer_is_empty(p->trigger_handler)) {
                    436:                /* cache-miss */
                    437:                buffer_copy_string_buffer(con->uri.path, p->baseurl);
                    438:                buffer_append_string_buffer(con->uri.path, p->trigger_handler);
                    439: 
                    440:                buffer_copy_string_buffer(con->physical.path, p->basedir);
                    441:                buffer_append_string_buffer(con->physical.path, p->trigger_handler);
                    442: 
                    443:                chunkqueue_reset(con->write_queue);
                    444:        }
                    445: 
                    446: error:
                    447:        lua_close(L);
                    448: 
                    449:        stream_close(&rm.st);
                    450:        buffer_free(b);
                    451: 
                    452:        return ret /* cache-error */;
                    453: }
                    454: #else
                    455: int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
                    456:        UNUSED(srv);
                    457:        UNUSED(con);
                    458:        UNUSED(p);
                    459:        UNUSED(fn);
                    460:        /* error */
                    461:        return -1;
                    462: }
                    463: #endif

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