1/**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "mod_lua.h"
19#include <string.h>
20#include <stdlib.h>
21#include <ctype.h>
22#include <apr_thread_mutex.h>
23
24#include "lua_apr.h"
25#include "lua_config.h"
26#include "apr_optional.h"
27#include "mod_ssl.h"
28#include "mod_auth.h"
29
30#ifdef APR_HAS_THREADS
31#include "apr_thread_proc.h"
32#endif
33
34APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ap_lua, AP_LUA, int, lua_open,
35                                    (lua_State *L, apr_pool_t *p),
36                                    (L, p), OK, DECLINED)
37
38APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ap_lua, AP_LUA, int, lua_request,
39                                    (lua_State *L, request_rec *r),
40                                    (L, r), OK, DECLINED)
41static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *lua_ssl_val = NULL;
42static APR_OPTIONAL_FN_TYPE(ssl_is_https) *lua_ssl_is_https = NULL;
43
44module AP_MODULE_DECLARE_DATA lua_module;
45
46#define AP_LUA_HOOK_FIRST (APR_HOOK_FIRST - 1)
47#define AP_LUA_HOOK_LAST  (APR_HOOK_LAST  + 1)
48
49typedef struct {
50    const char *name;
51    const char *file_name;
52    const char *function_name;
53    ap_lua_vm_spec *spec;
54    apr_array_header_t *args;
55} lua_authz_provider_spec;
56
57apr_hash_t *lua_authz_providers;
58
59typedef struct
60{
61    apr_bucket_brigade *tmpBucket;
62    lua_State *L;
63    ap_lua_vm_spec *spec;
64    int broken;
65} lua_filter_ctx;
66
67apr_thread_mutex_t* lua_ivm_mutex = NULL;
68
69/**
70 * error reporting if lua has an error.
71 * Extracts the error from lua stack and prints
72 */
73static void report_lua_error(lua_State *L, request_rec *r)
74{
75    const char *lua_response;
76    r->status = HTTP_INTERNAL_SERVER_ERROR;
77    r->content_type = "text/html";
78    ap_rputs("<b>Error!</b>\n", r);
79    ap_rputs("<p>", r);
80    lua_response = lua_tostring(L, -1);
81    ap_rputs(lua_response, r);
82    ap_rputs("</p>\n", r);
83
84    ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, r->pool, APLOGNO(01471) "Lua error: %s",
85                  lua_response);
86}
87
88static void lua_open_callback(lua_State *L, apr_pool_t *p, void *ctx)
89{
90    ap_lua_init(L, p);
91    ap_lua_load_apache2_lmodule(L);
92    ap_lua_load_request_lmodule(L, p);
93    ap_lua_load_config_lmodule(L);
94}
95
96static int lua_open_hook(lua_State *L, apr_pool_t *p)
97{
98    lua_open_callback(L, p, NULL);
99    return OK;
100}
101
102static const char *scope_to_string(unsigned int scope)
103{
104    switch (scope) {
105    case AP_LUA_SCOPE_ONCE:
106    case AP_LUA_SCOPE_UNSET:
107        return "once";
108    case AP_LUA_SCOPE_REQUEST:
109        return "request";
110    case AP_LUA_SCOPE_CONN:
111        return "conn";
112#if APR_HAS_THREADS
113    case AP_LUA_SCOPE_THREAD:
114        return "thread";
115    case AP_LUA_SCOPE_SERVER:
116        return "server";
117#endif
118    default:
119        ap_assert(0);
120        return 0;
121    }
122}
123
124static void ap_lua_release_state(lua_State* L, ap_lua_vm_spec* spec, request_rec* r) {
125    char *hash;
126    apr_reslist_t* reslist = NULL;
127    if (spec->scope == AP_LUA_SCOPE_SERVER) {
128        ap_lua_server_spec* sspec = NULL;
129        lua_settop(L, 0);
130        lua_getfield(L, LUA_REGISTRYINDEX, "Apache2.Lua.server_spec");
131        sspec = (ap_lua_server_spec*) lua_touserdata(L, 1);
132        hash = apr_psprintf(r->pool, "reslist:%s", spec->file);
133        if (apr_pool_userdata_get((void **)&reslist, hash,
134                                r->server->process->pool) == APR_SUCCESS) {
135            AP_DEBUG_ASSERT(sspec != NULL);
136            if (reslist != NULL) {
137                apr_reslist_release(reslist, sspec);
138            }
139        }
140    }
141}
142
143static ap_lua_vm_spec *create_vm_spec(apr_pool_t **lifecycle_pool,
144                                      request_rec *r,
145                                      const ap_lua_dir_cfg *cfg,
146                                      const ap_lua_server_cfg *server_cfg,
147                                      const char *filename,
148                                      const char *bytecode,
149                                      apr_size_t bytecode_len,
150                                      const char *function,
151                                      const char *what)
152{
153    apr_pool_t *pool;
154    ap_lua_vm_spec *spec = apr_pcalloc(r->pool, sizeof(ap_lua_vm_spec));
155
156    spec->scope = cfg->vm_scope;
157    spec->pool = r->pool;
158    spec->package_paths = cfg->package_paths;
159    spec->package_cpaths = cfg->package_cpaths;
160    spec->cb = &lua_open_callback;
161    spec->cb_arg = NULL;
162    spec->bytecode = bytecode;
163    spec->bytecode_len = bytecode_len;
164    spec->codecache = (cfg->codecache == AP_LUA_CACHE_UNSET) ? AP_LUA_CACHE_STAT : cfg->codecache;
165    spec->vm_min = cfg->vm_min ? cfg->vm_min : 1;
166    spec->vm_max = cfg->vm_max ? cfg->vm_max : 1;
167
168    if (filename) {
169        char *file;
170        apr_filepath_merge(&file, server_cfg->root_path,
171                           filename, APR_FILEPATH_NOTRELATIVE, r->pool);
172        spec->file = file;
173    }
174    else {
175        spec->file = r->filename;
176    }
177    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(02313)
178                  "%s details: scope: %s, file: %s, func: %s",
179                  what, scope_to_string(spec->scope), spec->file,
180                  function ? function : "-");
181
182    switch (spec->scope) {
183    case AP_LUA_SCOPE_ONCE:
184    case AP_LUA_SCOPE_UNSET:
185        apr_pool_create(&pool, r->pool);
186        break;
187    case AP_LUA_SCOPE_REQUEST:
188        pool = r->pool;
189        break;
190    case AP_LUA_SCOPE_CONN:
191        pool = r->connection->pool;
192        break;
193#if APR_HAS_THREADS
194    case AP_LUA_SCOPE_THREAD:
195        pool = apr_thread_pool_get(r->connection->current_thread);
196        break;
197    case AP_LUA_SCOPE_SERVER:
198        pool = r->server->process->pool;
199        break;
200#endif
201    default:
202        ap_assert(0);
203    }
204
205    *lifecycle_pool = pool;
206    return spec;
207}
208
209static const char* ap_lua_interpolate_string(apr_pool_t* pool, const char* string, const char** values)
210{
211    char *stringBetween;
212    const char* ret;
213    int srclen,x,y;
214    srclen = strlen(string);
215    ret = "";
216    y = 0;
217    for (x=0; x < srclen; x++) {
218        if (string[x] == '$' && x != srclen-1 && string[x+1] >= '0' && string[x+1] <= '9') {
219            int v = *(string+x+1) - '0';
220            if (x-y > 0) {
221                stringBetween = apr_pstrndup(pool, string+y, x-y);
222            }
223            else {
224                stringBetween = "";
225            }
226            ret = apr_pstrcat(pool, ret, stringBetween, values[v], NULL);
227            y = ++x+1;
228        }
229    }
230
231    if (x-y > 0 && y > 0) {
232        stringBetween = apr_pstrndup(pool, string+y, x-y);
233        ret = apr_pstrcat(pool, ret, stringBetween, NULL);
234    }
235    /* If no replacement was made, just return the original string */
236    else if (y == 0) {
237        return string;
238    }
239    return ret;
240}
241
242
243
244/**
245 * "main"
246 */
247static int lua_handler(request_rec *r)
248{
249    int rc = OK;
250    if (strcmp(r->handler, "lua-script")) {
251        return DECLINED;
252    }
253    /* Decline the request if the script does not exist (or is a directory),
254     * rather than just returning internal server error */
255    if (
256            (r->finfo.filetype == APR_NOFILE)
257            || (r->finfo.filetype & APR_DIR)
258        ) {
259        return DECLINED;
260    }
261    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(01472)
262                  "handling [%s] in mod_lua", r->filename);
263
264    /* XXX: This seems wrong because it may generate wrong headers for HEAD requests */
265    if (!r->header_only) {
266        lua_State *L;
267        apr_pool_t *pool;
268        const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
269                                                         &lua_module);
270        ap_lua_vm_spec *spec = create_vm_spec(&pool, r, cfg, NULL, NULL, NULL,
271                                              0, "handle", "request handler");
272
273        L = ap_lua_get_lua_state(pool, spec, r);
274        if (!L) {
275            /* TODO annotate spec with failure reason */
276            r->status = HTTP_INTERNAL_SERVER_ERROR;
277            ap_rputs("Unable to compile VM, see logs", r);
278            ap_lua_release_state(L, spec, r);
279            return HTTP_INTERNAL_SERVER_ERROR;
280        }
281        ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, APLOGNO(01474) "got a vm!");
282        lua_getglobal(L, "handle");
283        if (!lua_isfunction(L, -1)) {
284            ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01475)
285                          "lua: Unable to find function %s in %s",
286                          "handle",
287                          spec->file);
288            ap_lua_release_state(L, spec, r);
289            return HTTP_INTERNAL_SERVER_ERROR;
290        }
291        ap_lua_run_lua_request(L, r);
292        if (lua_pcall(L, 1, 1, 0)) {
293            report_lua_error(L, r);
294        }
295        if (lua_isnumber(L, -1)) {
296            rc = lua_tointeger(L, -1);
297        }
298        ap_lua_release_state(L, spec, r);
299    }
300    return rc;
301}
302
303
304/* ------------------- Input/output content filters ------------------- */
305
306
307static apr_status_t lua_setup_filter_ctx(ap_filter_t* f, request_rec* r, lua_filter_ctx** c) {
308    apr_pool_t *pool;
309    ap_lua_vm_spec *spec;
310    int n, rc;
311    lua_State *L;
312    lua_filter_ctx *ctx;
313    ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config,
314                                                        &lua_module);
315    const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
316                                                    &lua_module);
317
318    ctx = apr_pcalloc(r->pool, sizeof(lua_filter_ctx));
319    ctx->broken = 0;
320    *c = ctx;
321    /* Find the filter that was called.
322     * XXX: If we were wired with mod_filter, the filter (mod_filters name)
323     *      and the provider (our underlying filters name) need to have matched.
324     */
325    for (n = 0; n < cfg->mapped_filters->nelts; n++) {
326        ap_lua_filter_handler_spec *hook_spec =
327            ((ap_lua_filter_handler_spec **) cfg->mapped_filters->elts)[n];
328
329        if (hook_spec == NULL) {
330            continue;
331        }
332        if (!strcasecmp(hook_spec->filter_name, f->frec->name)) {
333            spec = create_vm_spec(&pool, r, cfg, server_cfg,
334                                    hook_spec->file_name,
335                                    NULL,
336                                    0,
337                                    hook_spec->function_name,
338                                    "filter");
339            L = ap_lua_get_lua_state(pool, spec, r);
340            if (L) {
341                L = lua_newthread(L);
342            }
343
344            if (!L) {
345                ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02328)
346                                "lua: Failed to obtain lua interpreter for %s %s",
347                                hook_spec->function_name, hook_spec->file_name);
348                ap_lua_release_state(L, spec, r);
349                return APR_EGENERAL;
350            }
351            if (hook_spec->function_name != NULL) {
352                lua_getglobal(L, hook_spec->function_name);
353                if (!lua_isfunction(L, -1)) {
354                    ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02329)
355                                    "lua: Unable to find function %s in %s",
356                                    hook_spec->function_name,
357                                    hook_spec->file_name);
358                    ap_lua_release_state(L, spec, r);
359                    return APR_EGENERAL;
360                }
361
362                ap_lua_run_lua_request(L, r);
363            }
364            else {
365                int t;
366                ap_lua_run_lua_request(L, r);
367
368                t = lua_gettop(L);
369                lua_setglobal(L, "r");
370                lua_settop(L, t);
371            }
372            ctx->L = L;
373            ctx->spec = spec;
374
375            /* If a Lua filter is interested in filtering a request, it must first do a yield,
376             * otherwise we'll assume that it's not interested and pretend we didn't find it.
377             */
378            rc = lua_resume(L, 1);
379            if (rc == LUA_YIELD) {
380                if (f->frec->providers == NULL) {
381                    /* Not wired by mod_filter */
382                    apr_table_unset(r->headers_out, "Content-Length");
383                    apr_table_unset(r->headers_out, "Content-MD5");
384                    apr_table_unset(r->headers_out, "ETAG");
385                }
386                return OK;
387            }
388            else {
389                ap_lua_release_state(L, spec, r);
390                return APR_ENOENT;
391            }
392        }
393    }
394    return APR_ENOENT;
395}
396
397static apr_status_t lua_output_filter_handle(ap_filter_t *f, apr_bucket_brigade *pbbIn) {
398    request_rec *r = f->r;
399    int rc;
400    lua_State *L;
401    lua_filter_ctx* ctx;
402    conn_rec *c = r->connection;
403    apr_bucket *pbktIn;
404    apr_status_t rv;
405
406    /* Set up the initial filter context and acquire the function.
407     * The corresponding Lua function should yield here.
408     */
409    if (!f->ctx) {
410        rc = lua_setup_filter_ctx(f,r,&ctx);
411        if (rc == APR_EGENERAL) {
412            return HTTP_INTERNAL_SERVER_ERROR;
413        }
414        if (rc == APR_ENOENT) {
415            /* No filter entry found (or the script declined to filter), just pass on the buckets */
416            ap_remove_output_filter(f);
417            return ap_pass_brigade(f->next,pbbIn);
418        }
419        else {
420            /* We've got a willing lua filter, setup and check for a prefix */
421            size_t olen;
422            apr_bucket *pbktOut;
423            const char* output = lua_tolstring(ctx->L, 1, &olen);
424
425            f->ctx = ctx;
426            ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc);
427
428            if (olen > 0) {
429                pbktOut = apr_bucket_heap_create(output, olen, NULL, c->bucket_alloc);
430                APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
431                rv = ap_pass_brigade(f->next, ctx->tmpBucket);
432                apr_brigade_cleanup(ctx->tmpBucket);
433                if (rv != APR_SUCCESS) {
434                    return rv;
435                }
436            }
437        }
438    }
439    ctx = (lua_filter_ctx*) f->ctx;
440    L = ctx->L;
441    /* While the Lua function is still yielding, pass in buckets to the coroutine */
442    if (!ctx->broken) {
443        for (pbktIn = APR_BRIGADE_FIRST(pbbIn);
444            pbktIn != APR_BRIGADE_SENTINEL(pbbIn);
445            pbktIn = APR_BUCKET_NEXT(pbktIn))
446            {
447            const char *data;
448            apr_size_t len;
449            apr_bucket *pbktOut;
450
451            /* read the bucket */
452            apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ);
453
454            /* Push the bucket onto the Lua stack as a global var */
455            lua_pushlstring(L, data, len);
456            lua_setglobal(L, "bucket");
457
458            /* If Lua yielded, it means we have something to pass on */
459            if (lua_resume(L, 0) == LUA_YIELD) {
460                size_t olen;
461                const char* output = lua_tolstring(L, 1, &olen);
462                if (olen > 0) {
463                    pbktOut = apr_bucket_heap_create(output, olen, NULL,
464                                            c->bucket_alloc);
465                    APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
466                    rv = ap_pass_brigade(f->next, ctx->tmpBucket);
467                    apr_brigade_cleanup(ctx->tmpBucket);
468                    if (rv != APR_SUCCESS) {
469                        return rv;
470                    }
471                }
472            }
473            else {
474                ctx->broken = 1;
475                ap_lua_release_state(L, ctx->spec, r);
476                ap_remove_output_filter(f);
477                apr_brigade_cleanup(pbbIn);
478                apr_brigade_cleanup(ctx->tmpBucket);
479                return HTTP_INTERNAL_SERVER_ERROR;
480            }
481        }
482        /* If we've safely reached the end, do a final call to Lua to allow for any
483        finishing moves by the script, such as appending a tail. */
484        if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(pbbIn))) {
485            apr_bucket *pbktEOS;
486            lua_pushnil(L);
487            lua_setglobal(L, "bucket");
488            if (lua_resume(L, 0) == LUA_YIELD) {
489                apr_bucket *pbktOut;
490                size_t olen;
491                const char* output = lua_tolstring(L, 1, &olen);
492                if (olen > 0) {
493                    pbktOut = apr_bucket_heap_create(output, olen, NULL,
494                            c->bucket_alloc);
495                    APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
496                }
497            }
498            pbktEOS = apr_bucket_eos_create(c->bucket_alloc);
499            APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktEOS);
500            ap_lua_release_state(L, ctx->spec, r);
501            rv = ap_pass_brigade(f->next, ctx->tmpBucket);
502            apr_brigade_cleanup(ctx->tmpBucket);
503            if (rv != APR_SUCCESS) {
504                return rv;
505            }
506        }
507    }
508    /* Clean up */
509    apr_brigade_cleanup(pbbIn);
510    return APR_SUCCESS;
511}
512
513
514
515static apr_status_t lua_input_filter_handle(ap_filter_t *f,
516                                       apr_bucket_brigade *pbbOut,
517                                       ap_input_mode_t eMode,
518                                       apr_read_type_e eBlock,
519                                       apr_off_t nBytes)
520{
521    request_rec *r = f->r;
522    int rc, lastCall = 0;
523    lua_State *L;
524    lua_filter_ctx* ctx;
525    conn_rec *c = r->connection;
526    apr_status_t ret;
527
528    /* Set up the initial filter context and acquire the function.
529     * The corresponding Lua function should yield here.
530     */
531    if (!f->ctx) {
532        rc = lua_setup_filter_ctx(f,r,&ctx);
533        f->ctx = ctx;
534        if (rc == APR_EGENERAL) {
535            ctx->broken = 1;
536            ap_remove_input_filter(f);
537            return HTTP_INTERNAL_SERVER_ERROR;
538        }
539        if (rc == APR_ENOENT ) {
540            ap_remove_input_filter(f);
541            ctx->broken = 1;
542        }
543        if (rc == APR_SUCCESS) {
544            ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc);
545        }
546    }
547    ctx = (lua_filter_ctx*) f->ctx;
548    L = ctx->L;
549    /* If the Lua script broke or denied serving the request, just pass the buckets through */
550    if (ctx->broken) {
551        return ap_get_brigade(f->next, pbbOut, eMode, eBlock, nBytes);
552    }
553
554    if (APR_BRIGADE_EMPTY(ctx->tmpBucket)) {
555        ret = ap_get_brigade(f->next, ctx->tmpBucket, eMode, eBlock, nBytes);
556        if (eMode == AP_MODE_EATCRLF || ret != APR_SUCCESS)
557            return ret;
558    }
559
560    /* While the Lua function is still yielding, pass buckets to the coroutine */
561    if (!ctx->broken) {
562        lastCall = 0;
563        while(!APR_BRIGADE_EMPTY(ctx->tmpBucket)) {
564            apr_bucket *pbktIn = APR_BRIGADE_FIRST(ctx->tmpBucket);
565            apr_bucket *pbktOut;
566            const char *data;
567            apr_size_t len;
568
569            if (APR_BUCKET_IS_EOS(pbktIn)) {
570                APR_BUCKET_REMOVE(pbktIn);
571                break;
572            }
573
574            /* read the bucket */
575            ret = apr_bucket_read(pbktIn, &data, &len, eBlock);
576            if (ret != APR_SUCCESS)
577                return ret;
578
579            /* Push the bucket onto the Lua stack as a global var */
580            lastCall++;
581            lua_pushlstring(L, data, len);
582            lua_setglobal(L, "bucket");
583
584            /* If Lua yielded, it means we have something to pass on */
585            if (lua_resume(L, 0) == LUA_YIELD) {
586                size_t olen;
587                const char* output = lua_tolstring(L, 1, &olen);
588                pbktOut = apr_bucket_heap_create(output, olen, 0, c->bucket_alloc);
589                APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
590                apr_bucket_delete(pbktIn);
591                return APR_SUCCESS;
592            }
593            else {
594                ctx->broken = 1;
595                ap_lua_release_state(L, ctx->spec, r);
596                ap_remove_input_filter(f);
597                apr_bucket_delete(pbktIn);
598                return HTTP_INTERNAL_SERVER_ERROR;
599            }
600        }
601        /* If we've safely reached the end, do a final call to Lua to allow for any
602        finishing moves by the script, such as appending a tail. */
603        if (lastCall == 0) {
604            apr_bucket *pbktEOS = apr_bucket_eos_create(c->bucket_alloc);
605            lua_pushnil(L);
606            lua_setglobal(L, "bucket");
607            if (lua_resume(L, 0) == LUA_YIELD) {
608                apr_bucket *pbktOut;
609                size_t olen;
610                const char* output = lua_tolstring(L, 1, &olen);
611                pbktOut = apr_bucket_heap_create(output, olen, 0, c->bucket_alloc);
612                APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
613            }
614            APR_BRIGADE_INSERT_TAIL(pbbOut,pbktEOS);
615            ap_lua_release_state(L, ctx->spec, r);
616        }
617    }
618    return APR_SUCCESS;
619}
620
621
622/* ---------------- Configury stuff --------------- */
623
624/** harnesses for magic hooks **/
625
626static int lua_request_rec_hook_harness(request_rec *r, const char *name, int apr_hook_when)
627{
628    int rc;
629    apr_pool_t *pool;
630    lua_State *L;
631    ap_lua_vm_spec *spec;
632    ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config,
633                                                         &lua_module);
634    const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
635                                                     &lua_module);
636    const char *key = apr_psprintf(r->pool, "%s_%d", name, apr_hook_when);
637    apr_array_header_t *hook_specs = apr_hash_get(cfg->hooks, key,
638                                                  APR_HASH_KEY_STRING);
639    if (hook_specs) {
640        int i;
641        for (i = 0; i < hook_specs->nelts; i++) {
642            ap_lua_mapped_handler_spec *hook_spec =
643                ((ap_lua_mapped_handler_spec **) hook_specs->elts)[i];
644
645            if (hook_spec == NULL) {
646                continue;
647            }
648            spec = create_vm_spec(&pool, r, cfg, server_cfg,
649                                  hook_spec->file_name,
650                                  hook_spec->bytecode,
651                                  hook_spec->bytecode_len,
652                                  hook_spec->function_name,
653                                  "request hook");
654
655            L = ap_lua_get_lua_state(pool, spec, r);
656
657            if (!L) {
658                ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01477)
659                              "lua: Failed to obtain lua interpreter for %s %s",
660                              hook_spec->function_name, hook_spec->file_name);
661                return HTTP_INTERNAL_SERVER_ERROR;
662            }
663
664            if (hook_spec->function_name != NULL) {
665                lua_getglobal(L, hook_spec->function_name);
666                if (!lua_isfunction(L, -1)) {
667                    ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01478)
668                                  "lua: Unable to find function %s in %s",
669                                  hook_spec->function_name,
670                                  hook_spec->file_name);
671                    ap_lua_release_state(L, spec, r);
672                    return HTTP_INTERNAL_SERVER_ERROR;
673                }
674
675                ap_lua_run_lua_request(L, r);
676            }
677            else {
678                int t;
679                ap_lua_run_lua_request(L, r);
680
681                t = lua_gettop(L);
682                lua_setglobal(L, "r");
683                lua_settop(L, t);
684            }
685
686            if (lua_pcall(L, 1, 1, 0)) {
687                report_lua_error(L, r);
688                ap_lua_release_state(L, spec, r);
689                return HTTP_INTERNAL_SERVER_ERROR;
690            }
691            rc = DECLINED;
692            if (lua_isnumber(L, -1)) {
693                rc = lua_tointeger(L, -1);
694                ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "Lua hook %s:%s for phase %s returned %d",
695                              hook_spec->file_name, hook_spec->function_name, name, rc);
696            }
697            else {
698                ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, "Lua hook %s:%s for phase %s did not return a numeric value",
699                              hook_spec->file_name, hook_spec->function_name, name);
700                return HTTP_INTERNAL_SERVER_ERROR;
701            }
702            if (rc != DECLINED) {
703                ap_lua_release_state(L, spec, r);
704                return rc;
705            }
706            ap_lua_release_state(L, spec, r);
707        }
708    }
709    return DECLINED;
710}
711
712static int lua_map_handler_fixups(request_rec *r)
713{
714    /* If there is no handler set yet, this might be a LuaMapHandler request */
715    if (r->handler == NULL) {
716        int n = 0;
717        ap_regmatch_t match[10];
718        const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
719                                                     &lua_module);
720        for (n = 0; n < cfg->mapped_handlers->nelts; n++) {
721            ap_lua_mapped_handler_spec *hook_spec =
722            ((ap_lua_mapped_handler_spec **) cfg->mapped_handlers->elts)[n];
723
724            if (hook_spec == NULL) {
725                continue;
726            }
727            if (!ap_regexec(hook_spec->uri_pattern, r->uri, 10, match, 0)) {
728                r->handler = apr_pstrdup(r->pool, "lua-map-handler");
729                return OK;
730            }
731        }
732    }
733    return DECLINED;
734}
735
736static int lua_map_handler(request_rec *r)
737{
738    int rc, n = 0;
739    apr_pool_t *pool;
740    lua_State *L;
741    const char *filename, *function_name;
742    const char *values[10];
743    ap_lua_vm_spec *spec;
744    ap_regmatch_t match[10];
745    ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config,
746                                                         &lua_module);
747    const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
748                                                     &lua_module);
749    for (n = 0; n < cfg->mapped_handlers->nelts; n++) {
750        ap_lua_mapped_handler_spec *hook_spec =
751            ((ap_lua_mapped_handler_spec **) cfg->mapped_handlers->elts)[n];
752
753        if (hook_spec == NULL) {
754            continue;
755        }
756        if (!ap_regexec(hook_spec->uri_pattern, r->uri, 10, match, 0)) {
757            int i;
758            for (i=0 ; i < 10; i++) {
759                if (match[i].rm_eo >= 0) {
760                    values[i] = apr_pstrndup(r->pool, r->uri+match[i].rm_so, match[i].rm_eo - match[i].rm_so);
761                }
762                else values[i] = "";
763            }
764            filename = ap_lua_interpolate_string(r->pool, hook_spec->file_name, values);
765            function_name = ap_lua_interpolate_string(r->pool, hook_spec->function_name, values);
766            spec = create_vm_spec(&pool, r, cfg, server_cfg,
767                                    filename,
768                                    hook_spec->bytecode,
769                                    hook_spec->bytecode_len,
770                                    function_name,
771                                    "mapped handler");
772            L = ap_lua_get_lua_state(pool, spec, r);
773
774            if (!L) {
775                ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02330)
776                                "lua: Failed to obtain lua interpreter for %s %s",
777                                function_name, filename);
778                ap_lua_release_state(L, spec, r);
779                return HTTP_INTERNAL_SERVER_ERROR;
780            }
781
782            if (function_name != NULL) {
783                lua_getglobal(L, function_name);
784                if (!lua_isfunction(L, -1)) {
785                    ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02331)
786                                    "lua: Unable to find function %s in %s",
787                                    function_name,
788                                    filename);
789                    ap_lua_release_state(L, spec, r);
790                    return HTTP_INTERNAL_SERVER_ERROR;
791                }
792
793                ap_lua_run_lua_request(L, r);
794            }
795            else {
796                int t;
797                ap_lua_run_lua_request(L, r);
798
799                t = lua_gettop(L);
800                lua_setglobal(L, "r");
801                lua_settop(L, t);
802            }
803
804            if (lua_pcall(L, 1, 1, 0)) {
805                report_lua_error(L, r);
806                ap_lua_release_state(L, spec, r);
807                return HTTP_INTERNAL_SERVER_ERROR;
808            }
809            rc = DECLINED;
810            if (lua_isnumber(L, -1)) {
811                rc = lua_tointeger(L, -1);
812            }
813            else {
814                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02483)
815                              "lua: Lua handler %s in %s did not return a value, assuming apache2.OK",
816                              function_name,
817                              filename);
818                rc = OK;
819            }
820            ap_lua_release_state(L, spec, r);
821            if (rc != DECLINED) {
822                return rc;
823            }
824        }
825    }
826    return DECLINED;
827}
828
829
830static apr_size_t config_getstr(ap_configfile_t *cfg, char *buf,
831                                size_t bufsiz)
832{
833    apr_size_t i = 0;
834
835    if (cfg->getstr) {
836        apr_status_t rc = (cfg->getstr) (buf, bufsiz, cfg->param);
837        if (rc == APR_SUCCESS) {
838            i = strlen(buf);
839            if (i && buf[i - 1] == '\n')
840                ++cfg->line_number;
841        }
842        else {
843            buf[0] = '\0';
844            i = 0;
845        }
846    }
847    else {
848        while (i < bufsiz) {
849            char ch;
850            apr_status_t rc = (cfg->getch) (&ch, cfg->param);
851            if (rc != APR_SUCCESS)
852                break;
853            buf[i++] = ch;
854            if (ch == '\n') {
855                ++cfg->line_number;
856                break;
857            }
858        }
859    }
860    return i;
861}
862
863typedef struct cr_ctx
864{
865    cmd_parms *cmd;
866    ap_configfile_t *cfp;
867    size_t startline;
868    const char *endstr;
869    char buf[HUGE_STRING_LEN];
870} cr_ctx;
871
872
873/* Okay, this deserves a little explaination -- in order for the errors that lua
874 * generates to be 'accuarate', including line numbers, we basically inject
875 * N line number new lines into the 'top' of the chunk reader.....
876 *
877 * be happy. this is cool.
878 *
879 */
880static const char *lf =
881    "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
882#define N_LF 32
883
884static const char *direct_chunkreader(lua_State *lvm, void *udata,
885                                      size_t *plen)
886{
887    const char *p;
888    struct cr_ctx *ctx = udata;
889
890    if (ctx->startline) {
891        *plen = ctx->startline > N_LF ? N_LF : ctx->startline;
892        ctx->startline -= *plen;
893        return lf;
894    }
895    *plen = config_getstr(ctx->cfp, ctx->buf, HUGE_STRING_LEN);
896
897    for (p = ctx->buf; isspace(*p); ++p);
898    if (p[0] == '<' && p[1] == '/') {
899        apr_size_t i = 0;
900        while (i < strlen(ctx->endstr)) {
901            if (tolower(p[i + 2]) != ctx->endstr[i])
902                return ctx->buf;
903            ++i;
904        }
905        *plen = 0;
906        return NULL;
907    }
908    /*fprintf(stderr, "buf read: %s\n", ctx->buf); */
909    return ctx->buf;
910}
911
912static int ldump_writer(lua_State *L, const void *b, size_t size, void *B)
913{
914    (void) L;
915    luaL_addlstring((luaL_Buffer *) B, (const char *) b, size);
916    return 0;
917}
918
919typedef struct hack_section_baton
920{
921    const char *name;
922    ap_lua_mapped_handler_spec *spec;
923    int apr_hook_when;
924} hack_section_baton;
925
926/* You can be unhappy now.
927 *
928 * This is uncool.
929 *
930 * When you create a <Section handler in httpd, the only 'easy' way to create
931 * a directory context is to parse the section, and convert it into a 'normal'
932 * Configureation option, and then collapse the entire section, in memory,
933 * back into the parent section -- from which you can then get the new directive
934 * invoked.... anyways. evil. Rici taught me how to do this hack :-)
935 */
936static const char *hack_section_handler(cmd_parms *cmd, void *_cfg,
937                                        const char *arg)
938{
939    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
940    ap_directive_t *directive = cmd->directive;
941    hack_section_baton *baton = directive->data;
942    const char *key = apr_psprintf(cmd->pool, "%s_%d", baton->name, baton->apr_hook_when);
943
944    apr_array_header_t *hook_specs = apr_hash_get(cfg->hooks, key,
945                                                  APR_HASH_KEY_STRING);
946    if (!hook_specs) {
947        hook_specs = apr_array_make(cmd->pool, 2,
948                                    sizeof(ap_lua_mapped_handler_spec *));
949        apr_hash_set(cfg->hooks, key,
950                     APR_HASH_KEY_STRING, hook_specs);
951    }
952
953    baton->spec->scope = cfg->vm_scope;
954
955    *(ap_lua_mapped_handler_spec **) apr_array_push(hook_specs) = baton->spec;
956
957    return NULL;
958}
959
960static const char *register_named_block_function_hook(const char *name,
961                                                      cmd_parms *cmd,
962                                                      void *mconfig,
963                                                      const char *line)
964{
965    const char *function = NULL;
966    ap_lua_mapped_handler_spec *spec;
967    int when = APR_HOOK_MIDDLE;
968    const char *endp = ap_strrchr_c(line, '>');
969
970    if (endp == NULL) {
971        return apr_pstrcat(cmd->pool, cmd->cmd->name,
972                           "> directive missing closing '>'", NULL);
973    }
974
975    line = apr_pstrndup(cmd->temp_pool, line, endp - line);
976
977    if (line[0]) {
978        const char *word;
979        word = ap_getword_conf(cmd->temp_pool, &line);
980        if (*word) {
981            function = apr_pstrdup(cmd->pool, word);
982        }
983        word = ap_getword_conf(cmd->temp_pool, &line);
984        if (*word) {
985            if (!strcasecmp("early", word)) {
986                when = AP_LUA_HOOK_FIRST;
987            }
988            else if (!strcasecmp("late", word)) {
989                when = AP_LUA_HOOK_LAST;
990            }
991            else {
992                return apr_pstrcat(cmd->pool, cmd->cmd->name,
993                                   "> 2nd argument must be 'early' or 'late'", NULL);
994            }
995        }
996    }
997
998    spec = apr_pcalloc(cmd->pool, sizeof(ap_lua_mapped_handler_spec));
999
1000    {
1001        cr_ctx ctx;
1002        lua_State *lvm;
1003        char *tmp;
1004        int rv;
1005        ap_directive_t **current;
1006        hack_section_baton *baton;
1007
1008        spec->file_name = apr_psprintf(cmd->pool, "%s:%u",
1009                                       cmd->config_file->name,
1010                                       cmd->config_file->line_number);
1011        if (function) {
1012            spec->function_name = (char *) function;
1013        }
1014        else {
1015            function = NULL;
1016        }
1017
1018        ctx.cmd = cmd;
1019        tmp = apr_pstrdup(cmd->pool, cmd->err_directive->directive + 1);
1020        ap_str_tolower(tmp);
1021        ctx.endstr = tmp;
1022        ctx.cfp = cmd->config_file;
1023        ctx.startline = cmd->config_file->line_number;
1024
1025        /* This lua State is used only to compile the input strings -> bytecode, so we don't need anything extra. */
1026        lvm = luaL_newstate();
1027
1028        lua_settop(lvm, 0);
1029
1030        rv = lua_load(lvm, direct_chunkreader, &ctx, spec->file_name);
1031
1032        if (rv != 0) {
1033            const char *errstr = apr_pstrcat(cmd->pool, "Lua Error:",
1034                                             lua_tostring(lvm, -1), NULL);
1035            lua_close(lvm);
1036            return errstr;
1037        }
1038        else {
1039            luaL_Buffer b;
1040            luaL_buffinit(lvm, &b);
1041            lua_dump(lvm, ldump_writer, &b);
1042            luaL_pushresult(&b);
1043            spec->bytecode_len = lua_strlen(lvm, -1);
1044            spec->bytecode = apr_pstrmemdup(cmd->pool, lua_tostring(lvm, -1),
1045                                            spec->bytecode_len);
1046            lua_close(lvm);
1047        }
1048
1049        current = mconfig;
1050
1051        /* Here, we have to replace our current config node for the next pass */
1052        if (!*current) {
1053            *current = apr_pcalloc(cmd->pool, sizeof(**current));
1054        }
1055
1056        baton = apr_pcalloc(cmd->pool, sizeof(hack_section_baton));
1057        baton->name = name;
1058        baton->spec = spec;
1059        baton->apr_hook_when = when;
1060
1061        (*current)->filename = cmd->config_file->name;
1062        (*current)->line_num = cmd->config_file->line_number;
1063        (*current)->directive = apr_pstrdup(cmd->pool, "Lua_____ByteCodeHack");
1064        (*current)->args = NULL;
1065        (*current)->data = baton;
1066    }
1067
1068    return NULL;
1069}
1070
1071static const char *register_named_file_function_hook(const char *name,
1072                                                     cmd_parms *cmd,
1073                                                     void *_cfg,
1074                                                     const char *file,
1075                                                     const char *function,
1076                                                     int apr_hook_when)
1077{
1078    ap_lua_mapped_handler_spec *spec;
1079    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
1080    const char *key = apr_psprintf(cmd->pool, "%s_%d", name, apr_hook_when);
1081    apr_array_header_t *hook_specs = apr_hash_get(cfg->hooks, key,
1082                                                  APR_HASH_KEY_STRING);
1083
1084    if (!hook_specs) {
1085        hook_specs = apr_array_make(cmd->pool, 2,
1086                                    sizeof(ap_lua_mapped_handler_spec *));
1087        apr_hash_set(cfg->hooks, key, APR_HASH_KEY_STRING, hook_specs);
1088    }
1089
1090    spec = apr_pcalloc(cmd->pool, sizeof(ap_lua_mapped_handler_spec));
1091    spec->file_name = apr_pstrdup(cmd->pool, file);
1092    spec->function_name = apr_pstrdup(cmd->pool, function);
1093    spec->scope = cfg->vm_scope;
1094
1095    *(ap_lua_mapped_handler_spec **) apr_array_push(hook_specs) = spec;
1096    return NULL;
1097}
1098static const char *register_mapped_file_function_hook(const char *pattern,
1099                                                     cmd_parms *cmd,
1100                                                     void *_cfg,
1101                                                     const char *file,
1102                                                     const char *function)
1103{
1104    ap_lua_mapped_handler_spec *spec;
1105    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
1106    ap_regex_t *regex = apr_pcalloc(cmd->pool, sizeof(ap_regex_t));
1107    if (ap_regcomp(regex, pattern,0)) {
1108        return "Invalid regex pattern!";
1109    }
1110
1111    spec = apr_pcalloc(cmd->pool, sizeof(ap_lua_mapped_handler_spec));
1112    spec->file_name = apr_pstrdup(cmd->pool, file);
1113    spec->function_name = apr_pstrdup(cmd->pool, function);
1114    spec->scope = cfg->vm_scope;
1115    spec->uri_pattern = regex;
1116
1117    *(ap_lua_mapped_handler_spec **) apr_array_push(cfg->mapped_handlers) = spec;
1118    return NULL;
1119}
1120static const char *register_filter_function_hook(const char *filter,
1121                                                     cmd_parms *cmd,
1122                                                     void *_cfg,
1123                                                     const char *file,
1124                                                     const char *function,
1125                                                     int direction)
1126{
1127    ap_lua_filter_handler_spec *spec;
1128    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
1129
1130    spec = apr_pcalloc(cmd->pool, sizeof(ap_lua_filter_handler_spec));
1131    spec->file_name = apr_pstrdup(cmd->pool, file);
1132    spec->function_name = apr_pstrdup(cmd->pool, function);
1133    spec->filter_name = filter;
1134
1135    *(ap_lua_filter_handler_spec **) apr_array_push(cfg->mapped_filters) = spec;
1136    /* TODO: Make it work on other types than just AP_FTYPE_RESOURCE? */
1137    if (direction == AP_LUA_FILTER_OUTPUT) {
1138        spec->direction = AP_LUA_FILTER_OUTPUT;
1139        ap_register_output_filter_protocol(filter, lua_output_filter_handle, NULL, AP_FTYPE_RESOURCE,
1140                                            AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH);
1141    }
1142    else {
1143        spec->direction = AP_LUA_FILTER_INPUT;
1144        ap_register_input_filter(filter, lua_input_filter_handle, NULL, AP_FTYPE_RESOURCE);
1145    }
1146    return NULL;
1147}
1148static int lua_check_user_id_harness_first(request_rec *r)
1149{
1150    return lua_request_rec_hook_harness(r, "check_user_id", AP_LUA_HOOK_FIRST);
1151}
1152static int lua_check_user_id_harness(request_rec *r)
1153{
1154    return lua_request_rec_hook_harness(r, "check_user_id", APR_HOOK_MIDDLE);
1155}
1156static int lua_check_user_id_harness_last(request_rec *r)
1157{
1158    return lua_request_rec_hook_harness(r, "check_user_id", AP_LUA_HOOK_LAST);
1159}
1160
1161static int lua_translate_name_harness_first(request_rec *r)
1162{
1163    return lua_request_rec_hook_harness(r, "translate_name", AP_LUA_HOOK_FIRST);
1164}
1165static int lua_translate_name_harness(request_rec *r)
1166{
1167    return lua_request_rec_hook_harness(r, "translate_name", APR_HOOK_MIDDLE);
1168}
1169static int lua_translate_name_harness_last(request_rec *r)
1170{
1171    return lua_request_rec_hook_harness(r, "translate_name", AP_LUA_HOOK_LAST);
1172}
1173
1174static int lua_fixup_harness(request_rec *r)
1175{
1176    return lua_request_rec_hook_harness(r, "fixups", APR_HOOK_MIDDLE);
1177}
1178
1179static int lua_map_to_storage_harness(request_rec *r)
1180{
1181    return lua_request_rec_hook_harness(r, "map_to_storage", APR_HOOK_MIDDLE);
1182}
1183
1184static int lua_type_checker_harness(request_rec *r)
1185{
1186    return lua_request_rec_hook_harness(r, "type_checker", APR_HOOK_MIDDLE);
1187}
1188
1189static int lua_access_checker_harness_first(request_rec *r)
1190{
1191    return lua_request_rec_hook_harness(r, "access_checker", AP_LUA_HOOK_FIRST);
1192}
1193static int lua_access_checker_harness(request_rec *r)
1194{
1195    return lua_request_rec_hook_harness(r, "access_checker", APR_HOOK_MIDDLE);
1196}
1197static int lua_access_checker_harness_last(request_rec *r)
1198{
1199    return lua_request_rec_hook_harness(r, "access_checker", AP_LUA_HOOK_LAST);
1200}
1201
1202static int lua_auth_checker_harness_first(request_rec *r)
1203{
1204    return lua_request_rec_hook_harness(r, "auth_checker", AP_LUA_HOOK_FIRST);
1205}
1206static int lua_auth_checker_harness(request_rec *r)
1207{
1208    return lua_request_rec_hook_harness(r, "auth_checker", APR_HOOK_MIDDLE);
1209}
1210static int lua_auth_checker_harness_last(request_rec *r)
1211{
1212    return lua_request_rec_hook_harness(r, "auth_checker", AP_LUA_HOOK_LAST);
1213}
1214static void lua_insert_filter_harness(request_rec *r)
1215{
1216    /* ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "LuaHookInsertFilter not yet implemented"); */
1217}
1218
1219static int lua_log_transaction_harness(request_rec *r)
1220{
1221    return lua_request_rec_hook_harness(r, "log_transaction", APR_HOOK_FIRST);
1222}
1223
1224static int lua_quick_harness(request_rec *r, int lookup)
1225{
1226    if (lookup) {
1227        return DECLINED;
1228    }
1229    return lua_request_rec_hook_harness(r, "quick", APR_HOOK_MIDDLE);
1230}
1231
1232static const char *register_translate_name_hook(cmd_parms *cmd, void *_cfg,
1233                                                const char *file,
1234                                                const char *function,
1235                                                const char *when)
1236{
1237    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|
1238                                           NOT_IN_HTACCESS);
1239    int apr_hook_when = APR_HOOK_MIDDLE;
1240    if (err) {
1241        return err;
1242    }
1243
1244    if (when) {
1245        if (!strcasecmp(when, "early")) {
1246            apr_hook_when = AP_LUA_HOOK_FIRST;
1247        }
1248        else if (!strcasecmp(when, "late")) {
1249            apr_hook_when = AP_LUA_HOOK_LAST;
1250        }
1251        else {
1252            return "Third argument must be 'early' or 'late'";
1253        }
1254    }
1255
1256    return register_named_file_function_hook("translate_name", cmd, _cfg,
1257                                             file, function, apr_hook_when);
1258}
1259
1260static const char *register_translate_name_block(cmd_parms *cmd, void *_cfg,
1261                                                 const char *line)
1262{
1263    return register_named_block_function_hook("translate_name", cmd, _cfg,
1264                                              line);
1265}
1266
1267
1268static const char *register_fixups_hook(cmd_parms *cmd, void *_cfg,
1269                                        const char *file,
1270                                        const char *function)
1271{
1272    return register_named_file_function_hook("fixups", cmd, _cfg, file,
1273                                             function, APR_HOOK_MIDDLE);
1274}
1275static const char *register_fixups_block(cmd_parms *cmd, void *_cfg,
1276                                         const char *line)
1277{
1278    return register_named_block_function_hook("fixups", cmd, _cfg, line);
1279}
1280
1281static const char *register_map_to_storage_hook(cmd_parms *cmd, void *_cfg,
1282                                                const char *file,
1283                                                const char *function)
1284{
1285    return register_named_file_function_hook("map_to_storage", cmd, _cfg,
1286                                             file, function, APR_HOOK_MIDDLE);
1287}
1288
1289static const char *register_log_transaction_hook(cmd_parms *cmd, void *_cfg,
1290                                                const char *file,
1291                                                const char *function)
1292{
1293    return register_named_file_function_hook("log_transaction", cmd, _cfg,
1294                                             file, function, APR_HOOK_FIRST);
1295}
1296
1297static const char *register_map_to_storage_block(cmd_parms *cmd, void *_cfg,
1298                                                 const char *line)
1299{
1300    return register_named_block_function_hook("map_to_storage", cmd, _cfg,
1301                                              line);
1302}
1303
1304
1305static const char *register_check_user_id_hook(cmd_parms *cmd, void *_cfg,
1306                                               const char *file,
1307                                               const char *function,
1308                                               const char *when)
1309{
1310    int apr_hook_when = APR_HOOK_MIDDLE;
1311
1312    if (when) {
1313        if (!strcasecmp(when, "early")) {
1314            apr_hook_when = AP_LUA_HOOK_FIRST;
1315        }
1316        else if (!strcasecmp(when, "late")) {
1317            apr_hook_when = AP_LUA_HOOK_LAST;
1318        }
1319        else {
1320            return "Third argument must be 'early' or 'late'";
1321        }
1322    }
1323
1324    return register_named_file_function_hook("check_user_id", cmd, _cfg, file,
1325                                             function, apr_hook_when);
1326}
1327static const char *register_check_user_id_block(cmd_parms *cmd, void *_cfg,
1328                                                const char *line)
1329{
1330    return register_named_block_function_hook("check_user_id", cmd, _cfg,
1331                                              line);
1332}
1333
1334static const char *register_type_checker_hook(cmd_parms *cmd, void *_cfg,
1335                                              const char *file,
1336                                              const char *function)
1337{
1338    return register_named_file_function_hook("type_checker", cmd, _cfg, file,
1339                                             function, APR_HOOK_MIDDLE);
1340}
1341static const char *register_type_checker_block(cmd_parms *cmd, void *_cfg,
1342                                               const char *line)
1343{
1344    return register_named_block_function_hook("type_checker", cmd, _cfg,
1345                                              line);
1346}
1347
1348static const char *register_access_checker_hook(cmd_parms *cmd, void *_cfg,
1349                                                const char *file,
1350                                                const char *function,
1351                                                const char *when)
1352{
1353    int apr_hook_when = APR_HOOK_MIDDLE;
1354
1355    if (when) {
1356        if (!strcasecmp(when, "early")) {
1357            apr_hook_when = AP_LUA_HOOK_FIRST;
1358        }
1359        else if (!strcasecmp(when, "late")) {
1360            apr_hook_when = AP_LUA_HOOK_LAST;
1361        }
1362        else {
1363            return "Third argument must be 'early' or 'late'";
1364        }
1365    }
1366
1367    return register_named_file_function_hook("access_checker", cmd, _cfg,
1368                                             file, function, apr_hook_when);
1369}
1370static const char *register_access_checker_block(cmd_parms *cmd, void *_cfg,
1371                                                 const char *line)
1372{
1373
1374    return register_named_block_function_hook("access_checker", cmd, _cfg,
1375                                              line);
1376}
1377
1378static const char *register_auth_checker_hook(cmd_parms *cmd, void *_cfg,
1379                                              const char *file,
1380                                              const char *function,
1381                                              const char *when)
1382{
1383    int apr_hook_when = APR_HOOK_MIDDLE;
1384
1385    if (when) {
1386        if (!strcasecmp(when, "early")) {
1387            apr_hook_when = AP_LUA_HOOK_FIRST;
1388        }
1389        else if (!strcasecmp(when, "late")) {
1390            apr_hook_when = AP_LUA_HOOK_LAST;
1391        }
1392        else {
1393            return "Third argument must be 'early' or 'late'";
1394        }
1395    }
1396
1397    return register_named_file_function_hook("auth_checker", cmd, _cfg, file,
1398                                             function, apr_hook_when);
1399}
1400static const char *register_auth_checker_block(cmd_parms *cmd, void *_cfg,
1401                                               const char *line)
1402{
1403    return register_named_block_function_hook("auth_checker", cmd, _cfg,
1404                                              line);
1405}
1406
1407static const char *register_insert_filter_hook(cmd_parms *cmd, void *_cfg,
1408                                               const char *file,
1409                                               const char *function)
1410{
1411    return "LuaHookInsertFilter not yet implemented";
1412}
1413
1414static const char *register_quick_hook(cmd_parms *cmd, void *_cfg,
1415                                       const char *file, const char *function)
1416{
1417    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|
1418                                                NOT_IN_HTACCESS);
1419    if (err) {
1420        return err;
1421    }
1422    return register_named_file_function_hook("quick", cmd, _cfg, file,
1423                                             function, APR_HOOK_MIDDLE);
1424}
1425static const char *register_map_handler(cmd_parms *cmd, void *_cfg,
1426                                       const char* match, const char *file, const char *function)
1427{
1428    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|
1429                                                NOT_IN_HTACCESS);
1430    if (err) {
1431        return err;
1432    }
1433    if (!function) function = "handle";
1434    return register_mapped_file_function_hook(match, cmd, _cfg, file,
1435                                             function);
1436}
1437static const char *register_output_filter(cmd_parms *cmd, void *_cfg,
1438                                       const char* filter, const char *file, const char *function)
1439{
1440    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|
1441                                                NOT_IN_HTACCESS);
1442    if (err) {
1443        return err;
1444    }
1445    if (!function) function = "handle";
1446    return register_filter_function_hook(filter, cmd, _cfg, file,
1447                                             function, AP_LUA_FILTER_OUTPUT);
1448}
1449static const char *register_input_filter(cmd_parms *cmd, void *_cfg,
1450                                       const char* filter, const char *file, const char *function)
1451{
1452    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|
1453                                                NOT_IN_HTACCESS);
1454    if (err) {
1455        return err;
1456    }
1457    if (!function) function = "handle";
1458    return register_filter_function_hook(filter, cmd, _cfg, file,
1459                                             function, AP_LUA_FILTER_INPUT);
1460}
1461static const char *register_quick_block(cmd_parms *cmd, void *_cfg,
1462                                        const char *line)
1463{
1464    return register_named_block_function_hook("quick", cmd, _cfg,
1465                                              line);
1466}
1467
1468
1469
1470static const char *register_package_helper(cmd_parms *cmd,
1471                                           const char *arg,
1472                                           apr_array_header_t *dir_array)
1473{
1474    apr_status_t rv;
1475
1476    ap_lua_server_cfg *server_cfg =
1477        ap_get_module_config(cmd->server->module_config, &lua_module);
1478
1479    char *fixed_filename;
1480    rv = apr_filepath_merge(&fixed_filename,
1481                            server_cfg->root_path,
1482                            arg,
1483                            APR_FILEPATH_NOTRELATIVE,
1484                            cmd->pool);
1485
1486    if (rv != APR_SUCCESS) {
1487        return apr_psprintf(cmd->pool,
1488                            "Unable to build full path to file, %s", arg);
1489    }
1490
1491    *(const char **) apr_array_push(dir_array) = fixed_filename;
1492    return NULL;
1493}
1494
1495
1496/**
1497 * Called for config directive which looks like
1498 * LuaPackagePath /lua/package/path/mapped/thing/like/this/?.lua
1499 */
1500static const char *register_package_dir(cmd_parms *cmd, void *_cfg,
1501                                        const char *arg)
1502{
1503    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
1504
1505    return register_package_helper(cmd, arg, cfg->package_paths);
1506}
1507
1508/**
1509 * Called for config directive which looks like
1510 * LuaPackageCPath /lua/package/path/mapped/thing/like/this/?.so
1511 */
1512static const char *register_package_cdir(cmd_parms *cmd,
1513                                         void *_cfg,
1514                                         const char *arg)
1515{
1516    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
1517
1518    return register_package_helper(cmd, arg, cfg->package_cpaths);
1519}
1520
1521static const char *register_lua_inherit(cmd_parms *cmd,
1522                                      void *_cfg,
1523                                      const char *arg)
1524{
1525    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
1526
1527    if (strcasecmp("none", arg) == 0) {
1528        cfg->inherit = AP_LUA_INHERIT_NONE;
1529    }
1530    else if (strcasecmp("parent-first", arg) == 0) {
1531        cfg->inherit = AP_LUA_INHERIT_PARENT_FIRST;
1532    }
1533    else if (strcasecmp("parent-last", arg) == 0) {
1534        cfg->inherit = AP_LUA_INHERIT_PARENT_LAST;
1535    }
1536    else {
1537        return apr_psprintf(cmd->pool,
1538                            "LuaInherit type of '%s' not recognized, valid "
1539                            "options are 'none', 'parent-first', and 'parent-last'",
1540                            arg);
1541    }
1542    return NULL;
1543}
1544static const char *register_lua_codecache(cmd_parms *cmd,
1545                                      void *_cfg,
1546                                      const char *arg)
1547{
1548    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
1549
1550    if (strcasecmp("never", arg) == 0) {
1551        cfg->codecache = AP_LUA_CACHE_NEVER;
1552    }
1553    else if (strcasecmp("stat", arg) == 0) {
1554        cfg->codecache = AP_LUA_CACHE_STAT;
1555    }
1556    else if (strcasecmp("forever", arg) == 0) {
1557        cfg->codecache = AP_LUA_CACHE_FOREVER;
1558    }
1559    else {
1560        return apr_psprintf(cmd->pool,
1561                            "LuaCodeCache type of '%s' not recognized, valid "
1562                            "options are 'never', 'stat', and 'forever'",
1563                            arg);
1564    }
1565    return NULL;
1566}
1567static const char *register_lua_scope(cmd_parms *cmd,
1568                                      void *_cfg,
1569                                      const char *scope,
1570                                      const char *min,
1571                                      const char *max)
1572{
1573    ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
1574    if (strcmp("once", scope) == 0) {
1575        cfg->vm_scope = AP_LUA_SCOPE_ONCE;
1576    }
1577    else if (strcmp("request", scope) == 0) {
1578        cfg->vm_scope = AP_LUA_SCOPE_REQUEST;
1579    }
1580    else if (strcmp("conn", scope) == 0) {
1581        cfg->vm_scope = AP_LUA_SCOPE_CONN;
1582    }
1583    else if (strcmp("thread", scope) == 0) {
1584#if !APR_HAS_THREADS
1585        return apr_psprintf(cmd->pool,
1586                            "Scope type of '%s' cannot be used because this "
1587                            "server does not have threading support "
1588                            "(APR_HAS_THREADS)"
1589                            scope);
1590#endif
1591        cfg->vm_scope = AP_LUA_SCOPE_THREAD;
1592    }
1593    else if (strcmp("server", scope) == 0) {
1594        unsigned int vmin, vmax;
1595#if !APR_HAS_THREADS
1596        return apr_psprintf(cmd->pool,
1597                            "Scope type of '%s' cannot be used because this "
1598                            "server does not have threading support "
1599                            "(APR_HAS_THREADS)"
1600                            scope);
1601#endif
1602        cfg->vm_scope = AP_LUA_SCOPE_SERVER;
1603        vmin = min ? atoi(min) : 1;
1604        vmax = max ? atoi(max) : 1;
1605        if (vmin == 0) {
1606            vmin = 1;
1607        }
1608        if (vmax < vmin) {
1609            vmax = vmin;
1610        }
1611        cfg->vm_min = vmin;
1612        cfg->vm_max = vmax;
1613    }
1614    else {
1615        return apr_psprintf(cmd->pool,
1616                            "Invalid value for LuaScope, '%s', acceptable "
1617                            "values are: 'once', 'request', 'conn'"
1618#if APR_HAS_THREADS
1619                            ", 'thread', 'server'"
1620#endif
1621                            ,scope);
1622    }
1623
1624    return NULL;
1625}
1626
1627
1628
1629static const char *register_lua_root(cmd_parms *cmd, void *_cfg,
1630                                     const char *root)
1631{
1632    /* ap_lua_dir_cfg* cfg = (ap_lua_dir_cfg*)_cfg; */
1633    ap_lua_server_cfg *cfg = ap_get_module_config(cmd->server->module_config,
1634                                                  &lua_module);
1635
1636    cfg->root_path = root;
1637    return NULL;
1638}
1639
1640const char *ap_lua_ssl_val(apr_pool_t *p, server_rec *s, conn_rec *c,
1641                           request_rec *r, const char *var)
1642{
1643    if (lua_ssl_val) {
1644        return (const char *)lua_ssl_val(p, s, c, r, (char *)var);
1645    }
1646    return NULL;
1647}
1648
1649int ap_lua_ssl_is_https(conn_rec *c)
1650{
1651    return lua_ssl_is_https ? lua_ssl_is_https(c) : 0;
1652}
1653
1654/*******************************/
1655
1656static const char *lua_authz_parse(cmd_parms *cmd, const char *require_line,
1657                                   const void **parsed_require_line)
1658{
1659    const char *provider_name;
1660    lua_authz_provider_spec *spec;
1661
1662    apr_pool_userdata_get((void**)&provider_name, AUTHZ_PROVIDER_NAME_NOTE,
1663                          cmd->temp_pool);
1664    ap_assert(provider_name != NULL);
1665
1666    spec = apr_hash_get(lua_authz_providers, provider_name, APR_HASH_KEY_STRING);
1667    ap_assert(spec != NULL);
1668
1669    if (require_line && *require_line) {
1670        const char *arg;
1671        spec->args = apr_array_make(cmd->pool, 2, sizeof(const char *));
1672        while ((arg = ap_getword_conf(cmd->pool, &require_line)) && *arg) {
1673            APR_ARRAY_PUSH(spec->args, const char *) = arg;
1674        }
1675    }
1676
1677    *parsed_require_line = spec;
1678    return NULL;
1679}
1680
1681static authz_status lua_authz_check(request_rec *r, const char *require_line,
1682                                    const void *parsed_require_line)
1683{
1684    apr_pool_t *pool;
1685    ap_lua_vm_spec *spec;
1686    lua_State *L;
1687    ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config,
1688                                                         &lua_module);
1689    const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
1690                                                     &lua_module);
1691    const lua_authz_provider_spec *prov_spec = parsed_require_line;
1692    int result;
1693    int nargs = 0;
1694
1695    spec = create_vm_spec(&pool, r, cfg, server_cfg, prov_spec->file_name,
1696                          NULL, 0, prov_spec->function_name, "authz provider");
1697
1698    L = ap_lua_get_lua_state(pool, spec, r);
1699    if (L == NULL) {
1700        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02314)
1701                      "Unable to compile VM for authz provider %s", prov_spec->name);
1702        return AUTHZ_GENERAL_ERROR;
1703    }
1704    lua_getglobal(L, prov_spec->function_name);
1705    if (!lua_isfunction(L, -1)) {
1706        ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02319)
1707                      "Unable to find function %s in %s",
1708                      prov_spec->function_name, prov_spec->file_name);
1709        ap_lua_release_state(L, spec, r);
1710        return AUTHZ_GENERAL_ERROR;
1711    }
1712    ap_lua_run_lua_request(L, r);
1713    if (prov_spec->args) {
1714        int i;
1715        if (!lua_checkstack(L, prov_spec->args->nelts)) {
1716            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02315)
1717                          "Error: authz provider %s: too many arguments", prov_spec->name);
1718            ap_lua_release_state(L, spec, r);
1719            return AUTHZ_GENERAL_ERROR;
1720        }
1721        for (i = 0; i < prov_spec->args->nelts; i++) {
1722            const char *arg = APR_ARRAY_IDX(prov_spec->args, i, const char *);
1723            lua_pushstring(L, arg);
1724        }
1725        nargs = prov_spec->args->nelts;
1726    }
1727    if (lua_pcall(L, 1 + nargs, 1, 0)) {
1728        const char *err = lua_tostring(L, -1);
1729        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02316)
1730                      "Error executing authz provider %s: %s", prov_spec->name, err);
1731        ap_lua_release_state(L, spec, r);
1732        return AUTHZ_GENERAL_ERROR;
1733    }
1734    if (!lua_isnumber(L, -1)) {
1735        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02317)
1736                      "Error: authz provider %s did not return integer", prov_spec->name);
1737        ap_lua_release_state(L, spec, r);
1738        return AUTHZ_GENERAL_ERROR;
1739    }
1740    result = lua_tointeger(L, -1);
1741    ap_lua_release_state(L, spec, r);
1742    switch (result) {
1743        case AUTHZ_DENIED:
1744        case AUTHZ_GRANTED:
1745        case AUTHZ_NEUTRAL:
1746        case AUTHZ_GENERAL_ERROR:
1747        case AUTHZ_DENIED_NO_USER:
1748            return result;
1749        default:
1750            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02318)
1751                          "Error: authz provider %s: invalid return value %d",
1752                          prov_spec->name, result);
1753    }
1754    return AUTHZ_GENERAL_ERROR;
1755}
1756
1757static const authz_provider lua_authz_provider =
1758{
1759    &lua_authz_check,
1760    &lua_authz_parse,
1761};
1762
1763static const char *register_authz_provider(cmd_parms *cmd, void *_cfg,
1764                                           const char *name, const char *file,
1765                                           const char *function)
1766{
1767    lua_authz_provider_spec *spec;
1768    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1769    if (err)
1770        return err;
1771
1772    spec = apr_pcalloc(cmd->pool, sizeof(*spec));
1773    spec->name = name;
1774    spec->file_name = file;
1775    spec->function_name = function;
1776
1777    apr_hash_set(lua_authz_providers, name, APR_HASH_KEY_STRING, spec);
1778    ap_register_auth_provider(cmd->pool, AUTHZ_PROVIDER_GROUP, name,
1779                              AUTHZ_PROVIDER_VERSION,
1780                              &lua_authz_provider,
1781                              AP_AUTH_INTERNAL_PER_CONF);
1782    return NULL;
1783}
1784
1785
1786command_rec lua_commands[] = {
1787
1788    AP_INIT_TAKE1("LuaRoot", register_lua_root, NULL, OR_ALL,
1789                  "Specify the base path for resolving relative paths for mod_lua directives"),
1790
1791    AP_INIT_TAKE1("LuaPackagePath", register_package_dir, NULL, OR_ALL,
1792                  "Add a directory to lua's package.path"),
1793
1794    AP_INIT_TAKE1("LuaPackageCPath", register_package_cdir, NULL, OR_ALL,
1795                  "Add a directory to lua's package.cpath"),
1796
1797    AP_INIT_TAKE3("LuaAuthzProvider", register_authz_provider, NULL, RSRC_CONF|EXEC_ON_READ,
1798                  "Provide an authorization provider"),
1799
1800    AP_INIT_TAKE23("LuaHookTranslateName", register_translate_name_hook, NULL,
1801                  OR_ALL,
1802                  "Provide a hook for the translate name phase of request processing"),
1803
1804    AP_INIT_RAW_ARGS("<LuaHookTranslateName", register_translate_name_block,
1805                     NULL,
1806                     EXEC_ON_READ | OR_ALL,
1807                     "Provide a hook for the translate name phase of request processing"),
1808
1809    AP_INIT_TAKE2("LuaHookFixups", register_fixups_hook, NULL, OR_ALL,
1810                  "Provide a hook for the fixups phase of request processing"),
1811    AP_INIT_RAW_ARGS("<LuaHookFixups", register_fixups_block, NULL,
1812                     EXEC_ON_READ | OR_ALL,
1813                     "Provide a inline hook for the fixups phase of request processing"),
1814
1815    /* todo: test */
1816    AP_INIT_TAKE2("LuaHookMapToStorage", register_map_to_storage_hook, NULL,
1817                  OR_ALL,
1818                  "Provide a hook for the map_to_storage phase of request processing"),
1819    AP_INIT_RAW_ARGS("<LuaHookMapToStorage", register_map_to_storage_block,
1820                     NULL,
1821                     EXEC_ON_READ | OR_ALL,
1822                     "Provide a hook for the map_to_storage phase of request processing"),
1823
1824    /* todo: test */
1825    AP_INIT_TAKE23("LuaHookCheckUserID", register_check_user_id_hook, NULL,
1826                  OR_ALL,
1827                  "Provide a hook for the check_user_id phase of request processing"),
1828    AP_INIT_RAW_ARGS("<LuaHookCheckUserID", register_check_user_id_block,
1829                     NULL,
1830                     EXEC_ON_READ | OR_ALL,
1831                     "Provide a hook for the check_user_id phase of request processing"),
1832
1833    /* todo: test */
1834    AP_INIT_TAKE2("LuaHookTypeChecker", register_type_checker_hook, NULL,
1835                  OR_ALL,
1836                  "Provide a hook for the type_checker phase of request processing"),
1837    AP_INIT_RAW_ARGS("<LuaHookTypeChecker", register_type_checker_block, NULL,
1838                     EXEC_ON_READ | OR_ALL,
1839                     "Provide a hook for the type_checker phase of request processing"),
1840
1841    /* todo: test */
1842    AP_INIT_TAKE23("LuaHookAccessChecker", register_access_checker_hook, NULL,
1843                  OR_ALL,
1844                  "Provide a hook for the access_checker phase of request processing"),
1845    AP_INIT_RAW_ARGS("<LuaHookAccessChecker", register_access_checker_block,
1846                     NULL,
1847                     EXEC_ON_READ | OR_ALL,
1848                     "Provide a hook for the access_checker phase of request processing"),
1849
1850    /* todo: test */
1851    AP_INIT_TAKE23("LuaHookAuthChecker", register_auth_checker_hook, NULL,
1852                  OR_ALL,
1853                  "Provide a hook for the auth_checker phase of request processing"),
1854    AP_INIT_RAW_ARGS("<LuaHookAuthChecker", register_auth_checker_block, NULL,
1855                     EXEC_ON_READ | OR_ALL,
1856                     "Provide a hook for the auth_checker phase of request processing"),
1857
1858    /* todo: test */
1859    AP_INIT_TAKE2("LuaHookInsertFilter", register_insert_filter_hook, NULL,
1860                  OR_ALL,
1861                  "Provide a hook for the insert_filter phase of request processing"),
1862
1863    AP_INIT_TAKE2("LuaHookLog", register_log_transaction_hook, NULL,
1864                  OR_ALL,
1865                  "Provide a hook for the logging phase of request processing"),
1866
1867    AP_INIT_TAKE123("LuaScope", register_lua_scope, NULL, OR_ALL,
1868                    "One of once, request, conn, server -- default is once"),
1869
1870    AP_INIT_TAKE1("LuaInherit", register_lua_inherit, NULL, OR_ALL,
1871     "Controls how Lua scripts in parent contexts are merged with the current "
1872     " context: none|parent-last|parent-first (default: parent-first) "),
1873
1874    AP_INIT_TAKE1("LuaCodeCache", register_lua_codecache, NULL, OR_ALL,
1875     "Controls the behavior of the in-memory code cache "
1876     " context: stat|forever|never (default: stat) "),
1877
1878    AP_INIT_TAKE2("LuaQuickHandler", register_quick_hook, NULL, OR_ALL,
1879                  "Provide a hook for the quick handler of request processing"),
1880    AP_INIT_RAW_ARGS("<LuaQuickHandler", register_quick_block, NULL,
1881                     EXEC_ON_READ | OR_ALL,
1882                     "Provide a hook for the quick handler of request processing"),
1883    AP_INIT_RAW_ARGS("Lua_____ByteCodeHack", hack_section_handler, NULL,
1884                     OR_ALL,
1885                     "(internal) Byte code handler"),
1886    AP_INIT_TAKE23("LuaMapHandler", register_map_handler, NULL, OR_ALL,
1887                  "Maps a path to a lua handler"),
1888    AP_INIT_TAKE3("LuaOutputFilter", register_output_filter, NULL, OR_ALL,
1889                  "Registers a Lua function as an output filter"),
1890    AP_INIT_TAKE3("LuaInputFilter", register_input_filter, NULL, OR_ALL,
1891                  "Registers a Lua function as an input filter"),
1892    {NULL}
1893};
1894
1895
1896static void *create_dir_config(apr_pool_t *p, char *dir)
1897{
1898    ap_lua_dir_cfg *cfg = apr_pcalloc(p, sizeof(ap_lua_dir_cfg));
1899    cfg->package_paths = apr_array_make(p, 2, sizeof(char *));
1900    cfg->package_cpaths = apr_array_make(p, 2, sizeof(char *));
1901    cfg->mapped_handlers =
1902        apr_array_make(p, 1, sizeof(ap_lua_mapped_handler_spec *));
1903    cfg->mapped_filters =
1904        apr_array_make(p, 1, sizeof(ap_lua_filter_handler_spec *));
1905    cfg->pool = p;
1906    cfg->hooks = apr_hash_make(p);
1907    cfg->dir = apr_pstrdup(p, dir);
1908    cfg->vm_scope = AP_LUA_SCOPE_UNSET;
1909    cfg->codecache = AP_LUA_CACHE_UNSET;
1910    cfg->vm_min = 0;
1911    cfg->vm_max = 0;
1912
1913    return cfg;
1914}
1915
1916static int create_request_config(request_rec *r)
1917{
1918    ap_lua_request_cfg *cfg = apr_palloc(r->pool, sizeof(ap_lua_request_cfg));
1919    cfg->mapped_request_details = NULL;
1920    cfg->request_scoped_vms = apr_hash_make(r->pool);
1921    ap_set_module_config(r->request_config, &lua_module, cfg);
1922    return OK;
1923}
1924
1925static void *create_server_config(apr_pool_t *p, server_rec *s)
1926{
1927
1928    ap_lua_server_cfg *cfg = apr_pcalloc(p, sizeof(ap_lua_server_cfg));
1929    cfg->vm_reslists = apr_hash_make(p);
1930    apr_thread_rwlock_create(&cfg->vm_reslists_lock, p);
1931    cfg->root_path = NULL;
1932
1933    return cfg;
1934}
1935
1936static int lua_request_hook(lua_State *L, request_rec *r)
1937{
1938    ap_lua_push_request(L, r);
1939    return OK;
1940}
1941
1942static int lua_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1943                             apr_pool_t *ptemp, server_rec *s)
1944{
1945    lua_ssl_val = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
1946    lua_ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
1947    return OK;
1948}
1949static void *overlay_hook_specs(apr_pool_t *p,
1950                                        const void *key,
1951                                        apr_ssize_t klen,
1952                                        const void *overlay_val,
1953                                        const void *base_val,
1954                                        const void *data)
1955{
1956    const apr_array_header_t *overlay_info = (const apr_array_header_t*)overlay_val;
1957    const apr_array_header_t *base_info = (const apr_array_header_t*)base_val;
1958    return apr_array_append(p, base_info, overlay_info);
1959}
1960
1961static void *merge_dir_config(apr_pool_t *p, void *basev, void *overridesv)
1962{
1963    ap_lua_dir_cfg *a, *base, *overrides;
1964
1965    a         = (ap_lua_dir_cfg *)apr_pcalloc(p, sizeof(ap_lua_dir_cfg));
1966    base      = (ap_lua_dir_cfg*)basev;
1967    overrides = (ap_lua_dir_cfg*)overridesv;
1968
1969    a->pool = overrides->pool;
1970    a->dir = apr_pstrdup(p, overrides->dir);
1971
1972    a->vm_scope = (overrides->vm_scope == AP_LUA_SCOPE_UNSET) ? base->vm_scope: overrides->vm_scope;
1973    a->inherit = (overrides->inherit == AP_LUA_INHERIT_UNSET) ? base->inherit : overrides->inherit;
1974    a->codecache = (overrides->codecache == AP_LUA_CACHE_UNSET) ? base->codecache : overrides->codecache;
1975
1976    a->vm_min = (overrides->vm_min == 0) ? base->vm_min : overrides->vm_min;
1977    a->vm_max = (overrides->vm_max == 0) ? base->vm_max : overrides->vm_max;
1978
1979    if (a->inherit == AP_LUA_INHERIT_UNSET || a->inherit == AP_LUA_INHERIT_PARENT_FIRST) {
1980        a->package_paths = apr_array_append(p, base->package_paths, overrides->package_paths);
1981        a->package_cpaths = apr_array_append(p, base->package_cpaths, overrides->package_cpaths);
1982        a->mapped_handlers = apr_array_append(p, base->mapped_handlers, overrides->mapped_handlers);
1983        a->mapped_filters = apr_array_append(p, base->mapped_filters, overrides->mapped_filters);
1984        a->hooks = apr_hash_merge(p, overrides->hooks, base->hooks, overlay_hook_specs, NULL);
1985    }
1986    else if (a->inherit == AP_LUA_INHERIT_PARENT_LAST) {
1987        a->package_paths = apr_array_append(p, overrides->package_paths, base->package_paths);
1988        a->package_cpaths = apr_array_append(p, overrides->package_cpaths, base->package_cpaths);
1989        a->mapped_handlers = apr_array_append(p, overrides->mapped_handlers, base->mapped_handlers);
1990        a->mapped_filters = apr_array_append(p, overrides->mapped_filters, base->mapped_filters);
1991        a->hooks = apr_hash_merge(p, base->hooks, overrides->hooks, overlay_hook_specs, NULL);
1992    }
1993    else {
1994        a->package_paths = overrides->package_paths;
1995        a->package_cpaths = overrides->package_cpaths;
1996        a->mapped_handlers= overrides->mapped_handlers;
1997        a->mapped_filters= overrides->mapped_filters;
1998        a->hooks= overrides->hooks;
1999    }
2000
2001    return a;
2002}
2003
2004static void lua_register_hooks(apr_pool_t *p)
2005{
2006    /* ap_register_output_filter("luahood", luahood, NULL, AP_FTYPE_RESOURCE); */
2007    ap_hook_handler(lua_handler, NULL, NULL, APR_HOOK_MIDDLE);
2008    ap_hook_create_request(create_request_config, NULL, NULL,
2009                           APR_HOOK_MIDDLE);
2010
2011    /* http_request.h hooks */
2012    ap_hook_translate_name(lua_translate_name_harness_first, NULL, NULL,
2013                           AP_LUA_HOOK_FIRST);
2014    ap_hook_translate_name(lua_translate_name_harness, NULL, NULL,
2015                           APR_HOOK_MIDDLE);
2016    ap_hook_translate_name(lua_translate_name_harness_last, NULL, NULL,
2017                           AP_LUA_HOOK_LAST);
2018
2019    ap_hook_fixups(lua_fixup_harness, NULL, NULL, APR_HOOK_MIDDLE);
2020    ap_hook_map_to_storage(lua_map_to_storage_harness, NULL, NULL,
2021                           APR_HOOK_MIDDLE);
2022
2023    ap_hook_check_user_id(lua_check_user_id_harness_first, NULL, NULL,
2024                          AP_LUA_HOOK_FIRST);
2025    ap_hook_check_user_id(lua_check_user_id_harness, NULL, NULL,
2026                           APR_HOOK_MIDDLE);
2027    ap_hook_check_user_id(lua_check_user_id_harness_last, NULL, NULL,
2028                          AP_LUA_HOOK_LAST);
2029
2030    ap_hook_type_checker(lua_type_checker_harness, NULL, NULL,
2031                         APR_HOOK_MIDDLE);
2032
2033    ap_hook_access_checker(lua_access_checker_harness_first, NULL, NULL,
2034                           AP_LUA_HOOK_FIRST);
2035    ap_hook_access_checker(lua_access_checker_harness, NULL, NULL,
2036                           APR_HOOK_MIDDLE);
2037    ap_hook_access_checker(lua_access_checker_harness_last, NULL, NULL,
2038                           AP_LUA_HOOK_LAST);
2039    ap_hook_auth_checker(lua_auth_checker_harness_first, NULL, NULL,
2040                         AP_LUA_HOOK_FIRST);
2041    ap_hook_auth_checker(lua_auth_checker_harness, NULL, NULL,
2042                         APR_HOOK_MIDDLE);
2043    ap_hook_auth_checker(lua_auth_checker_harness_last, NULL, NULL,
2044                         AP_LUA_HOOK_LAST);
2045
2046    ap_hook_insert_filter(lua_insert_filter_harness, NULL, NULL,
2047                          APR_HOOK_MIDDLE);
2048    ap_hook_quick_handler(lua_quick_harness, NULL, NULL, APR_HOOK_FIRST);
2049
2050    ap_hook_post_config(lua_post_config, NULL, NULL, APR_HOOK_MIDDLE);
2051
2052    APR_OPTIONAL_HOOK(ap_lua, lua_open, lua_open_hook, NULL, NULL,
2053                      APR_HOOK_REALLY_FIRST);
2054
2055    APR_OPTIONAL_HOOK(ap_lua, lua_request, lua_request_hook, NULL, NULL,
2056                      APR_HOOK_REALLY_FIRST);
2057    ap_hook_handler(lua_map_handler, NULL, NULL, AP_LUA_HOOK_FIRST);
2058    /* Hook this right before FallbackResource kicks in */
2059    ap_hook_fixups(lua_map_handler_fixups, NULL, NULL, AP_LUA_HOOK_LAST-2);
2060#if APR_HAS_THREADS
2061    ap_hook_child_init(ap_lua_init_mutex, NULL, NULL, APR_HOOK_MIDDLE);
2062#endif
2063    /* providers */
2064    lua_authz_providers = apr_hash_make(p);
2065
2066    /* ivm mutex */
2067    apr_thread_mutex_create(&lua_ivm_mutex, APR_THREAD_MUTEX_DEFAULT, p);
2068
2069    /* Logging catcher */
2070    ap_hook_log_transaction(lua_log_transaction_harness,NULL,NULL,
2071                            APR_HOOK_FIRST);
2072}
2073
2074AP_DECLARE_MODULE(lua) = {
2075    STANDARD20_MODULE_STUFF,
2076    create_dir_config,          /* create per-dir    config structures */
2077    merge_dir_config,           /* merge  per-dir    config structures */
2078    create_server_config,       /* create per-server config structures */
2079    NULL,                       /* merge  per-server config structures */
2080    lua_commands,               /* table of config file commands       */
2081    lua_register_hooks          /* register hooks                      */
2082};
2083