Annotation of embedaddon/lighttpd/src/mod_cml_lua.c, revision 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>