Annotation of embedaddon/nginx/src/http/modules/ngx_http_ssi_filter_module.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (C) Igor Sysoev
                      4:  * Copyright (C) Nginx, Inc.
                      5:  */
                      6: 
                      7: 
                      8: #include <ngx_config.h>
                      9: #include <ngx_core.h>
                     10: #include <ngx_http.h>
                     11: 
                     12: #define NGX_HTTP_SSI_ERROR          1
                     13: 
                     14: #define NGX_HTTP_SSI_DATE_LEN       2048
                     15: 
                     16: #define NGX_HTTP_SSI_ADD_PREFIX     1
                     17: #define NGX_HTTP_SSI_ADD_ZERO       2
                     18: 
                     19: 
                     20: typedef struct {
                     21:     ngx_flag_t    enable;
                     22:     ngx_flag_t    silent_errors;
                     23:     ngx_flag_t    ignore_recycled_buffers;
                     24: 
                     25:     ngx_hash_t    types;
                     26: 
                     27:     size_t        min_file_chunk;
                     28:     size_t        value_len;
                     29: 
                     30:     ngx_array_t  *types_keys;
                     31: } ngx_http_ssi_loc_conf_t;
                     32: 
                     33: 
                     34: typedef struct {
                     35:     ngx_str_t     name;
                     36:     ngx_uint_t    key;
                     37:     ngx_str_t     value;
                     38: } ngx_http_ssi_var_t;
                     39: 
                     40: 
                     41: typedef struct {
                     42:     ngx_str_t     name;
                     43:     ngx_chain_t  *bufs;
                     44:     ngx_uint_t    count;
                     45: } ngx_http_ssi_block_t;
                     46: 
                     47: 
                     48: typedef enum {
                     49:     ssi_start_state = 0,
                     50:     ssi_tag_state,
                     51:     ssi_comment0_state,
                     52:     ssi_comment1_state,
                     53:     ssi_sharp_state,
                     54:     ssi_precommand_state,
                     55:     ssi_command_state,
                     56:     ssi_preparam_state,
                     57:     ssi_param_state,
                     58:     ssi_preequal_state,
                     59:     ssi_prevalue_state,
                     60:     ssi_double_quoted_value_state,
                     61:     ssi_quoted_value_state,
                     62:     ssi_quoted_symbol_state,
                     63:     ssi_postparam_state,
                     64:     ssi_comment_end0_state,
                     65:     ssi_comment_end1_state,
                     66:     ssi_error_state,
                     67:     ssi_error_end0_state,
                     68:     ssi_error_end1_state
                     69: } ngx_http_ssi_state_e;
                     70: 
                     71: 
                     72: static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,
                     73:     ngx_http_ssi_ctx_t *ctx);
                     74: static void ngx_http_ssi_buffered(ngx_http_request_t *r,
                     75:     ngx_http_ssi_ctx_t *ctx);
                     76: static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
                     77:     ngx_http_ssi_ctx_t *ctx);
                     78: static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,
                     79:     ngx_str_t *name, ngx_uint_t key);
                     80: static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,
                     81:     ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);
                     82: static ngx_int_t ngx_http_ssi_regex_match(ngx_http_request_t *r,
                     83:     ngx_str_t *pattern, ngx_str_t *str);
                     84: 
                     85: static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
                     86:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                     87: static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,
                     88:     ngx_int_t rc);
                     89: static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,
                     90:     ngx_int_t rc);
                     91: static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
                     92:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                     93: static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
                     94:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                     95: static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,
                     96:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                     97: static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
                     98:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                     99: static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,
                    100:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                    101: static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
                    102:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                    103: static ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,
                    104:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                    105: static ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,
                    106:     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
                    107: 
                    108: static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
                    109:     ngx_http_variable_value_t *v, uintptr_t gmt);
                    110: 
                    111: static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);
                    112: static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);
                    113: static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);
                    114: static void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);
                    115: static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,
                    116:     void *parent, void *child);
                    117: static ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);
                    118: 
                    119: 
                    120: static ngx_command_t  ngx_http_ssi_filter_commands[] = {
                    121: 
                    122:     { ngx_string("ssi"),
                    123:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                    124:                         |NGX_CONF_FLAG,
                    125:       ngx_conf_set_flag_slot,
                    126:       NGX_HTTP_LOC_CONF_OFFSET,
                    127:       offsetof(ngx_http_ssi_loc_conf_t, enable),
                    128:       NULL },
                    129: 
                    130:     { ngx_string("ssi_silent_errors"),
                    131:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
                    132:       ngx_conf_set_flag_slot,
                    133:       NGX_HTTP_LOC_CONF_OFFSET,
                    134:       offsetof(ngx_http_ssi_loc_conf_t, silent_errors),
                    135:       NULL },
                    136: 
                    137:     { ngx_string("ssi_ignore_recycled_buffers"),
                    138:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
                    139:       ngx_conf_set_flag_slot,
                    140:       NGX_HTTP_LOC_CONF_OFFSET,
                    141:       offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),
                    142:       NULL },
                    143: 
                    144:     { ngx_string("ssi_min_file_chunk"),
                    145:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
                    146:       ngx_conf_set_size_slot,
                    147:       NGX_HTTP_LOC_CONF_OFFSET,
                    148:       offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),
                    149:       NULL },
                    150: 
                    151:     { ngx_string("ssi_value_length"),
                    152:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
                    153:       ngx_conf_set_size_slot,
                    154:       NGX_HTTP_LOC_CONF_OFFSET,
                    155:       offsetof(ngx_http_ssi_loc_conf_t, value_len),
                    156:       NULL },
                    157: 
                    158:     { ngx_string("ssi_types"),
                    159:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
                    160:       ngx_http_types_slot,
                    161:       NGX_HTTP_LOC_CONF_OFFSET,
                    162:       offsetof(ngx_http_ssi_loc_conf_t, types_keys),
                    163:       &ngx_http_html_default_types[0] },
                    164: 
                    165:       ngx_null_command
                    166: };
                    167: 
                    168: 
                    169: 
                    170: static ngx_http_module_t  ngx_http_ssi_filter_module_ctx = {
                    171:     ngx_http_ssi_preconfiguration,         /* preconfiguration */
                    172:     ngx_http_ssi_filter_init,              /* postconfiguration */
                    173: 
                    174:     ngx_http_ssi_create_main_conf,         /* create main configuration */
                    175:     ngx_http_ssi_init_main_conf,           /* init main configuration */
                    176: 
                    177:     NULL,                                  /* create server configuration */
                    178:     NULL,                                  /* merge server configuration */
                    179: 
                    180:     ngx_http_ssi_create_loc_conf,          /* create location configuration */
                    181:     ngx_http_ssi_merge_loc_conf            /* merge location configuration */
                    182: };
                    183: 
                    184: 
                    185: ngx_module_t  ngx_http_ssi_filter_module = {
                    186:     NGX_MODULE_V1,
                    187:     &ngx_http_ssi_filter_module_ctx,       /* module context */
                    188:     ngx_http_ssi_filter_commands,          /* module directives */
                    189:     NGX_HTTP_MODULE,                       /* module type */
                    190:     NULL,                                  /* init master */
                    191:     NULL,                                  /* init module */
                    192:     NULL,                                  /* init process */
                    193:     NULL,                                  /* init thread */
                    194:     NULL,                                  /* exit thread */
                    195:     NULL,                                  /* exit process */
                    196:     NULL,                                  /* exit master */
                    197:     NGX_MODULE_V1_PADDING
                    198: };
                    199: 
                    200: 
                    201: static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
                    202: static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
                    203: 
                    204: 
                    205: static u_char ngx_http_ssi_string[] = "<!--";
                    206: 
                    207: static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
                    208: static ngx_str_t ngx_http_ssi_null_string = ngx_null_string;
                    209: 
                    210: 
                    211: #define  NGX_HTTP_SSI_INCLUDE_VIRTUAL  0
                    212: #define  NGX_HTTP_SSI_INCLUDE_FILE     1
                    213: #define  NGX_HTTP_SSI_INCLUDE_WAIT     2
                    214: #define  NGX_HTTP_SSI_INCLUDE_SET      3
                    215: #define  NGX_HTTP_SSI_INCLUDE_STUB     4
                    216: 
                    217: #define  NGX_HTTP_SSI_ECHO_VAR         0
                    218: #define  NGX_HTTP_SSI_ECHO_DEFAULT     1
                    219: #define  NGX_HTTP_SSI_ECHO_ENCODING    2
                    220: 
                    221: #define  NGX_HTTP_SSI_CONFIG_ERRMSG    0
                    222: #define  NGX_HTTP_SSI_CONFIG_TIMEFMT   1
                    223: 
                    224: #define  NGX_HTTP_SSI_SET_VAR          0
                    225: #define  NGX_HTTP_SSI_SET_VALUE        1
                    226: 
                    227: #define  NGX_HTTP_SSI_IF_EXPR          0
                    228: 
                    229: #define  NGX_HTTP_SSI_BLOCK_NAME       0
                    230: 
                    231: 
                    232: static ngx_http_ssi_param_t  ngx_http_ssi_include_params[] = {
                    233:     { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },
                    234:     { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },
                    235:     { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT, 0, 0 },
                    236:     { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET, 0, 0 },
                    237:     { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB, 0, 0 },
                    238:     { ngx_null_string, 0, 0, 0 }
                    239: };
                    240: 
                    241: 
                    242: static ngx_http_ssi_param_t  ngx_http_ssi_echo_params[] = {
                    243:     { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },
                    244:     { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },
                    245:     { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 },
                    246:     { ngx_null_string, 0, 0, 0 }
                    247: };
                    248: 
                    249: 
                    250: static ngx_http_ssi_param_t  ngx_http_ssi_config_params[] = {
                    251:     { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0, 0 },
                    252:     { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0, 0 },
                    253:     { ngx_null_string, 0, 0, 0 }
                    254: };
                    255: 
                    256: 
                    257: static ngx_http_ssi_param_t  ngx_http_ssi_set_params[] = {
                    258:     { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1, 0 },
                    259:     { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1, 0 },
                    260:     { ngx_null_string, 0, 0, 0 }
                    261: };
                    262: 
                    263: 
                    264: static ngx_http_ssi_param_t  ngx_http_ssi_if_params[] = {
                    265:     { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1, 0 },
                    266:     { ngx_null_string, 0, 0, 0 }
                    267: };
                    268: 
                    269: 
                    270: static ngx_http_ssi_param_t  ngx_http_ssi_block_params[] = {
                    271:     { ngx_string("name"), NGX_HTTP_SSI_BLOCK_NAME, 1, 0 },
                    272:     { ngx_null_string, 0, 0, 0 }
                    273: };
                    274: 
                    275: 
                    276: static ngx_http_ssi_param_t  ngx_http_ssi_no_params[] = {
                    277:     { ngx_null_string, 0, 0, 0 }
                    278: };
                    279: 
                    280: 
                    281: static ngx_http_ssi_command_t  ngx_http_ssi_commands[] = {
                    282:     { ngx_string("include"), ngx_http_ssi_include,
                    283:                        ngx_http_ssi_include_params, 0, 0, 1 },
                    284:     { ngx_string("echo"), ngx_http_ssi_echo,
                    285:                        ngx_http_ssi_echo_params, 0, 0, 0 },
                    286:     { ngx_string("config"), ngx_http_ssi_config,
                    287:                        ngx_http_ssi_config_params, 0, 0, 0 },
                    288:     { ngx_string("set"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0, 0 },
                    289: 
                    290:     { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0, 0 },
                    291:     { ngx_string("elif"), ngx_http_ssi_if, ngx_http_ssi_if_params,
                    292:                        NGX_HTTP_SSI_COND_IF, 0, 0 },
                    293:     { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params,
                    294:                        NGX_HTTP_SSI_COND_IF, 0, 0 },
                    295:     { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params,
                    296:                        NGX_HTTP_SSI_COND_ELSE, 0, 0 },
                    297: 
                    298:     { ngx_string("block"), ngx_http_ssi_block,
                    299:                        ngx_http_ssi_block_params, 0, 0, 0 },
                    300:     { ngx_string("endblock"), ngx_http_ssi_endblock,
                    301:                        ngx_http_ssi_no_params, 0, 1, 0 },
                    302: 
                    303:     { ngx_null_string, NULL, NULL, 0, 0, 0 }
                    304: };
                    305: 
                    306: 
                    307: static ngx_http_variable_t  ngx_http_ssi_vars[] = {
                    308: 
                    309:     { ngx_string("date_local"), NULL, ngx_http_ssi_date_gmt_local_variable, 0,
                    310:       NGX_HTTP_VAR_NOCACHEABLE, 0 },
                    311: 
                    312:     { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,
                    313:       NGX_HTTP_VAR_NOCACHEABLE, 0 },
                    314: 
                    315:     { ngx_null_string, NULL, NULL, 0, 0, 0 }
                    316: };
                    317: 
                    318: 
                    319: 
                    320: static ngx_int_t
                    321: ngx_http_ssi_header_filter(ngx_http_request_t *r)
                    322: {
                    323:     ngx_http_ssi_ctx_t       *ctx;
                    324:     ngx_http_ssi_loc_conf_t  *slcf;
                    325: 
                    326:     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
                    327: 
                    328:     if (!slcf->enable
                    329:         || r->headers_out.content_length_n == 0
                    330:         || ngx_http_test_content_type(r, &slcf->types) == NULL)
                    331:     {
                    332:         return ngx_http_next_header_filter(r);
                    333:     }
                    334: 
                    335:     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));
                    336:     if (ctx == NULL) {
                    337:         return NGX_ERROR;
                    338:     }
                    339: 
                    340:     ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);
                    341: 
                    342: 
                    343:     ctx->value_len = slcf->value_len;
                    344:     ctx->last_out = &ctx->out;
                    345: 
                    346:     ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
                    347:     ctx->output = 1;
                    348: 
                    349:     ctx->params.elts = ctx->params_array;
                    350:     ctx->params.size = sizeof(ngx_table_elt_t);
                    351:     ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;
                    352:     ctx->params.pool = r->pool;
                    353: 
                    354:     ngx_str_set(&ctx->timefmt, "%A, %d-%b-%Y %H:%M:%S %Z");
                    355:     ngx_str_set(&ctx->errmsg,
                    356:                 "[an error occurred while processing the directive]");
                    357: 
                    358:     r->filter_need_in_memory = 1;
                    359: 
                    360:     if (r == r->main) {
                    361:         ngx_http_clear_content_length(r);
                    362:         ngx_http_clear_last_modified(r);
                    363:         ngx_http_clear_accept_ranges(r);
                    364:         ngx_http_clear_etag(r);
                    365:     }
                    366: 
                    367:     return ngx_http_next_header_filter(r);
                    368: }
                    369: 
                    370: 
                    371: static ngx_int_t
                    372: ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
                    373: {
                    374:     size_t                     len;
                    375:     ngx_int_t                  rc;
                    376:     ngx_buf_t                 *b;
                    377:     ngx_uint_t                 i, index;
                    378:     ngx_chain_t               *cl, **ll;
                    379:     ngx_table_elt_t           *param;
                    380:     ngx_http_ssi_ctx_t        *ctx, *mctx;
                    381:     ngx_http_ssi_block_t      *bl;
                    382:     ngx_http_ssi_param_t      *prm;
                    383:     ngx_http_ssi_command_t    *cmd;
                    384:     ngx_http_ssi_loc_conf_t   *slcf;
                    385:     ngx_http_ssi_main_conf_t  *smcf;
                    386:     ngx_str_t                 *params[NGX_HTTP_SSI_MAX_PARAMS + 1];
                    387: 
                    388:     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
                    389: 
                    390:     if (ctx == NULL
                    391:         || (in == NULL
                    392:             && ctx->buf == NULL
                    393:             && ctx->in == NULL
                    394:             && ctx->busy == NULL))
                    395:     {
                    396:         return ngx_http_next_body_filter(r, in);
                    397:     }
                    398: 
                    399:     /* add the incoming chain to the chain ctx->in */
                    400: 
                    401:     if (in) {
                    402:         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
                    403:             return NGX_ERROR;
                    404:         }
                    405:     }
                    406: 
                    407:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    408:                    "http ssi filter \"%V?%V\"", &r->uri, &r->args);
                    409: 
                    410:     if (ctx->wait) {
                    411: 
                    412:         if (r != r->connection->data) {
                    413:             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    414:                            "http ssi filter wait \"%V?%V\" non-active",
                    415:                            &ctx->wait->uri, &ctx->wait->args);
                    416: 
                    417:             return NGX_AGAIN;
                    418:         }
                    419: 
                    420:         if (ctx->wait->done) {
                    421:             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    422:                            "http ssi filter wait \"%V?%V\" done",
                    423:                            &ctx->wait->uri, &ctx->wait->args);
                    424: 
                    425:             ctx->wait = NULL;
                    426: 
                    427:         } else {
                    428:             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    429:                            "http ssi filter wait \"%V?%V\"",
                    430:                            &ctx->wait->uri, &ctx->wait->args);
                    431: 
                    432:             return ngx_http_next_body_filter(r, NULL);
                    433:         }
                    434:     }
                    435: 
                    436:     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
                    437: 
                    438:     while (ctx->in || ctx->buf) {
                    439: 
                    440:         if (ctx->buf == NULL) {
                    441:             ctx->buf = ctx->in->buf;
                    442:             ctx->in = ctx->in->next;
                    443:             ctx->pos = ctx->buf->pos;
                    444:         }
                    445: 
                    446:         if (ctx->state == ssi_start_state) {
                    447:             ctx->copy_start = ctx->pos;
                    448:             ctx->copy_end = ctx->pos;
                    449:         }
                    450: 
                    451:         b = NULL;
                    452: 
                    453:         while (ctx->pos < ctx->buf->last) {
                    454: 
                    455:             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    456:                            "saved: %d state: %d", ctx->saved, ctx->state);
                    457: 
                    458:             rc = ngx_http_ssi_parse(r, ctx);
                    459: 
                    460:             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    461:                            "parse: %d, looked: %d %p-%p",
                    462:                            rc, ctx->looked, ctx->copy_start, ctx->copy_end);
                    463: 
                    464:             if (rc == NGX_ERROR) {
                    465:                 return rc;
                    466:             }
                    467: 
                    468:             if (ctx->copy_start != ctx->copy_end) {
                    469: 
                    470:                 if (ctx->output) {
                    471: 
                    472:                     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    473:                                    "saved: %d", ctx->saved);
                    474: 
                    475:                     if (ctx->saved) {
                    476: 
                    477:                         if (ctx->free) {
                    478:                             cl = ctx->free;
                    479:                             ctx->free = ctx->free->next;
                    480:                             b = cl->buf;
                    481:                             ngx_memzero(b, sizeof(ngx_buf_t));
                    482: 
                    483:                         } else {
                    484:                             b = ngx_calloc_buf(r->pool);
                    485:                             if (b == NULL) {
                    486:                                 return NGX_ERROR;
                    487:                             }
                    488: 
                    489:                             cl = ngx_alloc_chain_link(r->pool);
                    490:                             if (cl == NULL) {
                    491:                                 return NGX_ERROR;
                    492:                             }
                    493: 
                    494:                             cl->buf = b;
                    495:                         }
                    496: 
                    497:                         b->memory = 1;
                    498:                         b->pos = ngx_http_ssi_string;
                    499:                         b->last = ngx_http_ssi_string + ctx->saved;
                    500: 
                    501:                         *ctx->last_out = cl;
                    502:                         ctx->last_out = &cl->next;
                    503: 
                    504:                         ctx->saved = 0;
                    505:                     }
                    506: 
                    507:                     if (ctx->free) {
                    508:                         cl = ctx->free;
                    509:                         ctx->free = ctx->free->next;
                    510:                         b = cl->buf;
                    511: 
                    512:                     } else {
                    513:                         b = ngx_alloc_buf(r->pool);
                    514:                         if (b == NULL) {
                    515:                             return NGX_ERROR;
                    516:                         }
                    517: 
                    518:                         cl = ngx_alloc_chain_link(r->pool);
                    519:                         if (cl == NULL) {
                    520:                             return NGX_ERROR;
                    521:                         }
                    522: 
                    523:                         cl->buf = b;
                    524:                     }
                    525: 
                    526:                     ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
                    527: 
                    528:                     b->pos = ctx->copy_start;
                    529:                     b->last = ctx->copy_end;
                    530:                     b->shadow = NULL;
                    531:                     b->last_buf = 0;
                    532:                     b->recycled = 0;
                    533: 
                    534:                     if (b->in_file) {
                    535:                         if (slcf->min_file_chunk < (size_t) (b->last - b->pos))
                    536:                         {
                    537:                             b->file_last = b->file_pos
                    538:                                                    + (b->last - ctx->buf->pos);
                    539:                             b->file_pos += b->pos - ctx->buf->pos;
                    540: 
                    541:                         } else {
                    542:                             b->in_file = 0;
                    543:                         }
                    544:                     }
                    545: 
                    546:                     cl->next = NULL;
                    547:                     *ctx->last_out = cl;
                    548:                     ctx->last_out = &cl->next;
                    549: 
                    550:                 } else {
                    551:                     if (ctx->block
                    552:                         && ctx->saved + (ctx->copy_end - ctx->copy_start))
                    553:                     {
                    554:                         b = ngx_create_temp_buf(r->pool,
                    555:                                ctx->saved + (ctx->copy_end - ctx->copy_start));
                    556: 
                    557:                         if (b == NULL) {
                    558:                             return NGX_ERROR;
                    559:                         }
                    560: 
                    561:                         if (ctx->saved) {
                    562:                             b->last = ngx_cpymem(b->pos, ngx_http_ssi_string,
                    563:                                                  ctx->saved);
                    564:                         }
                    565: 
                    566:                         b->last = ngx_cpymem(b->last, ctx->copy_start,
                    567:                                              ctx->copy_end - ctx->copy_start);
                    568: 
                    569:                         cl = ngx_alloc_chain_link(r->pool);
                    570:                         if (cl == NULL) {
                    571:                             return NGX_ERROR;
                    572:                         }
                    573: 
                    574:                         cl->buf = b;
                    575:                         cl->next = NULL;
                    576: 
                    577:                         b = NULL;
                    578: 
                    579:                         mctx = ngx_http_get_module_ctx(r->main,
                    580:                                                    ngx_http_ssi_filter_module);
                    581:                         bl = mctx->blocks->elts;
                    582:                         for (ll = &bl[mctx->blocks->nelts - 1].bufs;
                    583:                              *ll;
                    584:                              ll = &(*ll)->next)
                    585:                         {
                    586:                             /* void */
                    587:                         }
                    588: 
                    589:                         *ll = cl;
                    590:                     }
                    591: 
                    592:                     ctx->saved = 0;
                    593:                 }
                    594:             }
                    595: 
                    596:             if (ctx->state == ssi_start_state) {
                    597:                 ctx->copy_start = ctx->pos;
                    598:                 ctx->copy_end = ctx->pos;
                    599: 
                    600:             } else {
                    601:                 ctx->copy_start = NULL;
                    602:                 ctx->copy_end = NULL;
                    603:             }
                    604: 
                    605:             if (rc == NGX_AGAIN) {
                    606:                 continue;
                    607:             }
                    608: 
                    609: 
                    610:             b = NULL;
                    611: 
                    612:             if (rc == NGX_OK) {
                    613: 
                    614:                 smcf = ngx_http_get_module_main_conf(r,
                    615:                                                    ngx_http_ssi_filter_module);
                    616: 
                    617:                 cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,
                    618:                                     ctx->command.len);
                    619: 
                    620:                 if (cmd == NULL) {
                    621:                     if (ctx->output) {
                    622:                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                    623:                                       "invalid SSI command: \"%V\"",
                    624:                                       &ctx->command);
                    625:                         goto ssi_error;
                    626:                     }
                    627: 
                    628:                     continue;
                    629:                 }
                    630: 
                    631:                 if (!ctx->output && !cmd->block) {
                    632: 
                    633:                     if (ctx->block) {
                    634: 
                    635:                         /* reconstruct the SSI command text */
                    636: 
                    637:                         len = 5 + ctx->command.len + 4;
                    638: 
                    639:                         param = ctx->params.elts;
                    640:                         for (i = 0; i < ctx->params.nelts; i++) {
                    641:                             len += 1 + param[i].key.len + 2
                    642:                                 + param[i].value.len + 1;
                    643:                         }
                    644: 
                    645:                         b = ngx_create_temp_buf(r->pool, len);
                    646: 
                    647:                         if (b == NULL) {
                    648:                             return NGX_ERROR;
                    649:                         }
                    650: 
                    651:                         cl = ngx_alloc_chain_link(r->pool);
                    652:                         if (cl == NULL) {
                    653:                             return NGX_ERROR;
                    654:                         }
                    655: 
                    656:                         cl->buf = b;
                    657:                         cl->next = NULL;
                    658: 
                    659:                         *b->last++ = '<';
                    660:                         *b->last++ = '!';
                    661:                         *b->last++ = '-';
                    662:                         *b->last++ = '-';
                    663:                         *b->last++ = '#';
                    664: 
                    665:                         b->last = ngx_cpymem(b->last, ctx->command.data,
                    666:                                              ctx->command.len);
                    667: 
                    668:                         for (i = 0; i < ctx->params.nelts; i++) {
                    669:                             *b->last++ = ' ';
                    670:                             b->last = ngx_cpymem(b->last, param[i].key.data,
                    671:                                                  param[i].key.len);
                    672:                             *b->last++ = '=';
                    673:                             *b->last++ = '"';
                    674:                             b->last = ngx_cpymem(b->last, param[i].value.data,
                    675:                                                  param[i].value.len);
                    676:                             *b->last++ = '"';
                    677:                         }
                    678: 
                    679:                         *b->last++ = ' ';
                    680:                         *b->last++ = '-';
                    681:                         *b->last++ = '-';
                    682:                         *b->last++ = '>';
                    683: 
                    684:                         mctx = ngx_http_get_module_ctx(r->main,
                    685:                                                    ngx_http_ssi_filter_module);
                    686:                         bl = mctx->blocks->elts;
                    687:                         for (ll = &bl[mctx->blocks->nelts - 1].bufs;
                    688:                              *ll;
                    689:                              ll = &(*ll)->next)
                    690:                         {
                    691:                             /* void */
                    692:                         }
                    693: 
                    694:                         *ll = cl;
                    695: 
                    696:                         b = NULL;
                    697: 
                    698:                         continue;
                    699:                     }
                    700: 
                    701:                     if (cmd->conditional == 0) {
                    702:                         continue;
                    703:                     }
                    704:                 }
                    705: 
                    706:                 if (cmd->conditional
                    707:                     && (ctx->conditional == 0
                    708:                         || ctx->conditional > cmd->conditional))
                    709:                 {
                    710:                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                    711:                                   "invalid context of SSI command: \"%V\"",
                    712:                                   &ctx->command);
                    713:                     goto ssi_error;
                    714:                 }
                    715: 
                    716:                 if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {
                    717:                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                    718:                                   "too many SSI command parameters: \"%V\"",
                    719:                                   &ctx->command);
                    720:                     goto ssi_error;
                    721:                 }
                    722: 
                    723:                 ngx_memzero(params,
                    724:                            (NGX_HTTP_SSI_MAX_PARAMS + 1) * sizeof(ngx_str_t *));
                    725: 
                    726:                 param = ctx->params.elts;
                    727: 
                    728:                 for (i = 0; i < ctx->params.nelts; i++) {
                    729: 
                    730:                     for (prm = cmd->params; prm->name.len; prm++) {
                    731: 
                    732:                         if (param[i].key.len != prm->name.len
                    733:                             || ngx_strncmp(param[i].key.data, prm->name.data,
                    734:                                            prm->name.len) != 0)
                    735:                         {
                    736:                             continue;
                    737:                         }
                    738: 
                    739:                         if (!prm->multiple) {
                    740:                             if (params[prm->index]) {
                    741:                                 ngx_log_error(NGX_LOG_ERR,
                    742:                                               r->connection->log, 0,
                    743:                                               "duplicate \"%V\" parameter "
                    744:                                               "in \"%V\" SSI command",
                    745:                                               &param[i].key, &ctx->command);
                    746: 
                    747:                                 goto ssi_error;
                    748:                             }
                    749: 
                    750:                             params[prm->index] = &param[i].value;
                    751: 
                    752:                             break;
                    753:                         }
                    754: 
                    755:                         for (index = prm->index; params[index]; index++) {
                    756:                             /* void */
                    757:                         }
                    758: 
                    759:                         params[index] = &param[i].value;
                    760: 
                    761:                         break;
                    762:                     }
                    763: 
                    764:                     if (prm->name.len == 0) {
                    765:                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                    766:                                       "invalid parameter name: \"%V\" "
                    767:                                       "in \"%V\" SSI command",
                    768:                                       &param[i].key, &ctx->command);
                    769: 
                    770:                         goto ssi_error;
                    771:                     }
                    772:                 }
                    773: 
                    774:                 for (prm = cmd->params; prm->name.len; prm++) {
                    775:                     if (prm->mandatory && params[prm->index] == 0) {
                    776:                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                    777:                                       "mandatory \"%V\" parameter is absent "
                    778:                                       "in \"%V\" SSI command",
                    779:                                       &prm->name, &ctx->command);
                    780: 
                    781:                         goto ssi_error;
                    782:                     }
                    783:                 }
                    784: 
                    785:                 if (cmd->flush && ctx->out) {
                    786: 
                    787:                     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    788:                                    "ssi flush");
                    789: 
                    790:                     if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {
                    791:                         return NGX_ERROR;
                    792:                     }
                    793:                 }
                    794: 
                    795:                 rc = cmd->handler(r, ctx, params);
                    796: 
                    797:                 if (rc == NGX_OK) {
                    798:                     continue;
                    799:                 }
                    800: 
                    801:                 if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {
                    802:                     ngx_http_ssi_buffered(r, ctx);
                    803:                     return rc;
                    804:                 }
                    805:             }
                    806: 
                    807: 
                    808:             /* rc == NGX_HTTP_SSI_ERROR */
                    809: 
                    810:     ssi_error:
                    811: 
                    812:             if (slcf->silent_errors) {
                    813:                 continue;
                    814:             }
                    815: 
                    816:             if (ctx->free) {
                    817:                 cl = ctx->free;
                    818:                 ctx->free = ctx->free->next;
                    819:                 b = cl->buf;
                    820:                 ngx_memzero(b, sizeof(ngx_buf_t));
                    821: 
                    822:             } else {
                    823:                 b = ngx_calloc_buf(r->pool);
                    824:                 if (b == NULL) {
                    825:                     return NGX_ERROR;
                    826:                 }
                    827: 
                    828:                 cl = ngx_alloc_chain_link(r->pool);
                    829:                 if (cl == NULL) {
                    830:                     return NGX_ERROR;
                    831:                 }
                    832: 
                    833:                 cl->buf = b;
                    834:             }
                    835: 
                    836:             b->memory = 1;
                    837:             b->pos = ctx->errmsg.data;
                    838:             b->last = ctx->errmsg.data + ctx->errmsg.len;
                    839: 
                    840:             cl->next = NULL;
                    841:             *ctx->last_out = cl;
                    842:             ctx->last_out = &cl->next;
                    843: 
                    844:             continue;
                    845:         }
                    846: 
                    847:         if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {
                    848:             if (b == NULL) {
                    849:                 if (ctx->free) {
                    850:                     cl = ctx->free;
                    851:                     ctx->free = ctx->free->next;
                    852:                     b = cl->buf;
                    853:                     ngx_memzero(b, sizeof(ngx_buf_t));
                    854: 
                    855:                 } else {
                    856:                     b = ngx_calloc_buf(r->pool);
                    857:                     if (b == NULL) {
                    858:                         return NGX_ERROR;
                    859:                     }
                    860: 
                    861:                     cl = ngx_alloc_chain_link(r->pool);
                    862:                     if (cl == NULL) {
                    863:                         return NGX_ERROR;
                    864:                     }
                    865: 
                    866:                     cl->buf = b;
                    867:                 }
                    868: 
                    869:                 b->sync = 1;
                    870: 
                    871:                 cl->next = NULL;
                    872:                 *ctx->last_out = cl;
                    873:                 ctx->last_out = &cl->next;
                    874:             }
                    875: 
                    876:             b->last_buf = ctx->buf->last_buf;
                    877:             b->shadow = ctx->buf;
                    878: 
                    879:             if (slcf->ignore_recycled_buffers == 0)  {
                    880:                 b->recycled = ctx->buf->recycled;
                    881:             }
                    882:         }
                    883: 
                    884:         ctx->buf = NULL;
                    885: 
                    886:         ctx->saved = ctx->looked;
                    887:     }
                    888: 
                    889:     if (ctx->out == NULL && ctx->busy == NULL) {
                    890:         return NGX_OK;
                    891:     }
                    892: 
                    893:     return ngx_http_ssi_output(r, ctx);
                    894: }
                    895: 
                    896: 
                    897: static ngx_int_t
                    898: ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
                    899: {
                    900:     ngx_int_t     rc;
                    901:     ngx_buf_t    *b;
                    902:     ngx_chain_t  *cl;
                    903: 
                    904: #if 1
                    905:     b = NULL;
                    906:     for (cl = ctx->out; cl; cl = cl->next) {
                    907:         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    908:                        "ssi out: %p %p", cl->buf, cl->buf->pos);
                    909:         if (cl->buf == b) {
                    910:             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                    911:                           "the same buf was used in ssi");
                    912:             ngx_debug_point();
                    913:             return NGX_ERROR;
                    914:         }
                    915:         b = cl->buf;
                    916:     }
                    917: #endif
                    918: 
                    919:     rc = ngx_http_next_body_filter(r, ctx->out);
                    920: 
                    921:     if (ctx->busy == NULL) {
                    922:         ctx->busy = ctx->out;
                    923: 
                    924:     } else {
                    925:         for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
                    926:         cl->next = ctx->out;
                    927:     }
                    928: 
                    929:     ctx->out = NULL;
                    930:     ctx->last_out = &ctx->out;
                    931: 
                    932:     while (ctx->busy) {
                    933: 
                    934:         cl = ctx->busy;
                    935:         b = cl->buf;
                    936: 
                    937:         if (ngx_buf_size(b) != 0) {
                    938:             break;
                    939:         }
                    940: 
                    941:         if (b->shadow) {
                    942:             b->shadow->pos = b->shadow->last;
                    943:         }
                    944: 
                    945:         ctx->busy = cl->next;
                    946: 
                    947:         if (ngx_buf_in_memory(b) || b->in_file) {
                    948:             /* add data bufs only to the free buf chain */
                    949: 
                    950:             cl->next = ctx->free;
                    951:             ctx->free = cl;
                    952:         }
                    953:     }
                    954: 
                    955:     ngx_http_ssi_buffered(r, ctx);
                    956: 
                    957:     return rc;
                    958: }
                    959: 
                    960: 
                    961: static void
                    962: ngx_http_ssi_buffered(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
                    963: {
                    964:     if (ctx->in || ctx->buf) {
                    965:         r->buffered |= NGX_HTTP_SSI_BUFFERED;
                    966: 
                    967:     } else {
                    968:         r->buffered &= ~NGX_HTTP_SSI_BUFFERED;
                    969:     }
                    970: }
                    971: 
                    972: 
                    973: static ngx_int_t
                    974: ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
                    975: {
                    976:     u_char                *p, *value, *last, *copy_end, ch;
                    977:     size_t                 looked;
                    978:     ngx_http_ssi_state_e   state;
                    979: 
                    980:     state = ctx->state;
                    981:     looked = ctx->looked;
                    982:     last = ctx->buf->last;
                    983:     copy_end = ctx->copy_end;
                    984: 
                    985:     for (p = ctx->pos; p < last; p++) {
                    986: 
                    987:         ch = *p;
                    988: 
                    989:         if (state == ssi_start_state) {
                    990: 
                    991:             /* the tight loop */
                    992: 
                    993:             for ( ;; ) {
                    994:                 if (ch == '<') {
                    995:                     copy_end = p;
                    996:                     looked = 1;
                    997:                     state = ssi_tag_state;
                    998: 
                    999:                     goto tag_started;
                   1000:                 }
                   1001: 
                   1002:                 if (++p == last) {
                   1003:                     break;
                   1004:                 }
                   1005: 
                   1006:                 ch = *p;
                   1007:             }
                   1008: 
                   1009:             ctx->state = state;
                   1010:             ctx->pos = p;
                   1011:             ctx->looked = looked;
                   1012:             ctx->copy_end = p;
                   1013: 
                   1014:             if (ctx->copy_start == NULL) {
                   1015:                 ctx->copy_start = ctx->buf->pos;
                   1016:             }
                   1017: 
                   1018:             return NGX_AGAIN;
                   1019: 
                   1020:         tag_started:
                   1021: 
                   1022:             continue;
                   1023:         }
                   1024: 
                   1025:         switch (state) {
                   1026: 
                   1027:         case ssi_start_state:
                   1028:             /* not reached */
                   1029:             break;
                   1030: 
                   1031:         case ssi_tag_state:
                   1032:             switch (ch) {
                   1033:             case '!':
                   1034:                 looked = 2;
                   1035:                 state = ssi_comment0_state;
                   1036:                 break;
                   1037: 
                   1038:             case '<':
                   1039:                 copy_end = p;
                   1040:                 break;
                   1041: 
                   1042:             default:
                   1043:                 copy_end = p;
                   1044:                 looked = 0;
                   1045:                 state = ssi_start_state;
                   1046:                 break;
                   1047:             }
                   1048: 
                   1049:             break;
                   1050: 
                   1051:         case ssi_comment0_state:
                   1052:             switch (ch) {
                   1053:             case '-':
                   1054:                 looked = 3;
                   1055:                 state = ssi_comment1_state;
                   1056:                 break;
                   1057: 
                   1058:             case '<':
                   1059:                 copy_end = p;
                   1060:                 looked = 1;
                   1061:                 state = ssi_tag_state;
                   1062:                 break;
                   1063: 
                   1064:             default:
                   1065:                 copy_end = p;
                   1066:                 looked = 0;
                   1067:                 state = ssi_start_state;
                   1068:                 break;
                   1069:             }
                   1070: 
                   1071:             break;
                   1072: 
                   1073:         case ssi_comment1_state:
                   1074:             switch (ch) {
                   1075:             case '-':
                   1076:                 looked = 4;
                   1077:                 state = ssi_sharp_state;
                   1078:                 break;
                   1079: 
                   1080:             case '<':
                   1081:                 copy_end = p;
                   1082:                 looked = 1;
                   1083:                 state = ssi_tag_state;
                   1084:                 break;
                   1085: 
                   1086:             default:
                   1087:                 copy_end = p;
                   1088:                 looked = 0;
                   1089:                 state = ssi_start_state;
                   1090:                 break;
                   1091:             }
                   1092: 
                   1093:             break;
                   1094: 
                   1095:         case ssi_sharp_state:
                   1096:             switch (ch) {
                   1097:             case '#':
                   1098:                 if (p - ctx->pos < 4) {
                   1099:                     ctx->saved = 0;
                   1100:                 }
                   1101:                 looked = 0;
                   1102:                 state = ssi_precommand_state;
                   1103:                 break;
                   1104: 
                   1105:             case '<':
                   1106:                 copy_end = p;
                   1107:                 looked = 1;
                   1108:                 state = ssi_tag_state;
                   1109:                 break;
                   1110: 
                   1111:             default:
                   1112:                 copy_end = p;
                   1113:                 looked = 0;
                   1114:                 state = ssi_start_state;
                   1115:                 break;
                   1116:             }
                   1117: 
                   1118:             break;
                   1119: 
                   1120:         case ssi_precommand_state:
                   1121:             switch (ch) {
                   1122:             case ' ':
                   1123:             case CR:
                   1124:             case LF:
                   1125:             case '\t':
                   1126:                 break;
                   1127: 
                   1128:             default:
                   1129:                 ctx->command.len = 1;
                   1130:                 ctx->command.data = ngx_pnalloc(r->pool,
                   1131:                                                 NGX_HTTP_SSI_COMMAND_LEN);
                   1132:                 if (ctx->command.data == NULL) {
                   1133:                     return NGX_ERROR;
                   1134:                 }
                   1135: 
                   1136:                 ctx->command.data[0] = ch;
                   1137: 
                   1138:                 ctx->key = 0;
                   1139:                 ctx->key = ngx_hash(ctx->key, ch);
                   1140: 
                   1141:                 ctx->params.nelts = 0;
                   1142: 
                   1143:                 state = ssi_command_state;
                   1144:                 break;
                   1145:             }
                   1146: 
                   1147:             break;
                   1148: 
                   1149:         case ssi_command_state:
                   1150:             switch (ch) {
                   1151:             case ' ':
                   1152:             case CR:
                   1153:             case LF:
                   1154:             case '\t':
                   1155:                 state = ssi_preparam_state;
                   1156:                 break;
                   1157: 
                   1158:             case '-':
                   1159:                 state = ssi_comment_end0_state;
                   1160:                 break;
                   1161: 
                   1162:             default:
                   1163:                 if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {
                   1164:                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1165:                                   "the \"%V%c...\" SSI command is too long",
                   1166:                                   &ctx->command, ch);
                   1167: 
                   1168:                     state = ssi_error_state;
                   1169:                     break;
                   1170:                 }
                   1171: 
                   1172:                 ctx->command.data[ctx->command.len++] = ch;
                   1173:                 ctx->key = ngx_hash(ctx->key, ch);
                   1174:             }
                   1175: 
                   1176:             break;
                   1177: 
                   1178:         case ssi_preparam_state:
                   1179:             switch (ch) {
                   1180:             case ' ':
                   1181:             case CR:
                   1182:             case LF:
                   1183:             case '\t':
                   1184:                 break;
                   1185: 
                   1186:             case '-':
                   1187:                 state = ssi_comment_end0_state;
                   1188:                 break;
                   1189: 
                   1190:             default:
                   1191:                 ctx->param = ngx_array_push(&ctx->params);
                   1192:                 if (ctx->param == NULL) {
                   1193:                     return NGX_ERROR;
                   1194:                 }
                   1195: 
                   1196:                 ctx->param->key.len = 1;
                   1197:                 ctx->param->key.data = ngx_pnalloc(r->pool,
                   1198:                                                    NGX_HTTP_SSI_PARAM_LEN);
                   1199:                 if (ctx->param->key.data == NULL) {
                   1200:                     return NGX_ERROR;
                   1201:                 }
                   1202: 
                   1203:                 ctx->param->key.data[0] = ch;
                   1204: 
                   1205:                 ctx->param->value.len = 0;
                   1206: 
                   1207:                 if (ctx->value_buf == NULL) {
                   1208:                     ctx->param->value.data = ngx_pnalloc(r->pool,
                   1209:                                                          ctx->value_len + 1);
                   1210:                     if (ctx->param->value.data == NULL) {
                   1211:                         return NGX_ERROR;
                   1212:                     }
                   1213: 
                   1214:                 } else {
                   1215:                     ctx->param->value.data = ctx->value_buf;
                   1216:                 }
                   1217: 
                   1218:                 state = ssi_param_state;
                   1219:                 break;
                   1220:             }
                   1221: 
                   1222:             break;
                   1223: 
                   1224:         case ssi_param_state:
                   1225:             switch (ch) {
                   1226:             case ' ':
                   1227:             case CR:
                   1228:             case LF:
                   1229:             case '\t':
                   1230:                 state = ssi_preequal_state;
                   1231:                 break;
                   1232: 
                   1233:             case '=':
                   1234:                 state = ssi_prevalue_state;
                   1235:                 break;
                   1236: 
                   1237:             case '-':
                   1238:                 state = ssi_error_end0_state;
                   1239: 
                   1240:                 ctx->param->key.data[ctx->param->key.len++] = ch;
                   1241:                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1242:                               "invalid \"%V\" parameter in \"%V\" SSI command",
                   1243:                               &ctx->param->key, &ctx->command);
                   1244:                 break;
                   1245: 
                   1246:             default:
                   1247:                 if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {
                   1248:                     state = ssi_error_state;
                   1249:                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1250:                                   "too long \"%V%c...\" parameter in "
                   1251:                                   "\"%V\" SSI command",
                   1252:                                   &ctx->param->key, ch, &ctx->command);
                   1253:                     break;
                   1254:                 }
                   1255: 
                   1256:                 ctx->param->key.data[ctx->param->key.len++] = ch;
                   1257:             }
                   1258: 
                   1259:             break;
                   1260: 
                   1261:         case ssi_preequal_state:
                   1262:             switch (ch) {
                   1263:             case ' ':
                   1264:             case CR:
                   1265:             case LF:
                   1266:             case '\t':
                   1267:                 break;
                   1268: 
                   1269:             case '=':
                   1270:                 state = ssi_prevalue_state;
                   1271:                 break;
                   1272: 
                   1273:             default:
                   1274:                 if (ch == '-') {
                   1275:                     state = ssi_error_end0_state;
                   1276:                 } else {
                   1277:                     state = ssi_error_state;
                   1278:                 }
                   1279: 
                   1280:                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1281:                               "unexpected \"%c\" symbol after \"%V\" "
                   1282:                               "parameter in \"%V\" SSI command",
                   1283:                               ch, &ctx->param->key, &ctx->command);
                   1284:                 break;
                   1285:             }
                   1286: 
                   1287:             break;
                   1288: 
                   1289:         case ssi_prevalue_state:
                   1290:             switch (ch) {
                   1291:             case ' ':
                   1292:             case CR:
                   1293:             case LF:
                   1294:             case '\t':
                   1295:                 break;
                   1296: 
                   1297:             case '"':
                   1298:                 state = ssi_double_quoted_value_state;
                   1299:                 break;
                   1300: 
                   1301:             case '\'':
                   1302:                 state = ssi_quoted_value_state;
                   1303:                 break;
                   1304: 
                   1305:             default:
                   1306:                 if (ch == '-') {
                   1307:                     state = ssi_error_end0_state;
                   1308:                 } else {
                   1309:                     state = ssi_error_state;
                   1310:                 }
                   1311: 
                   1312:                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1313:                               "unexpected \"%c\" symbol before value of "
                   1314:                               "\"%V\" parameter in \"%V\" SSI command",
                   1315:                               ch, &ctx->param->key, &ctx->command);
                   1316:                 break;
                   1317:             }
                   1318: 
                   1319:             break;
                   1320: 
                   1321:         case ssi_double_quoted_value_state:
                   1322:             switch (ch) {
                   1323:             case '"':
                   1324:                 state = ssi_postparam_state;
                   1325:                 break;
                   1326: 
                   1327:             case '\\':
                   1328:                 ctx->saved_state = ssi_double_quoted_value_state;
                   1329:                 state = ssi_quoted_symbol_state;
                   1330: 
                   1331:                 /* fall through */
                   1332: 
                   1333:             default:
                   1334:                 if (ctx->param->value.len == ctx->value_len) {
                   1335:                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1336:                                   "too long \"%V%c...\" value of \"%V\" "
                   1337:                                   "parameter in \"%V\" SSI command",
                   1338:                                   &ctx->param->value, ch, &ctx->param->key,
                   1339:                                   &ctx->command);
                   1340:                     state = ssi_error_state;
                   1341:                     break;
                   1342:                 }
                   1343: 
                   1344:                 ctx->param->value.data[ctx->param->value.len++] = ch;
                   1345:             }
                   1346: 
                   1347:             break;
                   1348: 
                   1349:         case ssi_quoted_value_state:
                   1350:             switch (ch) {
                   1351:             case '\'':
                   1352:                 state = ssi_postparam_state;
                   1353:                 break;
                   1354: 
                   1355:             case '\\':
                   1356:                 ctx->saved_state = ssi_quoted_value_state;
                   1357:                 state = ssi_quoted_symbol_state;
                   1358: 
                   1359:                 /* fall through */
                   1360: 
                   1361:             default:
                   1362:                 if (ctx->param->value.len == ctx->value_len) {
                   1363:                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1364:                                   "too long \"%V%c...\" value of \"%V\" "
                   1365:                                   "parameter in \"%V\" SSI command",
                   1366:                                   &ctx->param->value, ch, &ctx->param->key,
                   1367:                                   &ctx->command);
                   1368:                     state = ssi_error_state;
                   1369:                     break;
                   1370:                 }
                   1371: 
                   1372:                 ctx->param->value.data[ctx->param->value.len++] = ch;
                   1373:             }
                   1374: 
                   1375:             break;
                   1376: 
                   1377:         case ssi_quoted_symbol_state:
                   1378:             state = ctx->saved_state;
                   1379: 
                   1380:             if (ctx->param->value.len == ctx->value_len) {
                   1381:                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1382:                               "too long \"%V%c...\" value of \"%V\" "
                   1383:                               "parameter in \"%V\" SSI command",
                   1384:                               &ctx->param->value, ch, &ctx->param->key,
                   1385:                               &ctx->command);
                   1386:                 state = ssi_error_state;
                   1387:                 break;
                   1388:             }
                   1389: 
                   1390:             ctx->param->value.data[ctx->param->value.len++] = ch;
                   1391: 
                   1392:             break;
                   1393: 
                   1394:         case ssi_postparam_state:
                   1395: 
                   1396:             if (ctx->param->value.len + 1 < ctx->value_len / 2) {
                   1397:                 value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);
                   1398:                 if (value == NULL) {
                   1399:                     return NGX_ERROR;
                   1400:                 }
                   1401: 
                   1402:                 ngx_memcpy(value, ctx->param->value.data,
                   1403:                            ctx->param->value.len);
                   1404: 
                   1405:                 ctx->value_buf = ctx->param->value.data;
                   1406:                 ctx->param->value.data = value;
                   1407: 
                   1408:             } else {
                   1409:                 ctx->value_buf = NULL;
                   1410:             }
                   1411: 
                   1412:             switch (ch) {
                   1413:             case ' ':
                   1414:             case CR:
                   1415:             case LF:
                   1416:             case '\t':
                   1417:                 state = ssi_preparam_state;
                   1418:                 break;
                   1419: 
                   1420:             case '-':
                   1421:                 state = ssi_comment_end0_state;
                   1422:                 break;
                   1423: 
                   1424:             default:
                   1425:                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1426:                               "unexpected \"%c\" symbol after \"%V\" value "
                   1427:                               "of \"%V\" parameter in \"%V\" SSI command",
                   1428:                               ch, &ctx->param->value, &ctx->param->key,
                   1429:                               &ctx->command);
                   1430:                 state = ssi_error_state;
                   1431:                 break;
                   1432:             }
                   1433: 
                   1434:             break;
                   1435: 
                   1436:         case ssi_comment_end0_state:
                   1437:             switch (ch) {
                   1438:             case '-':
                   1439:                 state = ssi_comment_end1_state;
                   1440:                 break;
                   1441: 
                   1442:             default:
                   1443:                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1444:                               "unexpected \"%c\" symbol in \"%V\" SSI command",
                   1445:                               ch, &ctx->command);
                   1446:                 state = ssi_error_state;
                   1447:                 break;
                   1448:             }
                   1449: 
                   1450:             break;
                   1451: 
                   1452:         case ssi_comment_end1_state:
                   1453:             switch (ch) {
                   1454:             case '>':
                   1455:                 ctx->state = ssi_start_state;
                   1456:                 ctx->pos = p + 1;
                   1457:                 ctx->looked = looked;
                   1458:                 ctx->copy_end = copy_end;
                   1459: 
                   1460:                 if (ctx->copy_start == NULL && copy_end) {
                   1461:                     ctx->copy_start = ctx->buf->pos;
                   1462:                 }
                   1463: 
                   1464:                 return NGX_OK;
                   1465: 
                   1466:             default:
                   1467:                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1468:                               "unexpected \"%c\" symbol in \"%V\" SSI command",
                   1469:                               ch, &ctx->command);
                   1470:                 state = ssi_error_state;
                   1471:                 break;
                   1472:             }
                   1473: 
                   1474:             break;
                   1475: 
                   1476:         case ssi_error_state:
                   1477:             switch (ch) {
                   1478:             case '-':
                   1479:                 state = ssi_error_end0_state;
                   1480:                 break;
                   1481: 
                   1482:             default:
                   1483:                 break;
                   1484:             }
                   1485: 
                   1486:             break;
                   1487: 
                   1488:         case ssi_error_end0_state:
                   1489:             switch (ch) {
                   1490:             case '-':
                   1491:                 state = ssi_error_end1_state;
                   1492:                 break;
                   1493: 
                   1494:             default:
                   1495:                 state = ssi_error_state;
                   1496:                 break;
                   1497:             }
                   1498: 
                   1499:             break;
                   1500: 
                   1501:         case ssi_error_end1_state:
                   1502:             switch (ch) {
                   1503:             case '>':
                   1504:                 ctx->state = ssi_start_state;
                   1505:                 ctx->pos = p + 1;
                   1506:                 ctx->looked = looked;
                   1507:                 ctx->copy_end = copy_end;
                   1508: 
                   1509:                 if (ctx->copy_start == NULL && copy_end) {
                   1510:                     ctx->copy_start = ctx->buf->pos;
                   1511:                 }
                   1512: 
                   1513:                 return NGX_HTTP_SSI_ERROR;
                   1514: 
                   1515:             default:
                   1516:                 state = ssi_error_state;
                   1517:                 break;
                   1518:             }
                   1519: 
                   1520:             break;
                   1521:         }
                   1522:     }
                   1523: 
                   1524:     ctx->state = state;
                   1525:     ctx->pos = p;
                   1526:     ctx->looked = looked;
                   1527: 
                   1528:     ctx->copy_end = (state == ssi_start_state) ? p : copy_end;
                   1529: 
                   1530:     if (ctx->copy_start == NULL && ctx->copy_end) {
                   1531:         ctx->copy_start = ctx->buf->pos;
                   1532:     }
                   1533: 
                   1534:     return NGX_AGAIN;
                   1535: }
                   1536: 
                   1537: 
                   1538: static ngx_str_t *
                   1539: ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name,
                   1540:     ngx_uint_t key)
                   1541: {
                   1542:     ngx_uint_t           i;
                   1543:     ngx_list_part_t     *part;
                   1544:     ngx_http_ssi_var_t  *var;
                   1545:     ngx_http_ssi_ctx_t  *ctx;
                   1546: 
                   1547:     ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
                   1548: 
                   1549: #if (NGX_PCRE)
                   1550:     {
                   1551:     ngx_str_t  *value;
                   1552: 
                   1553:     if (key >= '0' && key <= '9') {
                   1554:         i = key - '0';
                   1555: 
                   1556:         if (i < ctx->ncaptures) {
                   1557:             value = ngx_palloc(r->pool, sizeof(ngx_str_t));
                   1558:             if (value == NULL) {
                   1559:                 return NULL;
                   1560:             }
                   1561: 
                   1562:             i *= 2;
                   1563: 
                   1564:             value->data = ctx->captures_data + ctx->captures[i];
                   1565:             value->len = ctx->captures[i + 1] - ctx->captures[i];
                   1566: 
                   1567:             return value;
                   1568:         }
                   1569:     }
                   1570:     }
                   1571: #endif
                   1572: 
                   1573:     if (ctx->variables == NULL) {
                   1574:         return NULL;
                   1575:     }
                   1576: 
                   1577:     part = &ctx->variables->part;
                   1578:     var = part->elts;
                   1579: 
                   1580:     for (i = 0; /* void */ ; i++) {
                   1581: 
                   1582:         if (i >= part->nelts) {
                   1583:             if (part->next == NULL) {
                   1584:                 break;
                   1585:             }
                   1586: 
                   1587:             part = part->next;
                   1588:             var = part->elts;
                   1589:             i = 0;
                   1590:         }
                   1591: 
                   1592:         if (name->len != var[i].name.len) {
                   1593:             continue;
                   1594:         }
                   1595: 
                   1596:         if (key != var[i].key) {
                   1597:             continue;
                   1598:         }
                   1599: 
                   1600:         if (ngx_strncmp(name->data, var[i].name.data, name->len) == 0) {
                   1601:             return &var[i].value;
                   1602:         }
                   1603:     }
                   1604: 
                   1605:     return NULL;
                   1606: }
                   1607: 
                   1608: 
                   1609: static ngx_int_t
                   1610: ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   1611:     ngx_str_t *text, ngx_uint_t flags)
                   1612: {
                   1613:     u_char                      ch, *p, **value, *data, *part_data;
                   1614:     size_t                     *size, len, prefix, part_len;
                   1615:     ngx_str_t                   var, *val;
                   1616:     ngx_int_t                   key;
                   1617:     ngx_uint_t                  i, n, bracket, quoted;
                   1618:     ngx_array_t                 lengths, values;
                   1619:     ngx_http_variable_value_t  *vv;
                   1620: 
                   1621:     n = ngx_http_script_variables_count(text);
                   1622: 
                   1623:     if (n == 0) {
                   1624: 
                   1625:         data = text->data;
                   1626:         p = data;
                   1627: 
                   1628:         if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') {
                   1629: 
                   1630:             for (prefix = r->uri.len; prefix; prefix--) {
                   1631:                 if (r->uri.data[prefix - 1] == '/') {
                   1632:                     break;
                   1633:                 }
                   1634:             }
                   1635: 
                   1636:             if (prefix) {
                   1637:                 len = prefix + text->len;
                   1638: 
                   1639:                 data = ngx_pnalloc(r->pool, len);
                   1640:                 if (data == NULL) {
                   1641:                     return NGX_ERROR;
                   1642:                 }
                   1643: 
                   1644:                 p = ngx_copy(data, r->uri.data, prefix);
                   1645:             }
                   1646:         }
                   1647: 
                   1648:         quoted = 0;
                   1649: 
                   1650:         for (i = 0; i < text->len; i++) {
                   1651:             ch = text->data[i];
                   1652: 
                   1653:             if (!quoted) {
                   1654: 
                   1655:                 if (ch == '\\') {
                   1656:                     quoted = 1;
                   1657:                     continue;
                   1658:                 }
                   1659: 
                   1660:             } else {
                   1661:                 quoted = 0;
                   1662: 
                   1663:                 if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
                   1664:                     *p++ = '\\';
                   1665:                 }
                   1666:             }
                   1667: 
                   1668:             *p++ = ch;
                   1669:         }
                   1670: 
                   1671:         text->len = p - data;
                   1672:         text->data = data;
                   1673: 
                   1674:         return NGX_OK;
                   1675:     }
                   1676: 
                   1677:     if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
                   1678:         return NGX_ERROR;
                   1679:     }
                   1680: 
                   1681:     if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
                   1682:         return NGX_ERROR;
                   1683:     }
                   1684: 
                   1685:     len = 0;
                   1686:     i = 0;
                   1687: 
                   1688:     while (i < text->len) {
                   1689: 
                   1690:         if (text->data[i] == '$') {
                   1691: 
                   1692:             var.len = 0;
                   1693: 
                   1694:             if (++i == text->len) {
                   1695:                 goto invalid_variable;
                   1696:             }
                   1697: 
                   1698:             if (text->data[i] == '{') {
                   1699:                 bracket = 1;
                   1700: 
                   1701:                 if (++i == text->len) {
                   1702:                     goto invalid_variable;
                   1703:                 }
                   1704: 
                   1705:                 var.data = &text->data[i];
                   1706: 
                   1707:             } else {
                   1708:                 bracket = 0;
                   1709:                 var.data = &text->data[i];
                   1710:             }
                   1711: 
                   1712:             for ( /* void */ ; i < text->len; i++, var.len++) {
                   1713:                 ch = text->data[i];
                   1714: 
                   1715:                 if (ch == '}' && bracket) {
                   1716:                     i++;
                   1717:                     bracket = 0;
                   1718:                     break;
                   1719:                 }
                   1720: 
                   1721:                 if ((ch >= 'A' && ch <= 'Z')
                   1722:                     || (ch >= 'a' && ch <= 'z')
                   1723:                     || (ch >= '0' && ch <= '9')
                   1724:                     || ch == '_')
                   1725:                 {
                   1726:                     continue;
                   1727:                 }
                   1728: 
                   1729:                 break;
                   1730:             }
                   1731: 
                   1732:             if (bracket) {
                   1733:                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1734:                               "the closing bracket in \"%V\" "
                   1735:                               "variable is missing", &var);
                   1736:                 return NGX_HTTP_SSI_ERROR;
                   1737:             }
                   1738: 
                   1739:             if (var.len == 0) {
                   1740:                 goto invalid_variable;
                   1741:             }
                   1742: 
                   1743:             key = ngx_hash_strlow(var.data, var.data, var.len);
                   1744: 
                   1745:             val = ngx_http_ssi_get_variable(r, &var, key);
                   1746: 
                   1747:             if (val == NULL) {
                   1748:                 vv = ngx_http_get_variable(r, &var, key);
                   1749:                 if (vv == NULL) {
                   1750:                     return NGX_ERROR;
                   1751:                 }
                   1752: 
                   1753:                 if (vv->not_found) {
                   1754:                     continue;
                   1755:                 }
                   1756: 
                   1757:                 part_data = vv->data;
                   1758:                 part_len = vv->len;
                   1759: 
                   1760:             } else {
                   1761:                 part_data = val->data;
                   1762:                 part_len = val->len;
                   1763:             }
                   1764: 
                   1765:         } else {
                   1766:             part_data = &text->data[i];
                   1767:             quoted = 0;
                   1768: 
                   1769:             for (p = part_data; i < text->len; i++) {
                   1770:                 ch = text->data[i];
                   1771: 
                   1772:                 if (!quoted) {
                   1773: 
                   1774:                     if (ch == '\\') {
                   1775:                         quoted = 1;
                   1776:                         continue;
                   1777:                     }
                   1778: 
                   1779:                     if (ch == '$') {
                   1780:                         break;
                   1781:                     }
                   1782: 
                   1783:                 } else {
                   1784:                     quoted = 0;
                   1785: 
                   1786:                     if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
                   1787:                         *p++ = '\\';
                   1788:                     }
                   1789:                 }
                   1790: 
                   1791:                 *p++ = ch;
                   1792:             }
                   1793: 
                   1794:             part_len = p - part_data;
                   1795:         }
                   1796: 
                   1797:         len += part_len;
                   1798: 
                   1799:         size = ngx_array_push(&lengths);
                   1800:         if (size == NULL) {
                   1801:             return NGX_ERROR;
                   1802:         }
                   1803: 
                   1804:         *size = part_len;
                   1805: 
                   1806:         value = ngx_array_push(&values);
                   1807:         if (value == NULL) {
                   1808:             return NGX_ERROR;
                   1809:         }
                   1810: 
                   1811:         *value = part_data;
                   1812:     }
                   1813: 
                   1814:     prefix = 0;
                   1815: 
                   1816:     size = lengths.elts;
                   1817:     value = values.elts;
                   1818: 
                   1819:     if (flags & NGX_HTTP_SSI_ADD_PREFIX) {
                   1820:         for (i = 0; i < values.nelts; i++) {
                   1821:             if (size[i] != 0) {
                   1822:                 if (*value[i] != '/') {
                   1823:                     for (prefix = r->uri.len; prefix; prefix--) {
                   1824:                         if (r->uri.data[prefix - 1] == '/') {
                   1825:                             len += prefix;
                   1826:                             break;
                   1827:                         }
                   1828:                     }
                   1829:                 }
                   1830: 
                   1831:                 break;
                   1832:             }
                   1833:         }
                   1834:     }
                   1835: 
                   1836:     p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
                   1837:     if (p == NULL) {
                   1838:         return NGX_ERROR;
                   1839:     }
                   1840: 
                   1841:     text->len = len;
                   1842:     text->data = p;
                   1843: 
                   1844:     p = ngx_copy(p, r->uri.data, prefix);
                   1845: 
                   1846:     for (i = 0; i < values.nelts; i++) {
                   1847:         p = ngx_copy(p, value[i], size[i]);
                   1848:     }
                   1849: 
                   1850:     return NGX_OK;
                   1851: 
                   1852: invalid_variable:
                   1853: 
                   1854:     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1855:                   "invalid variable name in \"%V\"", text);
                   1856: 
                   1857:     return NGX_HTTP_SSI_ERROR;
                   1858: }
                   1859: 
                   1860: 
                   1861: static ngx_int_t
                   1862: ngx_http_ssi_regex_match(ngx_http_request_t *r, ngx_str_t *pattern,
                   1863:     ngx_str_t *str)
                   1864: {
                   1865: #if (NGX_PCRE)
                   1866:     int                   rc, *captures;
                   1867:     u_char               *p, errstr[NGX_MAX_CONF_ERRSTR];
                   1868:     size_t                size;
                   1869:     ngx_int_t             key;
                   1870:     ngx_str_t            *vv, name, value;
                   1871:     ngx_uint_t            i, n;
                   1872:     ngx_http_ssi_ctx_t   *ctx;
                   1873:     ngx_http_ssi_var_t   *var;
                   1874:     ngx_regex_compile_t   rgc;
                   1875: 
                   1876:     ngx_memzero(&rgc, sizeof(ngx_regex_compile_t));
                   1877: 
                   1878:     rgc.pattern = *pattern;
                   1879:     rgc.pool = r->pool;
                   1880:     rgc.err.len = NGX_MAX_CONF_ERRSTR;
                   1881:     rgc.err.data = errstr;
                   1882: 
                   1883:     if (ngx_regex_compile(&rgc) != NGX_OK) {
                   1884:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%V", &rgc.err);
                   1885:         return NGX_HTTP_SSI_ERROR;
                   1886:     }
                   1887: 
                   1888:     n = (rgc.captures + 1) * 3;
                   1889: 
                   1890:     captures = ngx_palloc(r->pool, n * sizeof(int));
                   1891:     if (captures == NULL) {
                   1892:         return NGX_ERROR;
                   1893:     }
                   1894: 
                   1895:     rc = ngx_regex_exec(rgc.regex, str, captures, n);
                   1896: 
                   1897:     if (rc < NGX_REGEX_NO_MATCHED) {
                   1898:         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                   1899:                       ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
                   1900:                       rc, str, pattern);
                   1901:         return NGX_HTTP_SSI_ERROR;
                   1902:     }
                   1903: 
                   1904:     if (rc == NGX_REGEX_NO_MATCHED) {
                   1905:         return NGX_DECLINED;
                   1906:     }
                   1907: 
                   1908:     ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
                   1909: 
                   1910:     ctx->ncaptures = rc;
                   1911:     ctx->captures = captures;
                   1912:     ctx->captures_data = str->data;
                   1913: 
                   1914:     if (rgc.named_captures > 0) {
                   1915: 
                   1916:         if (ctx->variables == NULL) {
                   1917:             ctx->variables = ngx_list_create(r->pool, 4,
                   1918:                                              sizeof(ngx_http_ssi_var_t));
                   1919:             if (ctx->variables == NULL) {
                   1920:                 return NGX_ERROR;
                   1921:             }
                   1922:         }
                   1923: 
                   1924:         size = rgc.name_size;
                   1925:         p = rgc.names;
                   1926: 
                   1927:         for (i = 0; i < (ngx_uint_t) rgc.named_captures; i++, p += size) {
                   1928: 
                   1929:             name.data = &p[2];
                   1930:             name.len = ngx_strlen(name.data);
                   1931: 
                   1932:             n = 2 * ((p[0] << 8) + p[1]);
                   1933: 
                   1934:             value.data = &str->data[captures[n]];
                   1935:             value.len = captures[n + 1] - captures[n];
                   1936: 
                   1937:             key = ngx_hash_strlow(name.data, name.data, name.len);
                   1938: 
                   1939:             vv = ngx_http_ssi_get_variable(r, &name, key);
                   1940: 
                   1941:             if (vv) {
                   1942:                 *vv = value;
                   1943:                 continue;
                   1944:             }
                   1945: 
                   1946:             var = ngx_list_push(ctx->variables);
                   1947:             if (var == NULL) {
                   1948:                 return NGX_ERROR;
                   1949:             }
                   1950: 
                   1951:             var->name = name;
                   1952:             var->key = key;
                   1953:             var->value = value;
                   1954:         }
                   1955:     }
                   1956: 
                   1957:     return NGX_OK;
                   1958: 
                   1959: #else
                   1960: 
                   1961:     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                   1962:                   "the using of the regex \"%V\" in SSI requires PCRE library",
                   1963:                   pattern);
                   1964:     return NGX_HTTP_SSI_ERROR;
                   1965: 
                   1966: #endif
                   1967: }
                   1968: 
                   1969: 
                   1970: static ngx_int_t
                   1971: ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   1972:     ngx_str_t **params)
                   1973: {
                   1974:     u_char                      *dst, *src;
                   1975:     size_t                       len;
                   1976:     ngx_int_t                    rc, key;
                   1977:     ngx_str_t                   *uri, *file, *wait, *set, *stub, args;
                   1978:     ngx_buf_t                   *b;
                   1979:     ngx_uint_t                   flags, i;
                   1980:     ngx_chain_t                 *cl, *tl, **ll, *out;
                   1981:     ngx_http_request_t          *sr;
                   1982:     ngx_http_ssi_var_t          *var;
                   1983:     ngx_http_ssi_ctx_t          *mctx;
                   1984:     ngx_http_ssi_block_t        *bl;
                   1985:     ngx_http_post_subrequest_t  *psr;
                   1986: 
                   1987:     uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
                   1988:     file = params[NGX_HTTP_SSI_INCLUDE_FILE];
                   1989:     wait = params[NGX_HTTP_SSI_INCLUDE_WAIT];
                   1990:     set = params[NGX_HTTP_SSI_INCLUDE_SET];
                   1991:     stub = params[NGX_HTTP_SSI_INCLUDE_STUB];
                   1992: 
                   1993:     if (uri && file) {
                   1994:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1995:                       "inlcusion may be either virtual=\"%V\" or file=\"%V\"",
                   1996:                       uri, file);
                   1997:         return NGX_HTTP_SSI_ERROR;
                   1998:     }
                   1999: 
                   2000:     if (uri == NULL && file == NULL) {
                   2001:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2002:                       "no parameter in \"include\" SSI command");
                   2003:         return NGX_HTTP_SSI_ERROR;
                   2004:     }
                   2005: 
                   2006:     if (set && stub) {
                   2007:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2008:                       "\"set\" and \"stub\" cannot be used together "
                   2009:                       "in \"include\" SSI command");
                   2010:         return NGX_HTTP_SSI_ERROR;
                   2011:     }
                   2012: 
                   2013:     if (wait) {
                   2014:         if (uri == NULL) {
                   2015:             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2016:                           "\"wait\" cannot be used with file=\"%V\"", file);
                   2017:             return NGX_HTTP_SSI_ERROR;
                   2018:         }
                   2019: 
                   2020:         if (wait->len == 2
                   2021:             && ngx_strncasecmp(wait->data, (u_char *) "no", 2) == 0)
                   2022:         {
                   2023:             wait = NULL;
                   2024: 
                   2025:         } else if (wait->len != 3
                   2026:                    || ngx_strncasecmp(wait->data, (u_char *) "yes", 3) != 0)
                   2027:         {
                   2028:             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2029:                           "invalid value \"%V\" in the \"wait\" parameter",
                   2030:                           wait);
                   2031:             return NGX_HTTP_SSI_ERROR;
                   2032:         }
                   2033:     }
                   2034: 
                   2035:     if (uri == NULL) {
                   2036:         uri = file;
                   2037:         wait = (ngx_str_t *) -1;
                   2038:     }
                   2039: 
                   2040:     rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);
                   2041: 
                   2042:     if (rc != NGX_OK) {
                   2043:         return rc;
                   2044:     }
                   2045: 
                   2046:     dst = uri->data;
                   2047:     src = uri->data;
                   2048: 
                   2049:     ngx_unescape_uri(&dst, &src, uri->len, NGX_UNESCAPE_URI);
                   2050: 
                   2051:     len = (uri->data + uri->len) - src;
                   2052:     if (len) {
                   2053:         dst = ngx_movemem(dst, src, len);
                   2054:     }
                   2055: 
                   2056:     uri->len = dst - uri->data;
                   2057: 
                   2058:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2059:                    "ssi include: \"%V\"", uri);
                   2060: 
                   2061:     ngx_str_null(&args);
                   2062:     flags = NGX_HTTP_LOG_UNSAFE;
                   2063: 
                   2064:     if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
                   2065:         return NGX_HTTP_SSI_ERROR;
                   2066:     }
                   2067: 
                   2068:     psr = NULL;
                   2069: 
                   2070:     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
                   2071: 
                   2072:     if (stub) {
                   2073:         if (mctx->blocks) {
                   2074:             bl = mctx->blocks->elts;
                   2075:             for (i = 0; i < mctx->blocks->nelts; i++) {
                   2076:                 if (stub->len == bl[i].name.len
                   2077:                     && ngx_strncmp(stub->data, bl[i].name.data, stub->len) == 0)
                   2078:                 {
                   2079:                     goto found;
                   2080:                 }
                   2081:             }
                   2082:         }
                   2083: 
                   2084:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2085:                       "\"stub\"=\"%V\" for \"include\" not found", stub);
                   2086:         return NGX_HTTP_SSI_ERROR;
                   2087: 
                   2088:     found:
                   2089: 
                   2090:         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
                   2091:         if (psr == NULL) {
                   2092:             return NGX_ERROR;
                   2093:         }
                   2094: 
                   2095:         psr->handler = ngx_http_ssi_stub_output;
                   2096: 
                   2097:         if (bl[i].count++) {
                   2098: 
                   2099:             out = NULL;
                   2100:             ll = &out;
                   2101: 
                   2102:             for (tl = bl[i].bufs; tl; tl = tl->next) {
                   2103: 
                   2104:                 if (ctx->free) {
                   2105:                     cl = ctx->free;
                   2106:                     ctx->free = ctx->free->next;
                   2107:                     b = cl->buf;
                   2108: 
                   2109:                 } else {
                   2110:                     b = ngx_alloc_buf(r->pool);
                   2111:                     if (b == NULL) {
                   2112:                         return NGX_ERROR;
                   2113:                     }
                   2114: 
                   2115:                     cl = ngx_alloc_chain_link(r->pool);
                   2116:                     if (cl == NULL) {
                   2117:                         return NGX_ERROR;
                   2118:                     }
                   2119: 
                   2120:                     cl->buf = b;
                   2121:                 }
                   2122: 
                   2123:                 ngx_memcpy(b, tl->buf, sizeof(ngx_buf_t));
                   2124: 
                   2125:                 b->pos = b->start;
                   2126: 
                   2127:                 *ll = cl;
                   2128:                 cl->next = NULL;
                   2129:                 ll = &cl->next;
                   2130:             }
                   2131: 
                   2132:             psr->data = out;
                   2133: 
                   2134:         } else {
                   2135:             psr->data = bl[i].bufs;
                   2136:         }
                   2137:     }
                   2138: 
                   2139:     if (wait) {
                   2140:         flags |= NGX_HTTP_SUBREQUEST_WAITED;
                   2141:     }
                   2142: 
                   2143:     if (set) {
                   2144:         key = ngx_hash_strlow(set->data, set->data, set->len);
                   2145: 
                   2146:         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
                   2147:         if (psr == NULL) {
                   2148:             return NGX_ERROR;
                   2149:         }
                   2150: 
                   2151:         psr->handler = ngx_http_ssi_set_variable;
                   2152:         psr->data = ngx_http_ssi_get_variable(r, set, key);
                   2153: 
                   2154:         if (psr->data == NULL) {
                   2155: 
                   2156:             if (mctx->variables == NULL) {
                   2157:                 mctx->variables = ngx_list_create(r->pool, 4,
                   2158:                                                   sizeof(ngx_http_ssi_var_t));
                   2159:                 if (mctx->variables == NULL) {
                   2160:                     return NGX_ERROR;
                   2161:                 }
                   2162:             }
                   2163: 
                   2164:             var = ngx_list_push(mctx->variables);
                   2165:             if (var == NULL) {
                   2166:                 return NGX_ERROR;
                   2167:             }
                   2168: 
                   2169:             var->name = *set;
                   2170:             var->key = key;
                   2171:             var->value = ngx_http_ssi_null_string;
                   2172:             psr->data = &var->value;
                   2173:         }
                   2174: 
                   2175:         flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;
                   2176:     }
                   2177: 
                   2178:     if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {
                   2179:         return NGX_HTTP_SSI_ERROR;
                   2180:     }
                   2181: 
                   2182:     if (wait == NULL && set == NULL) {
                   2183:         return NGX_OK;
                   2184:     }
                   2185: 
                   2186:     if (ctx->wait == NULL) {
                   2187:         ctx->wait = sr;
                   2188: 
                   2189:         return NGX_AGAIN;
                   2190: 
                   2191:     } else {
                   2192:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2193:                       "can only wait for one subrequest at a time");
                   2194:     }
                   2195: 
                   2196:     return NGX_OK;
                   2197: }
                   2198: 
                   2199: 
                   2200: static ngx_int_t
                   2201: ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc)
                   2202: {
                   2203:     ngx_chain_t  *out;
                   2204: 
                   2205:     if (rc == NGX_ERROR || r->connection->error || r->request_output) {
                   2206:         return rc;
                   2207:     }
                   2208: 
                   2209:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2210:                    "ssi stub output: \"%V?%V\"", &r->uri, &r->args);
                   2211: 
                   2212:     out = data;
                   2213: 
                   2214:     if (!r->header_sent) {
                   2215:         r->headers_out.content_type_len =
                   2216:                                       r->parent->headers_out.content_type_len;
                   2217:         r->headers_out.content_type = r->parent->headers_out.content_type;
                   2218: 
                   2219:         if (ngx_http_send_header(r) == NGX_ERROR) {
                   2220:             return NGX_ERROR;
                   2221:         }
                   2222:     }
                   2223: 
                   2224:     return ngx_http_output_filter(r, out);
                   2225: }
                   2226: 
                   2227: 
                   2228: static ngx_int_t
                   2229: ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc)
                   2230: {
                   2231:     ngx_str_t  *value = data;
                   2232: 
                   2233:     if (r->upstream) {
                   2234:         value->len = r->upstream->buffer.last - r->upstream->buffer.pos;
                   2235:         value->data = r->upstream->buffer.pos;
                   2236:     }
                   2237: 
                   2238:     return rc;
                   2239: }
                   2240: 
                   2241: 
                   2242: static ngx_int_t
                   2243: ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   2244:     ngx_str_t **params)
                   2245: {
                   2246:     u_char                     *p;
                   2247:     uintptr_t                   len;
                   2248:     ngx_int_t                   key;
                   2249:     ngx_buf_t                  *b;
                   2250:     ngx_str_t                  *var, *value, *enc, text;
                   2251:     ngx_chain_t                *cl;
                   2252:     ngx_http_variable_value_t  *vv;
                   2253: 
                   2254:     var = params[NGX_HTTP_SSI_ECHO_VAR];
                   2255: 
                   2256:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2257:                    "ssi echo \"%V\"", var);
                   2258: 
                   2259:     key = ngx_hash_strlow(var->data, var->data, var->len);
                   2260: 
                   2261:     value = ngx_http_ssi_get_variable(r, var, key);
                   2262: 
                   2263:     if (value == NULL) {
                   2264:         vv = ngx_http_get_variable(r, var, key);
                   2265: 
                   2266:         if (vv == NULL) {
                   2267:             return NGX_HTTP_SSI_ERROR;
                   2268:         }
                   2269: 
                   2270:         if (!vv->not_found) {
                   2271:             text.data = vv->data;
                   2272:             text.len = vv->len;
                   2273:             value = &text;
                   2274:         }
                   2275:     }
                   2276: 
                   2277:     if (value == NULL) {
                   2278:         value = params[NGX_HTTP_SSI_ECHO_DEFAULT];
                   2279: 
                   2280:         if (value == NULL) {
                   2281:             value = &ngx_http_ssi_none;
                   2282: 
                   2283:         } else if (value->len == 0) {
                   2284:             return NGX_OK;
                   2285:         }
                   2286: 
                   2287:     } else {
                   2288:         if (value->len == 0) {
                   2289:             return NGX_OK;
                   2290:         }
                   2291:     }
                   2292: 
                   2293:     enc = params[NGX_HTTP_SSI_ECHO_ENCODING];
                   2294: 
                   2295:     if (enc) {
                   2296:         if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) {
                   2297: 
                   2298:             ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;
                   2299: 
                   2300:         } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {
                   2301: 
                   2302:             ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;
                   2303: 
                   2304:         } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {
                   2305: 
                   2306:             ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
                   2307: 
                   2308:         } else {
                   2309:             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2310:                           "unknown encoding \"%V\" in the \"echo\" command",
                   2311:                           enc);
                   2312:         }
                   2313:     }
                   2314: 
                   2315:     p = value->data;
                   2316: 
                   2317:     switch (ctx->encoding) {
                   2318: 
                   2319:     case NGX_HTTP_SSI_URL_ENCODING:
                   2320:         len = 2 * ngx_escape_uri(NULL, value->data, value->len,
                   2321:                                  NGX_ESCAPE_HTML);
                   2322: 
                   2323:         if (len) {
                   2324:             p = ngx_pnalloc(r->pool, value->len + len);
                   2325:             if (p == NULL) {
                   2326:                 return NGX_HTTP_SSI_ERROR;
                   2327:             }
                   2328: 
                   2329:             (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);
                   2330:         }
                   2331: 
                   2332:         len += value->len;
                   2333:         break;
                   2334: 
                   2335:     case NGX_HTTP_SSI_ENTITY_ENCODING:
                   2336:         len = ngx_escape_html(NULL, value->data, value->len);
                   2337: 
                   2338:         if (len) {
                   2339:             p = ngx_pnalloc(r->pool, value->len + len);
                   2340:             if (p == NULL) {
                   2341:                 return NGX_HTTP_SSI_ERROR;
                   2342:             }
                   2343: 
                   2344:             (void) ngx_escape_html(p, value->data, value->len);
                   2345:         }
                   2346: 
                   2347:         len += value->len;
                   2348:         break;
                   2349: 
                   2350:     default: /* NGX_HTTP_SSI_NO_ENCODING */
                   2351:         len = value->len;
                   2352:         break;
                   2353:     }
                   2354: 
                   2355:     b = ngx_calloc_buf(r->pool);
                   2356:     if (b == NULL) {
                   2357:         return NGX_HTTP_SSI_ERROR;
                   2358:     }
                   2359: 
                   2360:     cl = ngx_alloc_chain_link(r->pool);
                   2361:     if (cl == NULL) {
                   2362:         return NGX_HTTP_SSI_ERROR;
                   2363:     }
                   2364: 
                   2365:     b->memory = 1;
                   2366:     b->pos = p;
                   2367:     b->last = p + len;
                   2368: 
                   2369:     cl->buf = b;
                   2370:     cl->next = NULL;
                   2371:     *ctx->last_out = cl;
                   2372:     ctx->last_out = &cl->next;
                   2373: 
                   2374:     return NGX_OK;
                   2375: }
                   2376: 
                   2377: 
                   2378: static ngx_int_t
                   2379: ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   2380:     ngx_str_t **params)
                   2381: {
                   2382:     ngx_str_t  *value;
                   2383: 
                   2384:     value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];
                   2385: 
                   2386:     if (value) {
                   2387:         ctx->timefmt.len = value->len;
                   2388:         ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);
                   2389:         if (ctx->timefmt.data == NULL) {
                   2390:             return NGX_HTTP_SSI_ERROR;
                   2391:         }
                   2392: 
                   2393:         ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1);
                   2394:     }
                   2395: 
                   2396:     value = params[NGX_HTTP_SSI_CONFIG_ERRMSG];
                   2397: 
                   2398:     if (value) {
                   2399:         ctx->errmsg = *value;
                   2400:     }
                   2401: 
                   2402:     return NGX_OK;
                   2403: }
                   2404: 
                   2405: 
                   2406: static ngx_int_t
                   2407: ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   2408:     ngx_str_t **params)
                   2409: {
                   2410:     ngx_int_t            key, rc;
                   2411:     ngx_str_t           *name, *value, *vv;
                   2412:     ngx_http_ssi_var_t  *var;
                   2413:     ngx_http_ssi_ctx_t  *mctx;
                   2414: 
                   2415:     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
                   2416: 
                   2417:     if (mctx->variables == NULL) {
                   2418:         mctx->variables = ngx_list_create(r->pool, 4,
                   2419:                                           sizeof(ngx_http_ssi_var_t));
                   2420:         if (mctx->variables == NULL) {
                   2421:             return NGX_ERROR;
                   2422:         }
                   2423:     }
                   2424: 
                   2425:     name = params[NGX_HTTP_SSI_SET_VAR];
                   2426:     value = params[NGX_HTTP_SSI_SET_VALUE];
                   2427: 
                   2428:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2429:                    "ssi set \"%V\" \"%V\"", name, value);
                   2430: 
                   2431:     rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);
                   2432: 
                   2433:     if (rc != NGX_OK) {
                   2434:         return rc;
                   2435:     }
                   2436: 
                   2437:     key = ngx_hash_strlow(name->data, name->data, name->len);
                   2438: 
                   2439:     vv = ngx_http_ssi_get_variable(r, name, key);
                   2440: 
                   2441:     if (vv) {
                   2442:         *vv = *value;
                   2443:         return NGX_OK;
                   2444:     }
                   2445: 
                   2446:     var = ngx_list_push(mctx->variables);
                   2447:     if (var == NULL) {
                   2448:         return NGX_ERROR;
                   2449:     }
                   2450: 
                   2451:     var->name = *name;
                   2452:     var->key = key;
                   2453:     var->value = *value;
                   2454: 
                   2455:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2456:                    "set: \"%V\"=\"%V\"", name, value);
                   2457: 
                   2458:     return NGX_OK;
                   2459: }
                   2460: 
                   2461: 
                   2462: static ngx_int_t
                   2463: ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   2464:     ngx_str_t **params)
                   2465: {
                   2466:     u_char       *p, *last;
                   2467:     ngx_str_t    *expr, left, right;
                   2468:     ngx_int_t     rc;
                   2469:     ngx_uint_t    negative, noregex, flags;
                   2470: 
                   2471:     if (ctx->command.len == 2) {
                   2472:         if (ctx->conditional) {
                   2473:             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2474:                           "the \"if\" command inside the \"if\" command");
                   2475:             return NGX_HTTP_SSI_ERROR;
                   2476:         }
                   2477:     }
                   2478: 
                   2479:     if (ctx->output_chosen) {
                   2480:         ctx->output = 0;
                   2481:         return NGX_OK;
                   2482:     }
                   2483: 
                   2484:     expr = params[NGX_HTTP_SSI_IF_EXPR];
                   2485: 
                   2486:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2487:                    "ssi if expr=\"%V\"", expr);
                   2488: 
                   2489:     left.data = expr->data;
                   2490:     last = expr->data + expr->len;
                   2491: 
                   2492:     for (p = left.data; p < last; p++) {
                   2493:         if (*p >= 'A' && *p <= 'Z') {
                   2494:             *p |= 0x20;
                   2495:             continue;
                   2496:         }
                   2497: 
                   2498:         if ((*p >= 'a' && *p <= 'z')
                   2499:              || (*p >= '0' && *p <= '9')
                   2500:              || *p == '$' || *p == '{' || *p == '}' || *p == '_'
                   2501:              || *p == '"' || *p == '\'')
                   2502:         {
                   2503:             continue;
                   2504:         }
                   2505: 
                   2506:         break;
                   2507:     }
                   2508: 
                   2509:     left.len = p - left.data;
                   2510: 
                   2511:     while (p < last && *p == ' ') {
                   2512:         p++;
                   2513:     }
                   2514: 
                   2515:     flags = 0;
                   2516: 
                   2517:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2518:                    "left: \"%V\"", &left);
                   2519: 
                   2520:     rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);
                   2521: 
                   2522:     if (rc != NGX_OK) {
                   2523:         return rc;
                   2524:     }
                   2525: 
                   2526:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2527:                    "evaluted left: \"%V\"", &left);
                   2528: 
                   2529:     if (p == last) {
                   2530:         if (left.len) {
                   2531:             ctx->output = 1;
                   2532:             ctx->output_chosen = 1;
                   2533: 
                   2534:         } else {
                   2535:             ctx->output = 0;
                   2536:         }
                   2537: 
                   2538:         ctx->conditional = NGX_HTTP_SSI_COND_IF;
                   2539: 
                   2540:         return NGX_OK;
                   2541:     }
                   2542: 
                   2543:     if (p < last && *p == '=') {
                   2544:         negative = 0;
                   2545:         p++;
                   2546: 
                   2547:     } else if (p + 1 < last && *p == '!' && *(p + 1) == '=') {
                   2548:         negative = 1;
                   2549:         p += 2;
                   2550: 
                   2551:     } else {
                   2552:         goto invalid_expression;
                   2553:     }
                   2554: 
                   2555:     while (p < last && *p == ' ') {
                   2556:         p++;
                   2557:     }
                   2558: 
                   2559:     if (p < last - 1 && *p == '/') {
                   2560:         if (*(last - 1) != '/') {
                   2561:             goto invalid_expression;
                   2562:         }
                   2563: 
                   2564:         noregex = 0;
                   2565:         flags = NGX_HTTP_SSI_ADD_ZERO;
                   2566:         last--;
                   2567:         p++;
                   2568: 
                   2569:     } else {
                   2570:         noregex = 1;
                   2571:         flags = 0;
                   2572: 
                   2573:         if (p < last - 1 && p[0] == '\\' && p[1] == '/') {
                   2574:             p++;
                   2575:         }
                   2576:     }
                   2577: 
                   2578:     right.len = last - p;
                   2579:     right.data = p;
                   2580: 
                   2581:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2582:                    "right: \"%V\"", &right);
                   2583: 
                   2584:     rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);
                   2585: 
                   2586:     if (rc != NGX_OK) {
                   2587:         return rc;
                   2588:     }
                   2589: 
                   2590:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2591:                    "evaluted right: \"%V\"", &right);
                   2592: 
                   2593:     if (noregex) {
                   2594:         if (left.len != right.len) {
                   2595:             rc = -1;
                   2596: 
                   2597:         } else {
                   2598:             rc = ngx_strncmp(left.data, right.data, right.len);
                   2599:         }
                   2600: 
                   2601:     } else {
                   2602:         right.data[right.len] = '\0';
                   2603: 
                   2604:         rc = ngx_http_ssi_regex_match(r, &right, &left);
                   2605: 
                   2606:         if (rc == NGX_OK) {
                   2607:             rc = 0;
                   2608:         } else if (rc == NGX_DECLINED) {
                   2609:             rc = -1;
                   2610:         } else {
                   2611:             return rc;
                   2612:         }
                   2613:     }
                   2614: 
                   2615:     if ((rc == 0 && !negative) || (rc != 0 && negative)) {
                   2616:         ctx->output = 1;
                   2617:         ctx->output_chosen = 1;
                   2618: 
                   2619:     } else {
                   2620:         ctx->output = 0;
                   2621:     }
                   2622: 
                   2623:     ctx->conditional = NGX_HTTP_SSI_COND_IF;
                   2624: 
                   2625:     return NGX_OK;
                   2626: 
                   2627: invalid_expression:
                   2628: 
                   2629:     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   2630:                   "invalid expression in \"%V\"", expr);
                   2631: 
                   2632:     return NGX_HTTP_SSI_ERROR;
                   2633: }
                   2634: 
                   2635: 
                   2636: static ngx_int_t
                   2637: ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   2638:     ngx_str_t **params)
                   2639: {
                   2640:     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2641:                    "ssi else");
                   2642: 
                   2643:     if (ctx->output_chosen) {
                   2644:         ctx->output = 0;
                   2645:     } else {
                   2646:         ctx->output = 1;
                   2647:     }
                   2648: 
                   2649:     ctx->conditional = NGX_HTTP_SSI_COND_ELSE;
                   2650: 
                   2651:     return NGX_OK;
                   2652: }
                   2653: 
                   2654: 
                   2655: static ngx_int_t
                   2656: ngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   2657:     ngx_str_t **params)
                   2658: {
                   2659:     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2660:                    "ssi endif");
                   2661: 
                   2662:     ctx->output = 1;
                   2663:     ctx->output_chosen = 0;
                   2664:     ctx->conditional = 0;
                   2665: 
                   2666:     return NGX_OK;
                   2667: }
                   2668: 
                   2669: 
                   2670: static ngx_int_t
                   2671: ngx_http_ssi_block(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   2672:     ngx_str_t **params)
                   2673: {
                   2674:     ngx_http_ssi_ctx_t    *mctx;
                   2675:     ngx_http_ssi_block_t  *bl;
                   2676: 
                   2677:     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2678:                    "ssi block");
                   2679: 
                   2680:     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
                   2681: 
                   2682:     if (mctx->blocks == NULL) {
                   2683:         mctx->blocks = ngx_array_create(r->pool, 4,
                   2684:                                         sizeof(ngx_http_ssi_block_t));
                   2685:         if (mctx->blocks == NULL) {
                   2686:             return NGX_HTTP_SSI_ERROR;
                   2687:         }
                   2688:     }
                   2689: 
                   2690:     bl = ngx_array_push(mctx->blocks);
                   2691:     if (bl == NULL) {
                   2692:         return NGX_HTTP_SSI_ERROR;
                   2693:     }
                   2694: 
                   2695:     bl->name = *params[NGX_HTTP_SSI_BLOCK_NAME];
                   2696:     bl->bufs = NULL;
                   2697:     bl->count = 0;
                   2698: 
                   2699:     ctx->output = 0;
                   2700:     ctx->block = 1;
                   2701: 
                   2702:     return NGX_OK;
                   2703: }
                   2704: 
                   2705: 
                   2706: static ngx_int_t
                   2707: ngx_http_ssi_endblock(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
                   2708:     ngx_str_t **params)
                   2709: {
                   2710:     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   2711:                    "ssi endblock");
                   2712: 
                   2713:     ctx->output = 1;
                   2714:     ctx->block = 0;
                   2715: 
                   2716:     return NGX_OK;
                   2717: }
                   2718: 
                   2719: 
                   2720: static ngx_int_t
                   2721: ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
                   2722:     ngx_http_variable_value_t *v, uintptr_t gmt)
                   2723: {
                   2724:     ngx_http_ssi_ctx_t  *ctx;
                   2725:     ngx_time_t          *tp;
                   2726:     struct tm            tm;
                   2727:     char                 buf[NGX_HTTP_SSI_DATE_LEN];
                   2728: 
                   2729:     v->valid = 1;
                   2730:     v->no_cacheable = 0;
                   2731:     v->not_found = 0;
                   2732: 
                   2733:     tp = ngx_timeofday();
                   2734: 
                   2735:     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
                   2736: 
                   2737:     if (ctx == NULL
                   2738:         || (ctx->timefmt.len == sizeof("%s") - 1
                   2739:             && ctx->timefmt.data[0] == '%' && ctx->timefmt.data[1] == 's'))
                   2740:     {
                   2741:         v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);
                   2742:         if (v->data == NULL) {
                   2743:             return NGX_ERROR;
                   2744:         }
                   2745: 
                   2746:         v->len = ngx_sprintf(v->data, "%T", tp->sec) - v->data;
                   2747: 
                   2748:         return NGX_OK;
                   2749:     }
                   2750: 
                   2751:     if (gmt) {
                   2752:         ngx_libc_gmtime(tp->sec, &tm);
                   2753:     } else {
                   2754:         ngx_libc_localtime(tp->sec, &tm);
                   2755:     }
                   2756: 
                   2757:     v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,
                   2758:                       (char *) ctx->timefmt.data, &tm);
                   2759:     if (v->len == 0) {
                   2760:         return NGX_ERROR;
                   2761:     }
                   2762: 
                   2763:     v->data = ngx_pnalloc(r->pool, v->len);
                   2764:     if (v->data == NULL) {
                   2765:         return NGX_ERROR;
                   2766:     }
                   2767: 
                   2768:     ngx_memcpy(v->data, buf, v->len);
                   2769: 
                   2770:     return NGX_OK;
                   2771: }
                   2772: 
                   2773: 
                   2774: static ngx_int_t
                   2775: ngx_http_ssi_preconfiguration(ngx_conf_t *cf)
                   2776: {
                   2777:     ngx_int_t                  rc;
                   2778:     ngx_http_variable_t       *var, *v;
                   2779:     ngx_http_ssi_command_t    *cmd;
                   2780:     ngx_http_ssi_main_conf_t  *smcf;
                   2781: 
                   2782:     for (v = ngx_http_ssi_vars; v->name.len; v++) {
                   2783:         var = ngx_http_add_variable(cf, &v->name, v->flags);
                   2784:         if (var == NULL) {
                   2785:             return NGX_ERROR;
                   2786:         }
                   2787: 
                   2788:         var->get_handler = v->get_handler;
                   2789:         var->data = v->data;
                   2790:     }
                   2791: 
                   2792:     smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);
                   2793: 
                   2794:     for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {
                   2795:         rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,
                   2796:                               NGX_HASH_READONLY_KEY);
                   2797: 
                   2798:         if (rc == NGX_OK) {
                   2799:             continue;
                   2800:         }
                   2801: 
                   2802:         if (rc == NGX_BUSY) {
                   2803:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                   2804:                                "conflicting SSI command \"%V\"", &cmd->name);
                   2805:         }
                   2806: 
                   2807:         return NGX_ERROR;
                   2808:     }
                   2809: 
                   2810:     return NGX_OK;
                   2811: }
                   2812: 
                   2813: 
                   2814: static void *
                   2815: ngx_http_ssi_create_main_conf(ngx_conf_t *cf)
                   2816: {
                   2817:     ngx_http_ssi_main_conf_t  *smcf;
                   2818: 
                   2819:     smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));
                   2820:     if (smcf == NULL) {
                   2821:         return NULL;
                   2822:     }
                   2823: 
                   2824:     smcf->commands.pool = cf->pool;
                   2825:     smcf->commands.temp_pool = cf->temp_pool;
                   2826: 
                   2827:     if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {
                   2828:         return NULL;
                   2829:     }
                   2830: 
                   2831:     return smcf;
                   2832: }
                   2833: 
                   2834: 
                   2835: static char *
                   2836: ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)
                   2837: {
                   2838:     ngx_http_ssi_main_conf_t *smcf = conf;
                   2839: 
                   2840:     ngx_hash_init_t  hash;
                   2841: 
                   2842:     hash.hash = &smcf->hash;
                   2843:     hash.key = ngx_hash_key;
                   2844:     hash.max_size = 1024;
                   2845:     hash.bucket_size = ngx_cacheline_size;
                   2846:     hash.name = "ssi_command_hash";
                   2847:     hash.pool = cf->pool;
                   2848:     hash.temp_pool = NULL;
                   2849: 
                   2850:     if (ngx_hash_init(&hash, smcf->commands.keys.elts,
                   2851:                       smcf->commands.keys.nelts)
                   2852:         != NGX_OK)
                   2853:     {
                   2854:         return NGX_CONF_ERROR;
                   2855:     }
                   2856: 
                   2857:     return NGX_CONF_OK;
                   2858: }
                   2859: 
                   2860: 
                   2861: static void *
                   2862: ngx_http_ssi_create_loc_conf(ngx_conf_t *cf)
                   2863: {
                   2864:     ngx_http_ssi_loc_conf_t  *slcf;
                   2865: 
                   2866:     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
                   2867:     if (slcf == NULL) {
                   2868:         return NULL;
                   2869:     }
                   2870: 
                   2871:     /*
                   2872:      * set by ngx_pcalloc():
                   2873:      *
                   2874:      *     conf->types = { NULL };
                   2875:      *     conf->types_keys = NULL;
                   2876:      */
                   2877: 
                   2878:     slcf->enable = NGX_CONF_UNSET;
                   2879:     slcf->silent_errors = NGX_CONF_UNSET;
                   2880:     slcf->ignore_recycled_buffers = NGX_CONF_UNSET;
                   2881: 
                   2882:     slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;
                   2883:     slcf->value_len = NGX_CONF_UNSET_SIZE;
                   2884: 
                   2885:     return slcf;
                   2886: }
                   2887: 
                   2888: 
                   2889: static char *
                   2890: ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
                   2891: {
                   2892:     ngx_http_ssi_loc_conf_t *prev = parent;
                   2893:     ngx_http_ssi_loc_conf_t *conf = child;
                   2894: 
                   2895:     ngx_conf_merge_value(conf->enable, prev->enable, 0);
                   2896:     ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
                   2897:     ngx_conf_merge_value(conf->ignore_recycled_buffers,
                   2898:                          prev->ignore_recycled_buffers, 0);
                   2899: 
                   2900:     ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
                   2901:     ngx_conf_merge_size_value(conf->value_len, prev->value_len, 255);
                   2902: 
                   2903:     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
                   2904:                              &prev->types_keys, &prev->types,
                   2905:                              ngx_http_html_default_types)
                   2906:         != NGX_OK)
                   2907:     {
                   2908:         return NGX_CONF_ERROR;
                   2909:     }
                   2910: 
                   2911:     return NGX_CONF_OK;
                   2912: }
                   2913: 
                   2914: 
                   2915: static ngx_int_t
                   2916: ngx_http_ssi_filter_init(ngx_conf_t *cf)
                   2917: {
                   2918:     ngx_http_next_header_filter = ngx_http_top_header_filter;
                   2919:     ngx_http_top_header_filter = ngx_http_ssi_header_filter;
                   2920: 
                   2921:     ngx_http_next_body_filter = ngx_http_top_body_filter;
                   2922:     ngx_http_top_body_filter = ngx_http_ssi_body_filter;
                   2923: 
                   2924:     return NGX_OK;
                   2925: }

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