Annotation of embedaddon/lighttpd/src/mod_ssi.c, revision 1.1.1.3

1.1.1.3 ! misho       1: #include "first.h"
        !             2: 
1.1       misho       3: #include "base.h"
                      4: #include "log.h"
                      5: #include "buffer.h"
                      6: 
                      7: #include "plugin.h"
                      8: #include "stream.h"
                      9: 
                     10: #include "response.h"
                     11: 
                     12: #include "mod_ssi.h"
                     13: 
                     14: #include "inet_ntop_cache.h"
                     15: 
                     16: #include "sys-socket.h"
                     17: 
                     18: #include <sys/types.h>
                     19: 
                     20: #include <ctype.h>
                     21: #include <stdlib.h>
                     22: #include <stdio.h>
                     23: #include <string.h>
1.1.1.3 ! misho      24: #include <strings.h>
1.1       misho      25: #include <errno.h>
1.1.1.3 ! misho      26: #include <fcntl.h>
1.1       misho      27: #include <time.h>
                     28: #include <unistd.h>
                     29: 
                     30: #ifdef HAVE_PWD_H
                     31: # include <pwd.h>
                     32: #endif
                     33: 
                     34: #ifdef HAVE_FORK
                     35: # include <sys/wait.h>
                     36: #endif
                     37: 
                     38: #ifdef HAVE_SYS_FILIO_H
                     39: # include <sys/filio.h>
                     40: #endif
                     41: 
                     42: #include "etag.h"
                     43: 
                     44: /* The newest modified time of included files for include statement */
                     45: static volatile time_t include_file_last_mtime = 0;
                     46: 
                     47: /* init the plugin data */
                     48: INIT_FUNC(mod_ssi_init) {
                     49:        plugin_data *p;
                     50: 
                     51:        p = calloc(1, sizeof(*p));
                     52: 
                     53:        p->timefmt = buffer_init();
                     54:        p->stat_fn = buffer_init();
                     55: 
                     56:        p->ssi_vars = array_init();
                     57:        p->ssi_cgi_env = array_init();
                     58: 
                     59:        return p;
                     60: }
                     61: 
                     62: /* detroy the plugin data */
                     63: FREE_FUNC(mod_ssi_free) {
                     64:        plugin_data *p = p_d;
                     65:        UNUSED(srv);
                     66: 
                     67:        if (!p) return HANDLER_GO_ON;
                     68: 
                     69:        if (p->config_storage) {
                     70:                size_t i;
                     71:                for (i = 0; i < srv->config_context->used; i++) {
                     72:                        plugin_config *s = p->config_storage[i];
                     73: 
1.1.1.3 ! misho      74:                        if (NULL == s) continue;
        !            75: 
1.1       misho      76:                        array_free(s->ssi_extension);
                     77:                        buffer_free(s->content_type);
                     78: 
                     79:                        free(s);
                     80:                }
                     81:                free(p->config_storage);
                     82:        }
                     83: 
                     84:        array_free(p->ssi_vars);
                     85:        array_free(p->ssi_cgi_env);
                     86:        buffer_free(p->timefmt);
                     87:        buffer_free(p->stat_fn);
                     88: 
                     89:        free(p);
                     90: 
                     91:        return HANDLER_GO_ON;
                     92: }
                     93: 
                     94: /* handle plugin config and check values */
                     95: 
                     96: SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
                     97:        plugin_data *p = p_d;
                     98:        size_t i = 0;
                     99: 
                    100:        config_values_t cv[] = {
                    101:                { "ssi.extension",              NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
                    102:                { "ssi.content-type",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },      /* 1 */
1.1.1.3 ! misho     103:                { "ssi.conditional-requests",   NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },     /* 2 */
        !           104:                { "ssi.exec",                   NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },     /* 3 */
1.1       misho     105:                { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
                    106:        };
                    107: 
                    108:        if (!p) return HANDLER_ERROR;
                    109: 
1.1.1.2   misho     110:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1       misho     111: 
                    112:        for (i = 0; i < srv->config_context->used; i++) {
1.1.1.3 ! misho     113:                data_config const* config = (data_config const*)srv->config_context->data[i];
1.1       misho     114:                plugin_config *s;
                    115: 
                    116:                s = calloc(1, sizeof(plugin_config));
                    117:                s->ssi_extension  = array_init();
                    118:                s->content_type = buffer_init();
1.1.1.3 ! misho     119:                s->conditional_requests = 0;
        !           120:                s->ssi_exec = 1;
1.1       misho     121: 
                    122:                cv[0].destination = s->ssi_extension;
                    123:                cv[1].destination = s->content_type;
1.1.1.3 ! misho     124:                cv[2].destination = &(s->conditional_requests);
        !           125:                cv[3].destination = &(s->ssi_exec);
1.1       misho     126: 
                    127:                p->config_storage[i] = s;
                    128: 
1.1.1.3 ! misho     129:                if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1.1       misho     130:                        return HANDLER_ERROR;
                    131:                }
                    132:        }
                    133: 
                    134:        return HANDLER_GO_ON;
                    135: }
                    136: 
1.1.1.3 ! misho     137: 
1.1       misho     138: static int ssi_env_add(array *env, const char *key, const char *val) {
                    139:        data_string *ds;
                    140: 
                    141:        if (NULL == (ds = (data_string *)array_get_unused_element(env, TYPE_STRING))) {
                    142:                ds = data_string_init();
                    143:        }
                    144:        buffer_copy_string(ds->key,   key);
                    145:        buffer_copy_string(ds->value, val);
                    146: 
                    147:        array_insert_unique(env, (data_unset *)ds);
                    148: 
                    149:        return 0;
                    150: }
                    151: 
                    152: /**
                    153:  *
                    154:  *  the next two functions are take from fcgi.c
                    155:  *
                    156:  */
                    157: 
                    158: static int ssi_env_add_request_headers(server *srv, connection *con, plugin_data *p) {
                    159:        size_t i;
                    160: 
                    161:        for (i = 0; i < con->request.headers->used; i++) {
                    162:                data_string *ds;
                    163: 
                    164:                ds = (data_string *)con->request.headers->data[i];
                    165: 
1.1.1.3 ! misho     166:                if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1.1       misho     167:                        /* don't forward the Authorization: Header */
1.1.1.3 ! misho     168:                        if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Authorization"))) {
1.1       misho     169:                                continue;
                    170:                        }
                    171: 
1.1.1.3 ! misho     172:                        /* Do not emit HTTP_PROXY in environment.
        !           173:                         * Some executables use HTTP_PROXY to configure
        !           174:                         * outgoing proxy.  See also https://httpoxy.org/ */
        !           175:                        if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) {
        !           176:                                continue;
1.1       misho     177:                        }
                    178: 
1.1.1.3 ! misho     179:                        buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 1);
1.1       misho     180: 
                    181:                        ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr);
                    182:                }
                    183:        }
                    184: 
                    185:        for (i = 0; i < con->environment->used; i++) {
                    186:                data_string *ds;
                    187: 
                    188:                ds = (data_string *)con->environment->data[i];
                    189: 
1.1.1.3 ! misho     190:                if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
        !           191:                        buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 0);
1.1       misho     192: 
                    193:                        ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr);
                    194:                }
                    195:        }
                    196: 
                    197:        return 0;
                    198: }
                    199: 
                    200: static int build_ssi_cgi_vars(server *srv, connection *con, plugin_data *p) {
1.1.1.3 ! misho     201:        char buf[LI_ITOSTRING_LENGTH];
1.1       misho     202: 
                    203:        server_socket *srv_sock = con->srv_socket;
                    204: 
                    205: #ifdef HAVE_IPV6
                    206:        char b2[INET6_ADDRSTRLEN + 1];
                    207: #endif
                    208: 
                    209: #define CONST_STRING(x) \
                    210:                x
                    211: 
                    212:        array_reset(p->ssi_cgi_env);
                    213: 
1.1.1.3 ! misho     214:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_SOFTWARE"), con->conf.server_tag->ptr);
1.1       misho     215:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_NAME"),
                    216: #ifdef HAVE_IPV6
                    217:                     inet_ntop(srv_sock->addr.plain.sa_family,
                    218:                               srv_sock->addr.plain.sa_family == AF_INET6 ?
                    219:                               (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
                    220:                               (const void *) &(srv_sock->addr.ipv4.sin_addr),
                    221:                               b2, sizeof(b2)-1)
                    222: #else
                    223:                     inet_ntoa(srv_sock->addr.ipv4.sin_addr)
                    224: #endif
                    225:                     );
                    226:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("GATEWAY_INTERFACE"), "CGI/1.1");
                    227: 
1.1.1.3 ! misho     228:        li_utostrn(buf, sizeof(buf),
1.1       misho     229: #ifdef HAVE_IPV6
                    230:               ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
                    231: #else
                    232:               ntohs(srv_sock->addr.ipv4.sin_port)
                    233: #endif
                    234:               );
                    235: 
                    236:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PORT"), buf);
                    237: 
                    238:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("REMOTE_ADDR"),
                    239:                    inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
                    240: 
                    241:        if (con->request.content_length > 0) {
                    242:                /* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */
                    243: 
1.1.1.3 ! misho     244:                li_itostrn(buf, sizeof(buf), con->request.content_length);
1.1       misho     245:                ssi_env_add(p->ssi_cgi_env, CONST_STRING("CONTENT_LENGTH"), buf);
                    246:        }
                    247: 
                    248:        /*
                    249:         * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
                    250:         * http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
                    251:         * (6.1.14, 6.1.6, 6.1.7)
                    252:         */
                    253: 
                    254:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("SCRIPT_NAME"), con->uri.path->ptr);
                    255:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("PATH_INFO"), "");
                    256: 
                    257:        /*
                    258:         * SCRIPT_FILENAME and DOCUMENT_ROOT for php. The PHP manual
                    259:         * http://www.php.net/manual/en/reserved.variables.php
                    260:         * treatment of PATH_TRANSLATED is different from the one of CGI specs.
                    261:         * TODO: this code should be checked against cgi.fix_pathinfo php
                    262:         * parameter.
                    263:         */
                    264: 
1.1.1.3 ! misho     265:        if (!buffer_string_is_empty(con->request.pathinfo)) {
1.1       misho     266:                ssi_env_add(p->ssi_cgi_env, CONST_STRING("PATH_INFO"), con->request.pathinfo->ptr);
                    267:        }
                    268: 
                    269:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("SCRIPT_FILENAME"), con->physical.path->ptr);
1.1.1.3 ! misho     270:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("DOCUMENT_ROOT"), con->physical.basedir->ptr);
1.1       misho     271: 
                    272:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_URI"), con->request.uri->ptr);
1.1.1.3 ! misho     273: 
        !           274:        if (!buffer_string_is_empty(con->uri.scheme)) {
        !           275:                ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_SCHEME"), con->uri.scheme->ptr);
        !           276:        }
        !           277: 
        !           278:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("QUERY_STRING"), buffer_is_empty(con->uri.query) ? "" : con->uri.query->ptr);
1.1       misho     279:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_METHOD"), get_http_method_name(con->request.http_method));
                    280:        ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PROTOCOL"), get_http_version_name(con->request.http_version));
1.1.1.3 ! misho     281:        /* set REDIRECT_STATUS for php compiled with --force-redirect
        !           282:         * (if REDIRECT_STATUS has not already been set by error handler) */
        !           283:        if (0 == con->error_handler_saved_status) {
        !           284:                ssi_env_add(p->ssi_cgi_env, CONST_STRING("REDIRECT_STATUS"), "200");
        !           285:        }
1.1       misho     286: 
                    287:        ssi_env_add_request_headers(srv, con, p);
                    288: 
                    289:        return 0;
                    290: }
                    291: 
1.1.1.3 ! misho     292: static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const char **l, size_t n, struct stat *st) {
        !           293: 
        !           294:        /**
        !           295:         * <!--#element attribute=value attribute=value ... -->
        !           296:         *
        !           297:         * config       DONE
        !           298:         *   errmsg     -- missing
        !           299:         *   sizefmt    DONE
        !           300:         *   timefmt    DONE
        !           301:         * echo         DONE
        !           302:         *   var        DONE
        !           303:         *   encoding   -- missing
        !           304:         * exec         DONE
        !           305:         *   cgi        -- never
        !           306:         *   cmd        DONE
        !           307:         * fsize        DONE
        !           308:         *   file       DONE
        !           309:         *   virtual    DONE
        !           310:         * flastmod     DONE
        !           311:         *   file       DONE
        !           312:         *   virtual    DONE
        !           313:         * include      DONE
        !           314:         *   file       DONE
        !           315:         *   virtual    DONE
        !           316:         * printenv     DONE
        !           317:         * set          DONE
        !           318:         *   var        DONE
        !           319:         *   value      DONE
        !           320:         *
        !           321:         * if           DONE
        !           322:         * elif         DONE
        !           323:         * else         DONE
        !           324:         * endif        DONE
        !           325:         *
        !           326:         *
        !           327:         * expressions
        !           328:         * AND, OR      DONE
        !           329:         * comp         DONE
        !           330:         * ${...}       -- missing
        !           331:         * $...         DONE
        !           332:         * '...'        DONE
        !           333:         * ( ... )      DONE
        !           334:         *
        !           335:         *
        !           336:         *
        !           337:         * ** all DONE **
        !           338:         * DATE_GMT
        !           339:         *   The current date in Greenwich Mean Time.
        !           340:         * DATE_LOCAL
        !           341:         *   The current date in the local time zone.
        !           342:         * DOCUMENT_NAME
        !           343:         *   The filename (excluding directories) of the document requested by the user.
        !           344:         * DOCUMENT_URI
        !           345:         *   The (%-decoded) URL path of the document requested by the user. Note that in the case of nested include files, this is not then URL for the current document.
        !           346:         * LAST_MODIFIED
        !           347:         *   The last modification date of the document requested by the user.
        !           348:         * USER_NAME
        !           349:         *   Contains the owner of the file which included it.
        !           350:         *
        !           351:         */
        !           352: 
1.1       misho     353:        size_t i, ssicmd = 0;
                    354:        char buf[255];
                    355:        buffer *b = NULL;
                    356: 
                    357:        struct {
                    358:                const char *var;
                    359:                enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD,
                    360:                                SSI_CONFIG, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELIF,
                    361:                                SSI_ELSE, SSI_ENDIF, SSI_EXEC } type;
                    362:        } ssicmds[] = {
                    363:                { "echo",     SSI_ECHO },
                    364:                { "include",  SSI_INCLUDE },
                    365:                { "flastmod", SSI_FLASTMOD },
                    366:                { "fsize",    SSI_FSIZE },
                    367:                { "config",   SSI_CONFIG },
                    368:                { "printenv", SSI_PRINTENV },
                    369:                { "set",      SSI_SET },
                    370:                { "if",       SSI_IF },
                    371:                { "elif",     SSI_ELIF },
                    372:                { "endif",    SSI_ENDIF },
                    373:                { "else",     SSI_ELSE },
                    374:                { "exec",     SSI_EXEC },
                    375: 
                    376:                { NULL, SSI_UNSET }
                    377:        };
                    378: 
                    379:        for (i = 0; ssicmds[i].var; i++) {
                    380:                if (0 == strcmp(l[1], ssicmds[i].var)) {
                    381:                        ssicmd = ssicmds[i].type;
                    382:                        break;
                    383:                }
                    384:        }
                    385: 
                    386:        switch(ssicmd) {
                    387:        case SSI_ECHO: {
                    388:                /* echo */
                    389:                int var = 0;
                    390:                /* int enc = 0; */
                    391:                const char *var_val = NULL;
                    392: 
                    393:                struct {
                    394:                        const char *var;
1.1.1.3 ! misho     395:                        enum {
        !           396:                                SSI_ECHO_UNSET,
        !           397:                                SSI_ECHO_DATE_GMT,
        !           398:                                SSI_ECHO_DATE_LOCAL,
        !           399:                                SSI_ECHO_DOCUMENT_NAME,
        !           400:                                SSI_ECHO_DOCUMENT_URI,
        !           401:                                SSI_ECHO_LAST_MODIFIED,
        !           402:                                SSI_ECHO_USER_NAME,
        !           403:                                SSI_ECHO_SCRIPT_URI,
        !           404:                                SSI_ECHO_SCRIPT_URL,
        !           405:                        } type;
1.1       misho     406:                } echovars[] = {
                    407:                        { "DATE_GMT",      SSI_ECHO_DATE_GMT },
                    408:                        { "DATE_LOCAL",    SSI_ECHO_DATE_LOCAL },
                    409:                        { "DOCUMENT_NAME", SSI_ECHO_DOCUMENT_NAME },
                    410:                        { "DOCUMENT_URI",  SSI_ECHO_DOCUMENT_URI },
                    411:                        { "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED },
                    412:                        { "USER_NAME",     SSI_ECHO_USER_NAME },
1.1.1.3 ! misho     413:                        { "SCRIPT_URI",    SSI_ECHO_SCRIPT_URI },
        !           414:                        { "SCRIPT_URL",    SSI_ECHO_SCRIPT_URL },
1.1       misho     415: 
                    416:                        { NULL, SSI_ECHO_UNSET }
                    417:                };
                    418: 
                    419: /*
                    420:                struct {
                    421:                        const char *var;
                    422:                        enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type;
                    423:                } encvars[] = {
                    424:                        { "url",          SSI_ENC_URL },
                    425:                        { "none",         SSI_ENC_NONE },
                    426:                        { "entity",       SSI_ENC_ENTITY },
                    427: 
                    428:                        { NULL, SSI_ENC_UNSET }
                    429:                };
                    430: */
                    431: 
                    432:                for (i = 2; i < n; i += 2) {
                    433:                        if (0 == strcmp(l[i], "var")) {
                    434:                                int j;
                    435: 
                    436:                                var_val = l[i+1];
                    437: 
                    438:                                for (j = 0; echovars[j].var; j++) {
                    439:                                        if (0 == strcmp(l[i+1], echovars[j].var)) {
                    440:                                                var = echovars[j].type;
                    441:                                                break;
                    442:                                        }
                    443:                                }
                    444:                        } else if (0 == strcmp(l[i], "encoding")) {
                    445: /*
                    446:                                int j;
                    447: 
                    448:                                for (j = 0; encvars[j].var; j++) {
                    449:                                        if (0 == strcmp(l[i+1], encvars[j].var)) {
                    450:                                                enc = encvars[j].type;
                    451:                                                break;
                    452:                                        }
                    453:                                }
                    454: */
                    455:                        } else {
                    456:                                log_error_write(srv, __FILE__, __LINE__, "sss",
1.1.1.3 ! misho     457:                                                "ssi: unknown attribute for ",
1.1       misho     458:                                                l[1], l[i]);
                    459:                        }
                    460:                }
                    461: 
                    462:                if (p->if_is_false) break;
                    463: 
                    464:                if (!var_val) {
                    465:                        log_error_write(srv, __FILE__, __LINE__, "sss",
                    466:                                        "ssi: ",
                    467:                                        l[1], "var is missing");
                    468:                        break;
                    469:                }
                    470: 
                    471:                switch(var) {
                    472:                case SSI_ECHO_USER_NAME: {
                    473:                        struct passwd *pw;
                    474: 
1.1.1.3 ! misho     475:                        b = buffer_init();
1.1       misho     476: #ifdef HAVE_PWD_H
1.1.1.3 ! misho     477:                        if (NULL == (pw = getpwuid(st->st_uid))) {
        !           478:                                buffer_copy_int(b, st->st_uid);
1.1       misho     479:                        } else {
                    480:                                buffer_copy_string(b, pw->pw_name);
                    481:                        }
                    482: #else
1.1.1.3 ! misho     483:                        buffer_copy_int(b, st->st_uid);
1.1       misho     484: #endif
1.1.1.3 ! misho     485:                        chunkqueue_append_buffer(con->write_queue, b);
        !           486:                        buffer_free(b);
1.1       misho     487:                        break;
                    488:                }
1.1.1.3 ! misho     489:                case SSI_ECHO_LAST_MODIFIED: {
        !           490:                        time_t t = st->st_mtime;
1.1       misho     491: 
                    492:                        if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
1.1.1.3 ! misho     493:                                chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
1.1       misho     494:                        } else {
1.1.1.3 ! misho     495:                                chunkqueue_append_mem(con->write_queue, buf, strlen(buf));
1.1       misho     496:                        }
                    497:                        break;
                    498:                }
                    499:                case SSI_ECHO_DATE_LOCAL: {
                    500:                        time_t t = time(NULL);
                    501: 
                    502:                        if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
1.1.1.3 ! misho     503:                                chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
1.1       misho     504:                        } else {
1.1.1.3 ! misho     505:                                chunkqueue_append_mem(con->write_queue, buf, strlen(buf));
1.1       misho     506:                        }
                    507:                        break;
                    508:                }
                    509:                case SSI_ECHO_DATE_GMT: {
                    510:                        time_t t = time(NULL);
                    511: 
                    512:                        if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) {
1.1.1.3 ! misho     513:                                chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
1.1       misho     514:                        } else {
1.1.1.3 ! misho     515:                                chunkqueue_append_mem(con->write_queue, buf, strlen(buf));
1.1       misho     516:                        }
                    517:                        break;
                    518:                }
                    519:                case SSI_ECHO_DOCUMENT_NAME: {
                    520:                        char *sl;
                    521: 
                    522:                        if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) {
1.1.1.3 ! misho     523:                                chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->physical.path));
1.1       misho     524:                        } else {
1.1.1.3 ! misho     525:                                chunkqueue_append_mem(con->write_queue, sl + 1, strlen(sl + 1));
1.1       misho     526:                        }
                    527:                        break;
                    528:                }
                    529:                case SSI_ECHO_DOCUMENT_URI: {
1.1.1.3 ! misho     530:                        chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.path));
        !           531:                        break;
        !           532:                }
        !           533:                case SSI_ECHO_SCRIPT_URI: {
        !           534:                        if (!buffer_string_is_empty(con->uri.scheme) && !buffer_string_is_empty(con->uri.authority)) {
        !           535:                                chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.scheme));
        !           536:                                chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("://"));
        !           537:                                chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.authority));
        !           538:                                chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->request.uri));
        !           539:                                if (!buffer_string_is_empty(con->uri.query)) {
        !           540:                                        chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("?"));
        !           541:                                        chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.query));
        !           542:                                }
        !           543:                        }
        !           544:                        break;
        !           545:                }
        !           546:                case SSI_ECHO_SCRIPT_URL: {
        !           547:                        chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->request.uri));
        !           548:                        if (!buffer_string_is_empty(con->uri.query)) {
        !           549:                                chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("?"));
        !           550:                                chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.query));
        !           551:                        }
1.1       misho     552:                        break;
                    553:                }
                    554:                default: {
                    555:                        data_string *ds;
1.1.1.3 ! misho     556:                        /* check if it is a cgi-var or a ssi-var */
1.1       misho     557: 
1.1.1.3 ! misho     558:                        if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, var_val)) ||
        !           559:                            NULL != (ds = (data_string *)array_get_element(p->ssi_vars, var_val))) {
        !           560:                                chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(ds->value));
1.1       misho     561:                        } else {
1.1.1.3 ! misho     562:                                chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
1.1       misho     563:                        }
                    564: 
                    565:                        break;
                    566:                }
                    567:                }
                    568:                break;
                    569:        }
                    570:        case SSI_INCLUDE:
                    571:        case SSI_FLASTMOD:
                    572:        case SSI_FSIZE: {
                    573:                const char * file_path = NULL, *virt_path = NULL;
1.1.1.3 ! misho     574:                struct stat stb;
1.1       misho     575:                char *sl;
                    576: 
                    577:                for (i = 2; i < n; i += 2) {
                    578:                        if (0 == strcmp(l[i], "file")) {
                    579:                                file_path = l[i+1];
                    580:                        } else if (0 == strcmp(l[i], "virtual")) {
                    581:                                virt_path = l[i+1];
                    582:                        } else {
                    583:                                log_error_write(srv, __FILE__, __LINE__, "sss",
1.1.1.3 ! misho     584:                                                "ssi: unknown attribute for ",
1.1       misho     585:                                                l[1], l[i]);
                    586:                        }
                    587:                }
                    588: 
                    589:                if (!file_path && !virt_path) {
                    590:                        log_error_write(srv, __FILE__, __LINE__, "sss",
                    591:                                        "ssi: ",
                    592:                                        l[1], "file or virtual are missing");
                    593:                        break;
                    594:                }
                    595: 
                    596:                if (file_path && virt_path) {
                    597:                        log_error_write(srv, __FILE__, __LINE__, "sss",
                    598:                                        "ssi: ",
                    599:                                        l[1], "only one of file and virtual is allowed here");
                    600:                        break;
                    601:                }
                    602: 
                    603: 
                    604:                if (p->if_is_false) break;
                    605: 
                    606:                if (file_path) {
                    607:                        /* current doc-root */
                    608:                        if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) {
                    609:                                buffer_copy_string_len(p->stat_fn, CONST_STR_LEN("/"));
                    610:                        } else {
                    611:                                buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, sl - con->physical.path->ptr + 1);
                    612:                        }
                    613: 
                    614:                        buffer_copy_string(srv->tmp_buf, file_path);
                    615:                        buffer_urldecode_path(srv->tmp_buf);
                    616:                        buffer_path_simplify(srv->tmp_buf, srv->tmp_buf);
                    617:                        buffer_append_string_buffer(p->stat_fn, srv->tmp_buf);
                    618:                } else {
                    619:                        /* virtual */
1.1.1.3 ! misho     620:                        size_t remain;
1.1       misho     621: 
                    622:                        if (virt_path[0] == '/') {
                    623:                                buffer_copy_string(p->stat_fn, virt_path);
                    624:                        } else {
                    625:                                /* there is always a / */
                    626:                                sl = strrchr(con->uri.path->ptr, '/');
                    627: 
                    628:                                buffer_copy_string_len(p->stat_fn, con->uri.path->ptr, sl - con->uri.path->ptr + 1);
                    629:                                buffer_append_string(p->stat_fn, virt_path);
                    630:                        }
                    631: 
                    632:                        buffer_urldecode_path(p->stat_fn);
                    633:                        buffer_path_simplify(srv->tmp_buf, p->stat_fn);
                    634: 
                    635:                        /* we have an uri */
                    636: 
1.1.1.3 ! misho     637:                        /* Destination physical path (similar to code in mod_webdav.c)
        !           638:                         * src con->physical.path might have been remapped with mod_alias, mod_userdir.
        !           639:                         *   (but neither modifies con->physical.rel_path)
        !           640:                         * Find matching prefix to support relative paths to current physical path.
        !           641:                         * Aliasing of paths underneath current con->physical.basedir might not work.
        !           642:                         * Likewise, mod_rewrite URL rewriting might thwart this comparison.
        !           643:                         * Use mod_redirect instead of mod_alias to remap paths *under* this basedir.
        !           644:                         * Use mod_redirect instead of mod_rewrite on *any* parts of path to basedir.
        !           645:                         * (Related, use mod_auth to protect this basedir, but avoid attempting to
        !           646:                         *  use mod_auth on paths underneath this basedir, as target path is not
        !           647:                         *  validated with mod_auth)
        !           648:                         */
        !           649: 
        !           650:                        /* find matching URI prefix
        !           651:                         * check if remaining con->physical.rel_path matches suffix
        !           652:                         *   of con->physical.basedir so that we can use it to
        !           653:                         *   remap Destination physical path */
        !           654:                        {
        !           655:                                const char *sep, *sep2;
        !           656:                                sep = con->uri.path->ptr;
        !           657:                                sep2 = srv->tmp_buf->ptr;
        !           658:                                for (i = 0; sep[i] && sep[i] == sep2[i]; ++i) ;
        !           659:                                while (i != 0 && sep[--i] != '/') ; /* find matching directory path */
        !           660:                        }
        !           661:                        if (con->conf.force_lowercase_filenames) {
        !           662:                                buffer_to_lower(srv->tmp_buf);
        !           663:                        }
        !           664:                        remain = buffer_string_length(con->uri.path) - i;
        !           665:                        if (!con->conf.force_lowercase_filenames
        !           666:                            ? buffer_is_equal_right_len(con->physical.path, con->physical.rel_path, remain)
        !           667:                            :(buffer_string_length(con->physical.path) >= remain
        !           668:                              && 0 == strncasecmp(con->physical.path->ptr+buffer_string_length(con->physical.path)-remain, con->physical.rel_path->ptr+i, remain))) {
        !           669:                                buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, buffer_string_length(con->physical.path)-remain);
        !           670:                                buffer_append_string_len(p->stat_fn, srv->tmp_buf->ptr+i, buffer_string_length(srv->tmp_buf)-i);
        !           671:                        } else {
        !           672:                                /* unable to perform physical path remap here;
        !           673:                                 * assume doc_root/rel_path and no remapping */
        !           674:                                buffer_copy_buffer(p->stat_fn, con->physical.doc_root);
        !           675:                                buffer_append_string_buffer(p->stat_fn, srv->tmp_buf);
        !           676:                        }
1.1       misho     677:                }
                    678: 
1.1.1.3 ! misho     679:                if (0 == stat(p->stat_fn->ptr, &stb)) {
        !           680:                        time_t t = stb.st_mtime;
1.1       misho     681: 
                    682:                        switch (ssicmd) {
                    683:                        case SSI_FSIZE:
1.1.1.3 ! misho     684:                                b = buffer_init();
1.1       misho     685:                                if (p->sizefmt) {
                    686:                                        int j = 0;
                    687:                                        const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL };
                    688: 
1.1.1.3 ! misho     689:                                        off_t s = stb.st_size;
1.1       misho     690: 
                    691:                                        for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++);
                    692: 
1.1.1.3 ! misho     693:                                        buffer_copy_int(b, s);
1.1       misho     694:                                        buffer_append_string(b, abr[j]);
                    695:                                } else {
1.1.1.3 ! misho     696:                                        buffer_copy_int(b, stb.st_size);
1.1       misho     697:                                }
1.1.1.3 ! misho     698:                                chunkqueue_append_buffer(con->write_queue, b);
        !           699:                                buffer_free(b);
1.1       misho     700:                                break;
                    701:                        case SSI_FLASTMOD:
                    702:                                if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
1.1.1.3 ! misho     703:                                        chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
1.1       misho     704:                                } else {
1.1.1.3 ! misho     705:                                        chunkqueue_append_mem(con->write_queue, buf, strlen(buf));
1.1       misho     706:                                }
                    707:                                break;
                    708:                        case SSI_INCLUDE:
1.1.1.3 ! misho     709:                                chunkqueue_append_file(con->write_queue, p->stat_fn, 0, stb.st_size);
1.1       misho     710: 
                    711:                                /* Keep the newest mtime of included files */
1.1.1.3 ! misho     712:                                if (stb.st_mtime > include_file_last_mtime)
        !           713:                                        include_file_last_mtime = stb.st_mtime;
1.1       misho     714: 
                    715:                                break;
                    716:                        }
                    717:                } else {
                    718:                        log_error_write(srv, __FILE__, __LINE__, "sbs",
                    719:                                        "ssi: stating failed ",
                    720:                                        p->stat_fn, strerror(errno));
                    721:                }
                    722:                break;
                    723:        }
                    724:        case SSI_SET: {
                    725:                const char *key = NULL, *val = NULL;
                    726:                for (i = 2; i < n; i += 2) {
                    727:                        if (0 == strcmp(l[i], "var")) {
                    728:                                key = l[i+1];
                    729:                        } else if (0 == strcmp(l[i], "value")) {
                    730:                                val = l[i+1];
                    731:                        } else {
                    732:                                log_error_write(srv, __FILE__, __LINE__, "sss",
1.1.1.3 ! misho     733:                                                "ssi: unknown attribute for ",
1.1       misho     734:                                                l[1], l[i]);
                    735:                        }
                    736:                }
                    737: 
                    738:                if (p->if_is_false) break;
                    739: 
                    740:                if (key && val) {
                    741:                        data_string *ds;
                    742: 
                    743:                        if (NULL == (ds = (data_string *)array_get_unused_element(p->ssi_vars, TYPE_STRING))) {
                    744:                                ds = data_string_init();
                    745:                        }
                    746:                        buffer_copy_string(ds->key,   key);
                    747:                        buffer_copy_string(ds->value, val);
                    748: 
                    749:                        array_insert_unique(p->ssi_vars, (data_unset *)ds);
1.1.1.3 ! misho     750:                } else if (key || val) {
        !           751:                        log_error_write(srv, __FILE__, __LINE__, "sSSss",
        !           752:                                        "ssi: var and value have to be set in <!--#set", l[1], "=", l[2], "-->");
1.1       misho     753:                } else {
1.1.1.3 ! misho     754:                        log_error_write(srv, __FILE__, __LINE__, "s",
        !           755:                                        "ssi: var and value have to be set in <!--#set var=... value=... -->");
1.1       misho     756:                }
                    757:                break;
                    758:        }
                    759:        case SSI_CONFIG:
                    760:                if (p->if_is_false) break;
                    761: 
                    762:                for (i = 2; i < n; i += 2) {
                    763:                        if (0 == strcmp(l[i], "timefmt")) {
                    764:                                buffer_copy_string(p->timefmt, l[i+1]);
                    765:                        } else if (0 == strcmp(l[i], "sizefmt")) {
                    766:                                if (0 == strcmp(l[i+1], "abbrev")) {
                    767:                                        p->sizefmt = 1;
1.1.1.3 ! misho     768:                                } else if (0 == strcmp(l[i+1], "bytes")) {
1.1       misho     769:                                        p->sizefmt = 0;
                    770:                                } else {
                    771:                                        log_error_write(srv, __FILE__, __LINE__, "sssss",
1.1.1.3 ! misho     772:                                                        "ssi: unknown value for attribute '",
1.1       misho     773:                                                        l[i],
                    774:                                                        "' for ",
                    775:                                                        l[1], l[i+1]);
                    776:                                }
                    777:                        } else {
                    778:                                log_error_write(srv, __FILE__, __LINE__, "sss",
1.1.1.3 ! misho     779:                                                "ssi: unknown attribute for ",
1.1       misho     780:                                                l[1], l[i]);
                    781:                        }
                    782:                }
                    783:                break;
                    784:        case SSI_PRINTENV:
                    785:                if (p->if_is_false) break;
                    786: 
1.1.1.3 ! misho     787:                b = buffer_init();
1.1       misho     788:                for (i = 0; i < p->ssi_vars->used; i++) {
                    789:                        data_string *ds = (data_string *)p->ssi_vars->data[p->ssi_vars->sorted[i]];
                    790: 
                    791:                        buffer_append_string_buffer(b, ds->key);
                    792:                        buffer_append_string_len(b, CONST_STR_LEN("="));
                    793:                        buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML);
                    794:                        buffer_append_string_len(b, CONST_STR_LEN("\n"));
                    795:                }
                    796:                for (i = 0; i < p->ssi_cgi_env->used; i++) {
                    797:                        data_string *ds = (data_string *)p->ssi_cgi_env->data[p->ssi_cgi_env->sorted[i]];
                    798: 
                    799:                        buffer_append_string_buffer(b, ds->key);
                    800:                        buffer_append_string_len(b, CONST_STR_LEN("="));
                    801:                        buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML);
                    802:                        buffer_append_string_len(b, CONST_STR_LEN("\n"));
                    803:                }
1.1.1.3 ! misho     804:                chunkqueue_append_buffer(con->write_queue, b);
        !           805:                buffer_free(b);
1.1       misho     806: 
                    807:                break;
                    808:        case SSI_EXEC: {
                    809:                const char *cmd = NULL;
                    810:                pid_t pid;
                    811:                int from_exec_fds[2];
                    812: 
1.1.1.3 ! misho     813:                if (!p->conf.ssi_exec) { /* <!--#exec ... --> disabled by config */
        !           814:                        break;
        !           815:                }
        !           816: 
1.1       misho     817:                for (i = 2; i < n; i += 2) {
                    818:                        if (0 == strcmp(l[i], "cmd")) {
                    819:                                cmd = l[i+1];
                    820:                        } else {
                    821:                                log_error_write(srv, __FILE__, __LINE__, "sss",
1.1.1.3 ! misho     822:                                                "ssi: unknown attribute for ",
1.1       misho     823:                                                l[1], l[i]);
                    824:                        }
                    825:                }
                    826: 
                    827:                if (p->if_is_false) break;
                    828: 
                    829:                /* create a return pipe and send output to the html-page
                    830:                 *
                    831:                 * as exec is assumed evil it is implemented synchronously
                    832:                 */
                    833: 
                    834:                if (!cmd) break;
                    835: #ifdef HAVE_FORK
                    836:                if (pipe(from_exec_fds)) {
                    837:                        log_error_write(srv, __FILE__, __LINE__, "ss",
                    838:                                        "pipe failed: ", strerror(errno));
                    839:                        return -1;
                    840:                }
                    841: 
                    842:                /* fork, execve */
                    843:                switch (pid = fork()) {
                    844:                case 0: {
                    845:                        /* move stdout to from_rrdtool_fd[1] */
                    846:                        close(STDOUT_FILENO);
                    847:                        dup2(from_exec_fds[1], STDOUT_FILENO);
                    848:                        close(from_exec_fds[1]);
                    849:                        /* not needed */
                    850:                        close(from_exec_fds[0]);
                    851: 
                    852:                        /* close stdin */
                    853:                        close(STDIN_FILENO);
                    854: 
                    855:                        execl("/bin/sh", "sh", "-c", cmd, (char *)NULL);
                    856: 
                    857:                        log_error_write(srv, __FILE__, __LINE__, "sss", "spawing exec failed:", strerror(errno), cmd);
                    858: 
                    859:                        /* */
                    860:                        SEGFAULT();
                    861:                        break;
                    862:                }
                    863:                case -1:
                    864:                        /* error */
                    865:                        log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
                    866:                        break;
                    867:                default: {
                    868:                        /* father */
                    869:                        int status;
                    870:                        ssize_t r;
                    871:                        int was_interrupted = 0;
                    872: 
                    873:                        close(from_exec_fds[1]);
                    874: 
                    875:                        /* wait for the client to end */
                    876: 
                    877:                        /*
                    878:                         * OpenBSD and Solaris send a EINTR on SIGCHILD even if we ignore it
                    879:                         */
                    880:                        do {
                    881:                                if (-1 == waitpid(pid, &status, 0)) {
                    882:                                        if (errno == EINTR) {
                    883:                                                was_interrupted++;
                    884:                                        } else {
                    885:                                                was_interrupted = 0;
                    886:                                                log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno));
                    887:                                        }
                    888:                                } else if (WIFEXITED(status)) {
                    889:                                        int toread;
                    890:                                        /* read everything from client and paste it into the output */
                    891:                                        was_interrupted = 0;
1.1.1.3 ! misho     892: 
1.1       misho     893:                                        while(1) {
                    894:                                                if (ioctl(from_exec_fds[0], FIONREAD, &toread)) {
                    895:                                                        log_error_write(srv, __FILE__, __LINE__, "s",
                    896:                                                                "unexpected end-of-file (perhaps the ssi-exec process died)");
                    897:                                                        return -1;
                    898:                                                }
1.1.1.3 ! misho     899: 
1.1       misho     900:                                                if (toread > 0) {
1.1.1.3 ! misho     901:                                                        char *mem;
        !           902:                                                        size_t mem_len;
        !           903: 
        !           904:                                                        chunkqueue_get_memory(con->write_queue, &mem, &mem_len, 0, toread);
        !           905:                                                        r = read(from_exec_fds[0], mem, mem_len);
        !           906:                                                        chunkqueue_use_memory(con->write_queue, r > 0 ? r : 0);
        !           907: 
        !           908:                                                        if (r < 0) break; /* read failed */
1.1       misho     909:                                                } else {
                    910:                                                        break;
                    911:                                                }
                    912:                                        }
                    913:                                } else {
                    914:                                        was_interrupted = 0;
                    915:                                        log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally");
                    916:                                }
                    917:                        } while (was_interrupted > 0 && was_interrupted < 4); /* if waitpid() gets interrupted, retry, but max 4 times */
                    918: 
                    919:                        close(from_exec_fds[0]);
                    920: 
                    921:                        break;
                    922:                }
                    923:                }
                    924: #else
                    925: 
                    926:                return -1;
                    927: #endif
                    928: 
                    929:                break;
                    930:        }
                    931:        case SSI_IF: {
                    932:                const char *expr = NULL;
                    933: 
                    934:                for (i = 2; i < n; i += 2) {
                    935:                        if (0 == strcmp(l[i], "expr")) {
                    936:                                expr = l[i+1];
                    937:                        } else {
                    938:                                log_error_write(srv, __FILE__, __LINE__, "sss",
1.1.1.3 ! misho     939:                                                "ssi: unknown attribute for ",
1.1       misho     940:                                                l[1], l[i]);
                    941:                        }
                    942:                }
                    943: 
                    944:                if (!expr) {
                    945:                        log_error_write(srv, __FILE__, __LINE__, "sss",
                    946:                                        "ssi: ",
                    947:                                        l[1], "expr missing");
                    948:                        break;
                    949:                }
                    950: 
                    951:                if ((!p->if_is_false) &&
                    952:                    ((p->if_is_false_level == 0) ||
                    953:                     (p->if_level < p->if_is_false_level))) {
                    954:                        switch (ssi_eval_expr(srv, con, p, expr)) {
                    955:                        case -1:
                    956:                        case 0:
                    957:                                p->if_is_false = 1;
                    958:                                p->if_is_false_level = p->if_level;
                    959:                                break;
                    960:                        case 1:
                    961:                                p->if_is_false = 0;
                    962:                                break;
                    963:                        }
                    964:                }
                    965: 
                    966:                p->if_level++;
                    967: 
                    968:                break;
                    969:        }
                    970:        case SSI_ELSE:
                    971:                p->if_level--;
                    972: 
                    973:                if (p->if_is_false) {
                    974:                        if ((p->if_level == p->if_is_false_level) &&
                    975:                            (p->if_is_false_endif == 0)) {
                    976:                                p->if_is_false = 0;
                    977:                        }
                    978:                } else {
                    979:                        p->if_is_false = 1;
                    980: 
                    981:                        p->if_is_false_level = p->if_level;
                    982:                }
                    983:                p->if_level++;
                    984: 
                    985:                break;
                    986:        case SSI_ELIF: {
                    987:                const char *expr = NULL;
                    988:                for (i = 2; i < n; i += 2) {
                    989:                        if (0 == strcmp(l[i], "expr")) {
                    990:                                expr = l[i+1];
                    991:                        } else {
                    992:                                log_error_write(srv, __FILE__, __LINE__, "sss",
1.1.1.3 ! misho     993:                                                "ssi: unknown attribute for ",
1.1       misho     994:                                                l[1], l[i]);
                    995:                        }
                    996:                }
                    997: 
                    998:                if (!expr) {
                    999:                        log_error_write(srv, __FILE__, __LINE__, "sss",
                   1000:                                        "ssi: ",
                   1001:                                        l[1], "expr missing");
                   1002:                        break;
                   1003:                }
                   1004: 
                   1005:                p->if_level--;
                   1006: 
                   1007:                if (p->if_level == p->if_is_false_level) {
                   1008:                        if ((p->if_is_false) &&
                   1009:                            (p->if_is_false_endif == 0)) {
                   1010:                                switch (ssi_eval_expr(srv, con, p, expr)) {
                   1011:                                case -1:
                   1012:                                case 0:
                   1013:                                        p->if_is_false = 1;
                   1014:                                        p->if_is_false_level = p->if_level;
                   1015:                                        break;
                   1016:                                case 1:
                   1017:                                        p->if_is_false = 0;
                   1018:                                        break;
                   1019:                                }
                   1020:                        } else {
                   1021:                                p->if_is_false = 1;
                   1022:                                p->if_is_false_level = p->if_level;
                   1023:                                p->if_is_false_endif = 1;
                   1024:                        }
                   1025:                }
                   1026: 
                   1027:                p->if_level++;
                   1028: 
                   1029:                break;
                   1030:        }
                   1031:        case SSI_ENDIF:
                   1032:                p->if_level--;
                   1033: 
                   1034:                if (p->if_level == p->if_is_false_level) {
                   1035:                        p->if_is_false = 0;
                   1036:                        p->if_is_false_endif = 0;
                   1037:                }
                   1038: 
                   1039:                break;
                   1040:        default:
                   1041:                log_error_write(srv, __FILE__, __LINE__, "ss",
1.1.1.3 ! misho    1042:                                "ssi: unknown ssi-command:",
1.1       misho    1043:                                l[1]);
                   1044:                break;
                   1045:        }
                   1046: 
                   1047:        return 0;
                   1048: 
                   1049: }
                   1050: 
1.1.1.3 ! misho    1051: static int mod_ssi_parse_ssi_stmt_value(const char * const s, const int len) {
        !          1052:        int n;
        !          1053:        const int c = (s[0] == '"' ? '"' : s[0] == '\'' ? '\'' : 0);
        !          1054:        if (0 != c) {
        !          1055:                for (n = 1; n < len; ++n) {
        !          1056:                        if (s[n] == c) return n+1;
        !          1057:                        if (s[n] == '\\') {
        !          1058:                                if (n+1 == len) return 0; /* invalid */
        !          1059:                                ++n;
        !          1060:                        }
        !          1061:                }
        !          1062:                return 0; /* invalid */
        !          1063:        } else {
        !          1064:                for (n = 0; n < len; ++n) {
        !          1065:                        if (isspace(s[n])) return n;
        !          1066:                        if (s[n] == '\\') {
        !          1067:                                if (n+1 == len) return 0; /* invalid */
        !          1068:                                ++n;
        !          1069:                        }
        !          1070:                }
        !          1071:                return n;
        !          1072:        }
        !          1073: }
1.1       misho    1074: 
1.1.1.3 ! misho    1075: static int mod_ssi_parse_ssi_stmt_offlen(int o[10], const char * const s, const int len) {
        !          1076: 
        !          1077:        /**
        !          1078:         * <!--#element attribute=value attribute=value ... -->
        !          1079:         */
1.1       misho    1080: 
1.1.1.3 ! misho    1081:        /* s must begin "<!--#" and must end with "-->" */
        !          1082:        int n = 5;
        !          1083:        o[0] = n;
        !          1084:        for (; light_isalpha(s[n]); ++n) ; /*(n = 5 to begin after "<!--#")*/
        !          1085:        o[1] = n - o[0];
        !          1086:        if (0 == o[1]) return -1; /* empty token */
        !          1087: 
        !          1088:        if (n+3 == len) return 2; /* token only; no params */
        !          1089:        if (!isspace(s[n])) return -1;
        !          1090:        do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */
        !          1091:        if (n+3 == len) return 2; /* token only; no params */
        !          1092: 
        !          1093:        o[2] = n;
        !          1094:        for (; light_isalpha(s[n]); ++n) ;
        !          1095:        o[3] = n - o[2];
        !          1096:        if (0 == o[3] || s[n++] != '=') return -1;
        !          1097: 
        !          1098:        o[4] = n;
        !          1099:        o[5] = mod_ssi_parse_ssi_stmt_value(s+n, len-n-3);
        !          1100:        if (0 == o[5]) return -1; /* empty or invalid token */
        !          1101:        n += o[5];
        !          1102: 
        !          1103:        if (n+3 == len) return 6; /* token and one param */
        !          1104:        if (!isspace(s[n])) return -1;
        !          1105:        do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */
        !          1106:        if (n+3 == len) return 6; /* token and one param */
        !          1107: 
        !          1108:        o[6] = n;
        !          1109:        for (; light_isalpha(s[n]); ++n) ;
        !          1110:        o[7] = n - o[6];
        !          1111:        if (0 == o[7] || s[n++] != '=') return -1;
        !          1112: 
        !          1113:        o[8] = n;
        !          1114:        o[9] = mod_ssi_parse_ssi_stmt_value(s+n, len-n-3);
        !          1115:        if (0 == o[9]) return -1; /* empty or invalid token */
        !          1116:        n += o[9];
        !          1117: 
        !          1118:        if (n+3 == len) return 10; /* token and two params */
        !          1119:        if (!isspace(s[n])) return -1;
        !          1120:        do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */
        !          1121:        if (n+3 == len) return 10; /* token and two params */
        !          1122:        return -1;
        !          1123: }
1.1.1.2   misho    1124: 
1.1.1.3 ! misho    1125: static void mod_ssi_parse_ssi_stmt(server *srv, connection *con, plugin_data *p, char *s, int len, struct stat *st) {
        !          1126: 
        !          1127:        /**
        !          1128:         * <!--#element attribute=value attribute=value ... -->
        !          1129:         */
        !          1130: 
        !          1131:        int o[10];
        !          1132:        int m;
        !          1133:        const int n = mod_ssi_parse_ssi_stmt_offlen(o, s, len);
        !          1134:        char *l[6] = { s, NULL, NULL, NULL, NULL, NULL };
        !          1135:        if (-1 == n) {
        !          1136:                /* XXX: perhaps emit error comment instead of invalid <!--#...--> code to client */
        !          1137:                chunkqueue_append_mem(con->write_queue, s, len); /* append stmt as-is */
        !          1138:                return;
        !          1139:        }
        !          1140: 
        !          1141:       #if 0
        !          1142:        /* dup s and then modify s */
        !          1143:        /*(l[0] is no longer used; was previously used in only one place for error reporting)*/
        !          1144:        l[0] = malloc((size_t)(len+1));
        !          1145:        memcpy(l[0], s, (size_t)len);
        !          1146:        (l[0])[len] = '\0';
        !          1147:       #endif
        !          1148: 
        !          1149:        /* modify s in-place to split string into arg tokens */
        !          1150:        for (m = 0; m < n; m += 2) {
        !          1151:                char *ptr = s+o[m];
        !          1152:                switch (*ptr) {
        !          1153:                case '"':
        !          1154:                case '\'': (++ptr)[o[m+1]-2] = '\0'; break;
        !          1155:                default:       ptr[o[m+1]] = '\0';   break;
        !          1156:                }
        !          1157:                l[1+(m>>1)] = ptr;
        !          1158:                if (m == 4 || m == 8) {
        !          1159:                        /* XXX: removing '\\' escapes from param value would be
        !          1160:                         * the right thing to do, but would potentially change
        !          1161:                         * current behavior, e.g. <!--#exec cmd=... --> */
        !          1162:                }
        !          1163:        }
        !          1164: 
        !          1165:        process_ssi_stmt(srv, con, p, (const char **)l, 1+(n>>1), st);
        !          1166: 
        !          1167:       #if 0
        !          1168:        free(l[0]);
        !          1169:       #endif
        !          1170: }
        !          1171: 
        !          1172: static int mod_ssi_stmt_len(const char *s, const int len) {
        !          1173:        /* s must begin "<!--#" */
        !          1174:        int n, sq = 0, dq = 0, bs = 0;
        !          1175:        for (n = 5; n < len; ++n) { /*(n = 5 to begin after "<!--#")*/
        !          1176:                switch (s[n]) {
        !          1177:                default:
        !          1178:                        break;
        !          1179:                case '-':
        !          1180:                        if (!sq && !dq && n+2 < len && s[n+1] == '-' && s[n+2] == '>') return n+3; /* found end of stmt */
        !          1181:                        break;
        !          1182:                case '"':
        !          1183:                        if (!sq && (!dq || !bs)) dq = !dq;
        !          1184:                        break;
        !          1185:                case '\'':
        !          1186:                        if (!dq && (!sq || !bs)) sq = !sq;
        !          1187:                        break;
        !          1188:                case '\\':
        !          1189:                        if (sq || dq) bs = !bs;
        !          1190:                        break;
        !          1191:                }
        !          1192:        }
        !          1193:        return 0; /* incomplete directive "<!--#...-->" */
        !          1194: }
        !          1195: 
        !          1196: static void mod_ssi_read_fd(server *srv, connection *con, plugin_data *p, int fd, struct stat *st) {
        !          1197:        ssize_t rd;
        !          1198:        size_t offset, pretag;
        !          1199:        char buf[8192];
        !          1200: 
        !          1201:        offset = 0;
        !          1202:        pretag = 0;
        !          1203:        while (0 < (rd = read(fd, buf+offset, sizeof(buf)-offset))) {
        !          1204:                char *s;
        !          1205:                size_t prelen = 0, len;
        !          1206:                offset += (size_t)rd;
        !          1207:                for (; (s = memchr(buf+prelen, '<', offset-prelen)); ++prelen) {
        !          1208:                        prelen = s - buf;
        !          1209:                        if (prelen + 5 <= offset) { /*("<!--#" is 5 chars)*/
        !          1210:                                if (0 != memcmp(s+1, CONST_STR_LEN("!--#"))) continue; /* loop to loop for next '<' */
        !          1211: 
        !          1212:                                if (prelen - pretag && !p->if_is_false) {
        !          1213:                                        chunkqueue_append_mem(con->write_queue, buf+pretag, prelen-pretag);
        !          1214:                                }
        !          1215: 
        !          1216:                                len = mod_ssi_stmt_len(buf+prelen, offset-prelen);
        !          1217:                                if (len) { /* num of chars to be consumed */
        !          1218:                                        mod_ssi_parse_ssi_stmt(srv, con, p, buf+prelen, len, st);
        !          1219:                                        prelen += (len - 1); /* offset to '>' at end of SSI directive; incremented at top of loop */
        !          1220:                                        pretag = prelen + 1;
        !          1221:                                        if (pretag == offset) {
        !          1222:                                                offset = pretag = 0;
        !          1223:                                                break;
        !          1224:                                        }
        !          1225:                                } else if (0 == prelen && offset == sizeof(buf)) { /*(full buf)*/
        !          1226:                                        /* SSI statement is way too long
        !          1227:                                         * NOTE: skipping this buf will expose *the rest* of this SSI statement */
        !          1228:                                        chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("<!-- [an error occurred: directive too long] "));
        !          1229:                                        /* check if buf ends with "-" or "--" which might be part of "-->"
        !          1230:                                         * (buf contains at least 5 chars for "<!--#") */
        !          1231:                                        if (buf[offset-2] == '-' && buf[offset-1] == '-') {
        !          1232:                                                chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("--"));
        !          1233:                                        } else if (buf[offset-1] == '-') {
        !          1234:                                                chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("-"));
        !          1235:                                        }
        !          1236:                                        offset = pretag = 0;
        !          1237:                                        break;
        !          1238:                                } else { /* incomplete directive "<!--#...-->" */
        !          1239:                                        memmove(buf, buf+prelen, (offset -= prelen));
        !          1240:                                        pretag = 0;
        !          1241:                                        break;
        !          1242:                                }
        !          1243:                        } else if (prelen + 1 == offset || 0 == memcmp(s+1, "!--", offset - prelen - 1)) {
        !          1244:                                if (prelen - pretag && !p->if_is_false) {
        !          1245:                                        chunkqueue_append_mem(con->write_queue, buf+pretag, prelen-pretag);
        !          1246:                                }
        !          1247:                                memcpy(buf, buf+prelen, (offset -= prelen));
        !          1248:                                pretag = 0;
        !          1249:                                break;
        !          1250:                        }
        !          1251:                        /* loop to look for next '<' */
        !          1252:                }
        !          1253:                if (offset == sizeof(buf)) {
        !          1254:                        if (!p->if_is_false) {
        !          1255:                                chunkqueue_append_mem(con->write_queue, buf+pretag, offset-pretag);
        !          1256:                        }
        !          1257:                        offset = pretag = 0;
        !          1258:                }
        !          1259:        }
        !          1260: 
        !          1261:        if (0 != rd) {
        !          1262:                log_error_write(srv, __FILE__, __LINE__,  "SsB", "read(): ", strerror(errno), con->physical.path);
        !          1263:        }
        !          1264: 
        !          1265:        if (offset - pretag) {
        !          1266:                /* copy remaining data in buf */
        !          1267:                if (!p->if_is_false) {
        !          1268:                        chunkqueue_append_mem(con->write_queue, buf+pretag, offset-pretag);
        !          1269:                }
        !          1270:        }
        !          1271: }
        !          1272: 
        !          1273: 
        !          1274: /* don't want to block when open()ing a fifo */
        !          1275: #if defined(O_NONBLOCK)
        !          1276: # define FIFO_NONBLOCK O_NONBLOCK
        !          1277: #else
        !          1278: # define FIFO_NONBLOCK 0
        !          1279: #endif
        !          1280: 
        !          1281: static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) {
        !          1282:        int fd;
        !          1283:        struct stat st;
1.1.1.2   misho    1284: 
1.1       misho    1285:        /* get a stream to the file */
                   1286: 
                   1287:        array_reset(p->ssi_vars);
                   1288:        array_reset(p->ssi_cgi_env);
                   1289:        buffer_copy_string_len(p->timefmt, CONST_STR_LEN("%a, %d %b %Y %H:%M:%S %Z"));
                   1290:        p->sizefmt = 0;
                   1291:        build_ssi_cgi_vars(srv, con, p);
                   1292:        p->if_is_false = 0;
                   1293: 
                   1294:        /* Reset the modified time of included files */
                   1295:        include_file_last_mtime = 0;
                   1296: 
1.1.1.3 ! misho    1297:        if (-1 == (fd = open(con->physical.path->ptr, O_RDONLY | FIFO_NONBLOCK))) {
1.1       misho    1298:                log_error_write(srv, __FILE__, __LINE__, "sb",
1.1.1.3 ! misho    1299:                                "open: ", con->physical.path);
1.1       misho    1300:                return -1;
                   1301:        }
                   1302: 
1.1.1.3 ! misho    1303:        if (0 != fstat(fd, &st)) {
        !          1304:                log_error_write(srv, __FILE__, __LINE__,  "SB", "fstat failed: ", con->physical.path);
        !          1305:                close(fd);
        !          1306:                return -1;
1.1       misho    1307:        }
                   1308: 
1.1.1.3 ! misho    1309:        mod_ssi_read_fd(srv, con, p, fd, &st);
1.1       misho    1310: 
1.1.1.3 ! misho    1311:        close(fd);
1.1       misho    1312:        con->file_started  = 1;
                   1313:        con->file_finished = 1;
                   1314:        con->mode = p->id;
                   1315: 
1.1.1.3 ! misho    1316:        if (buffer_string_is_empty(p->conf.content_type)) {
1.1       misho    1317:                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
                   1318:        } else {
                   1319:                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type));
                   1320:        }
                   1321: 
1.1.1.3 ! misho    1322:        if (p->conf.conditional_requests) {
1.1.1.2   misho    1323:                /* Generate "ETag" & "Last-Modified" headers */
1.1       misho    1324:                buffer *mtime = NULL;
                   1325: 
1.1.1.3 ! misho    1326:                /* use most recently modified include file for ETag and Last-Modified */
        !          1327:                if (st.st_mtime < include_file_last_mtime)
        !          1328:                        st.st_mtime = include_file_last_mtime;
1.1       misho    1329: 
1.1.1.3 ! misho    1330:                etag_create(con->physical.etag, &st, con->etag_flags);
        !          1331:                response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
1.1       misho    1332: 
1.1.1.3 ! misho    1333:                mtime = strftime_cache_get(srv, st.st_mtime);
1.1       misho    1334:                response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
1.1.1.3 ! misho    1335: 
        !          1336:                if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
        !          1337:                        /* ok, the client already has our content,
        !          1338:                         * no need to send it again */
        !          1339: 
        !          1340:                        chunkqueue_reset(con->write_queue);
        !          1341:                }
1.1       misho    1342:        }
                   1343: 
                   1344:        /* Reset the modified time of included files */
                   1345:        include_file_last_mtime = 0;
                   1346: 
                   1347:        /* reset physical.path */
                   1348:        buffer_reset(con->physical.path);
                   1349: 
                   1350:        return 0;
                   1351: }
                   1352: 
                   1353: #define PATCH(x) \
                   1354:        p->conf.x = s->x;
                   1355: static int mod_ssi_patch_connection(server *srv, connection *con, plugin_data *p) {
                   1356:        size_t i, j;
                   1357:        plugin_config *s = p->config_storage[0];
                   1358: 
                   1359:        PATCH(ssi_extension);
                   1360:        PATCH(content_type);
1.1.1.3 ! misho    1361:        PATCH(conditional_requests);
        !          1362:        PATCH(ssi_exec);
1.1       misho    1363: 
                   1364:        /* skip the first, the global context */
                   1365:        for (i = 1; i < srv->config_context->used; i++) {
                   1366:                data_config *dc = (data_config *)srv->config_context->data[i];
                   1367:                s = p->config_storage[i];
                   1368: 
                   1369:                /* condition didn't match */
                   1370:                if (!config_check_cond(srv, con, dc)) continue;
                   1371: 
                   1372:                /* merge config */
                   1373:                for (j = 0; j < dc->value->used; j++) {
                   1374:                        data_unset *du = dc->value->data[j];
                   1375: 
                   1376:                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.extension"))) {
                   1377:                                PATCH(ssi_extension);
                   1378:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.content-type"))) {
                   1379:                                PATCH(content_type);
1.1.1.3 ! misho    1380:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.conditional-requests"))) {
        !          1381:                                PATCH(conditional_requests);
        !          1382:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.exec"))) {
        !          1383:                                PATCH(ssi_exec);
1.1       misho    1384:                        }
                   1385:                }
                   1386:        }
                   1387: 
                   1388:        return 0;
                   1389: }
                   1390: #undef PATCH
                   1391: 
                   1392: URIHANDLER_FUNC(mod_ssi_physical_path) {
                   1393:        plugin_data *p = p_d;
                   1394:        size_t k;
                   1395: 
                   1396:        if (con->mode != DIRECT) return HANDLER_GO_ON;
                   1397: 
1.1.1.3 ! misho    1398:        if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
1.1       misho    1399: 
                   1400:        mod_ssi_patch_connection(srv, con, p);
                   1401: 
                   1402:        for (k = 0; k < p->conf.ssi_extension->used; k++) {
                   1403:                data_string *ds = (data_string *)p->conf.ssi_extension->data[k];
                   1404: 
1.1.1.3 ! misho    1405:                if (buffer_is_empty(ds->value)) continue;
1.1       misho    1406: 
1.1.1.3 ! misho    1407:                if (buffer_is_equal_right_len(con->physical.path, ds->value, buffer_string_length(ds->value))) {
1.1       misho    1408:                        /* handle ssi-request */
                   1409: 
                   1410:                        if (mod_ssi_handle_request(srv, con, p)) {
                   1411:                                /* on error */
                   1412:                                con->http_status = 500;
                   1413:                                con->mode = DIRECT;
                   1414:                        }
                   1415: 
                   1416:                        return HANDLER_FINISHED;
                   1417:                }
                   1418:        }
                   1419: 
                   1420:        /* not found */
                   1421:        return HANDLER_GO_ON;
                   1422: }
                   1423: 
                   1424: /* this function is called at dlopen() time and inits the callbacks */
                   1425: 
                   1426: int mod_ssi_plugin_init(plugin *p);
                   1427: int mod_ssi_plugin_init(plugin *p) {
                   1428:        p->version     = LIGHTTPD_VERSION_ID;
                   1429:        p->name        = buffer_init_string("ssi");
                   1430: 
                   1431:        p->init        = mod_ssi_init;
                   1432:        p->handle_subrequest_start = mod_ssi_physical_path;
                   1433:        p->set_defaults  = mod_ssi_set_defaults;
                   1434:        p->cleanup     = mod_ssi_free;
                   1435: 
                   1436:        p->data        = NULL;
                   1437: 
                   1438:        return 0;
                   1439: }

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