Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 #define NGX_HTTP_SSI_ERROR          1
0013 
0014 #define NGX_HTTP_SSI_DATE_LEN       2048
0015 
0016 #define NGX_HTTP_SSI_ADD_PREFIX     1
0017 #define NGX_HTTP_SSI_ADD_ZERO       2
0018 
0019 
0020 typedef struct {
0021     ngx_flag_t    enable;
0022     ngx_flag_t    silent_errors;
0023     ngx_flag_t    ignore_recycled_buffers;
0024     ngx_flag_t    last_modified;
0025 
0026     ngx_hash_t    types;
0027 
0028     size_t        min_file_chunk;
0029     size_t        value_len;
0030 
0031     ngx_array_t  *types_keys;
0032 } ngx_http_ssi_loc_conf_t;
0033 
0034 
0035 typedef struct {
0036     ngx_str_t     name;
0037     ngx_uint_t    key;
0038     ngx_str_t     value;
0039 } ngx_http_ssi_var_t;
0040 
0041 
0042 typedef struct {
0043     ngx_str_t     name;
0044     ngx_chain_t  *bufs;
0045     ngx_uint_t    count;
0046 } ngx_http_ssi_block_t;
0047 
0048 
0049 typedef enum {
0050     ssi_start_state = 0,
0051     ssi_tag_state,
0052     ssi_comment0_state,
0053     ssi_comment1_state,
0054     ssi_sharp_state,
0055     ssi_precommand_state,
0056     ssi_command_state,
0057     ssi_preparam_state,
0058     ssi_param_state,
0059     ssi_preequal_state,
0060     ssi_prevalue_state,
0061     ssi_double_quoted_value_state,
0062     ssi_quoted_value_state,
0063     ssi_quoted_symbol_state,
0064     ssi_postparam_state,
0065     ssi_comment_end0_state,
0066     ssi_comment_end1_state,
0067     ssi_error_state,
0068     ssi_error_end0_state,
0069     ssi_error_end1_state
0070 } ngx_http_ssi_state_e;
0071 
0072 
0073 static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,
0074     ngx_http_ssi_ctx_t *ctx);
0075 static void ngx_http_ssi_buffered(ngx_http_request_t *r,
0076     ngx_http_ssi_ctx_t *ctx);
0077 static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
0078     ngx_http_ssi_ctx_t *ctx);
0079 static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,
0080     ngx_str_t *name, ngx_uint_t key);
0081 static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,
0082     ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);
0083 static ngx_int_t ngx_http_ssi_regex_match(ngx_http_request_t *r,
0084     ngx_str_t *pattern, ngx_str_t *str);
0085 
0086 static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
0087     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0088 static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,
0089     ngx_int_t rc);
0090 static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,
0091     ngx_int_t rc);
0092 static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
0093     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0094 static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
0095     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0096 static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,
0097     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0098 static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
0099     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0100 static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,
0101     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0102 static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
0103     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0104 static ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,
0105     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0106 static ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,
0107     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
0108 
0109 static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
0110     ngx_http_variable_value_t *v, uintptr_t gmt);
0111 
0112 static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);
0113 static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);
0114 static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);
0115 static void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);
0116 static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,
0117     void *parent, void *child);
0118 static ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);
0119 
0120 
0121 static ngx_command_t  ngx_http_ssi_filter_commands[] = {
0122 
0123     { ngx_string("ssi"),
0124       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
0125                         |NGX_CONF_FLAG,
0126       ngx_conf_set_flag_slot,
0127       NGX_HTTP_LOC_CONF_OFFSET,
0128       offsetof(ngx_http_ssi_loc_conf_t, enable),
0129       NULL },
0130 
0131     { ngx_string("ssi_silent_errors"),
0132       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0133       ngx_conf_set_flag_slot,
0134       NGX_HTTP_LOC_CONF_OFFSET,
0135       offsetof(ngx_http_ssi_loc_conf_t, silent_errors),
0136       NULL },
0137 
0138     { ngx_string("ssi_ignore_recycled_buffers"),
0139       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0140       ngx_conf_set_flag_slot,
0141       NGX_HTTP_LOC_CONF_OFFSET,
0142       offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),
0143       NULL },
0144 
0145     { ngx_string("ssi_min_file_chunk"),
0146       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0147       ngx_conf_set_size_slot,
0148       NGX_HTTP_LOC_CONF_OFFSET,
0149       offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),
0150       NULL },
0151 
0152     { ngx_string("ssi_value_length"),
0153       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0154       ngx_conf_set_size_slot,
0155       NGX_HTTP_LOC_CONF_OFFSET,
0156       offsetof(ngx_http_ssi_loc_conf_t, value_len),
0157       NULL },
0158 
0159     { ngx_string("ssi_types"),
0160       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0161       ngx_http_types_slot,
0162       NGX_HTTP_LOC_CONF_OFFSET,
0163       offsetof(ngx_http_ssi_loc_conf_t, types_keys),
0164       &ngx_http_html_default_types[0] },
0165 
0166     { ngx_string("ssi_last_modified"),
0167       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0168       ngx_conf_set_flag_slot,
0169       NGX_HTTP_LOC_CONF_OFFSET,
0170       offsetof(ngx_http_ssi_loc_conf_t, last_modified),
0171       NULL },
0172 
0173       ngx_null_command
0174 };
0175 
0176 
0177 
0178 static ngx_http_module_t  ngx_http_ssi_filter_module_ctx = {
0179     ngx_http_ssi_preconfiguration,         /* preconfiguration */
0180     ngx_http_ssi_filter_init,              /* postconfiguration */
0181 
0182     ngx_http_ssi_create_main_conf,         /* create main configuration */
0183     ngx_http_ssi_init_main_conf,           /* init main configuration */
0184 
0185     NULL,                                  /* create server configuration */
0186     NULL,                                  /* merge server configuration */
0187 
0188     ngx_http_ssi_create_loc_conf,          /* create location configuration */
0189     ngx_http_ssi_merge_loc_conf            /* merge location configuration */
0190 };
0191 
0192 
0193 ngx_module_t  ngx_http_ssi_filter_module = {
0194     NGX_MODULE_V1,
0195     &ngx_http_ssi_filter_module_ctx,       /* module context */
0196     ngx_http_ssi_filter_commands,          /* module directives */
0197     NGX_HTTP_MODULE,                       /* module type */
0198     NULL,                                  /* init master */
0199     NULL,                                  /* init module */
0200     NULL,                                  /* init process */
0201     NULL,                                  /* init thread */
0202     NULL,                                  /* exit thread */
0203     NULL,                                  /* exit process */
0204     NULL,                                  /* exit master */
0205     NGX_MODULE_V1_PADDING
0206 };
0207 
0208 
0209 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0210 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
0211 
0212 
0213 static u_char ngx_http_ssi_string[] = "<!--";
0214 
0215 static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
0216 static ngx_str_t ngx_http_ssi_timefmt = ngx_string("%A, %d-%b-%Y %H:%M:%S %Z");
0217 static ngx_str_t ngx_http_ssi_null_string = ngx_null_string;
0218 
0219 
0220 #define  NGX_HTTP_SSI_INCLUDE_VIRTUAL  0
0221 #define  NGX_HTTP_SSI_INCLUDE_FILE     1
0222 #define  NGX_HTTP_SSI_INCLUDE_WAIT     2
0223 #define  NGX_HTTP_SSI_INCLUDE_SET      3
0224 #define  NGX_HTTP_SSI_INCLUDE_STUB     4
0225 
0226 #define  NGX_HTTP_SSI_ECHO_VAR         0
0227 #define  NGX_HTTP_SSI_ECHO_DEFAULT     1
0228 #define  NGX_HTTP_SSI_ECHO_ENCODING    2
0229 
0230 #define  NGX_HTTP_SSI_CONFIG_ERRMSG    0
0231 #define  NGX_HTTP_SSI_CONFIG_TIMEFMT   1
0232 
0233 #define  NGX_HTTP_SSI_SET_VAR          0
0234 #define  NGX_HTTP_SSI_SET_VALUE        1
0235 
0236 #define  NGX_HTTP_SSI_IF_EXPR          0
0237 
0238 #define  NGX_HTTP_SSI_BLOCK_NAME       0
0239 
0240 
0241 static ngx_http_ssi_param_t  ngx_http_ssi_include_params[] = {
0242     { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },
0243     { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },
0244     { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT, 0, 0 },
0245     { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET, 0, 0 },
0246     { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB, 0, 0 },
0247     { ngx_null_string, 0, 0, 0 }
0248 };
0249 
0250 
0251 static ngx_http_ssi_param_t  ngx_http_ssi_echo_params[] = {
0252     { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },
0253     { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },
0254     { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 },
0255     { ngx_null_string, 0, 0, 0 }
0256 };
0257 
0258 
0259 static ngx_http_ssi_param_t  ngx_http_ssi_config_params[] = {
0260     { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0, 0 },
0261     { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0, 0 },
0262     { ngx_null_string, 0, 0, 0 }
0263 };
0264 
0265 
0266 static ngx_http_ssi_param_t  ngx_http_ssi_set_params[] = {
0267     { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1, 0 },
0268     { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1, 0 },
0269     { ngx_null_string, 0, 0, 0 }
0270 };
0271 
0272 
0273 static ngx_http_ssi_param_t  ngx_http_ssi_if_params[] = {
0274     { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1, 0 },
0275     { ngx_null_string, 0, 0, 0 }
0276 };
0277 
0278 
0279 static ngx_http_ssi_param_t  ngx_http_ssi_block_params[] = {
0280     { ngx_string("name"), NGX_HTTP_SSI_BLOCK_NAME, 1, 0 },
0281     { ngx_null_string, 0, 0, 0 }
0282 };
0283 
0284 
0285 static ngx_http_ssi_param_t  ngx_http_ssi_no_params[] = {
0286     { ngx_null_string, 0, 0, 0 }
0287 };
0288 
0289 
0290 static ngx_http_ssi_command_t  ngx_http_ssi_commands[] = {
0291     { ngx_string("include"), ngx_http_ssi_include,
0292                        ngx_http_ssi_include_params, 0, 0, 1 },
0293     { ngx_string("echo"), ngx_http_ssi_echo,
0294                        ngx_http_ssi_echo_params, 0, 0, 0 },
0295     { ngx_string("config"), ngx_http_ssi_config,
0296                        ngx_http_ssi_config_params, 0, 0, 0 },
0297     { ngx_string("set"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0, 0 },
0298 
0299     { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0, 0 },
0300     { ngx_string("elif"), ngx_http_ssi_if, ngx_http_ssi_if_params,
0301                        NGX_HTTP_SSI_COND_IF, 0, 0 },
0302     { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params,
0303                        NGX_HTTP_SSI_COND_IF, 0, 0 },
0304     { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params,
0305                        NGX_HTTP_SSI_COND_ELSE, 0, 0 },
0306 
0307     { ngx_string("block"), ngx_http_ssi_block,
0308                        ngx_http_ssi_block_params, 0, 0, 0 },
0309     { ngx_string("endblock"), ngx_http_ssi_endblock,
0310                        ngx_http_ssi_no_params, 0, 1, 0 },
0311 
0312     { ngx_null_string, NULL, NULL, 0, 0, 0 }
0313 };
0314 
0315 
0316 static ngx_http_variable_t  ngx_http_ssi_vars[] = {
0317 
0318     { ngx_string("date_local"), NULL, ngx_http_ssi_date_gmt_local_variable, 0,
0319       NGX_HTTP_VAR_NOCACHEABLE, 0 },
0320 
0321     { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,
0322       NGX_HTTP_VAR_NOCACHEABLE, 0 },
0323 
0324       ngx_http_null_variable
0325 };
0326 
0327 
0328 
0329 static ngx_int_t
0330 ngx_http_ssi_header_filter(ngx_http_request_t *r)
0331 {
0332     ngx_http_ssi_ctx_t       *ctx;
0333     ngx_http_ssi_loc_conf_t  *slcf;
0334 
0335     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
0336 
0337     if (!slcf->enable
0338         || r->headers_out.content_length_n == 0
0339         || ngx_http_test_content_type(r, &slcf->types) == NULL)
0340     {
0341         return ngx_http_next_header_filter(r);
0342     }
0343 
0344     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));
0345     if (ctx == NULL) {
0346         return NGX_ERROR;
0347     }
0348 
0349     ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);
0350 
0351 
0352     ctx->value_len = slcf->value_len;
0353     ctx->last_out = &ctx->out;
0354 
0355     ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
0356     ctx->output = 1;
0357 
0358     ctx->params.elts = ctx->params_array;
0359     ctx->params.size = sizeof(ngx_table_elt_t);
0360     ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;
0361     ctx->params.pool = r->pool;
0362 
0363     ctx->timefmt = ngx_http_ssi_timefmt;
0364     ngx_str_set(&ctx->errmsg,
0365                 "[an error occurred while processing the directive]");
0366 
0367     r->filter_need_in_memory = 1;
0368 
0369     if (r == r->main) {
0370         ngx_http_clear_content_length(r);
0371         ngx_http_clear_accept_ranges(r);
0372 
0373         r->preserve_body = 1;
0374 
0375         if (!slcf->last_modified) {
0376             ngx_http_clear_last_modified(r);
0377             ngx_http_clear_etag(r);
0378 
0379         } else {
0380             ngx_http_weak_etag(r);
0381         }
0382     }
0383 
0384     return ngx_http_next_header_filter(r);
0385 }
0386 
0387 
0388 static ngx_int_t
0389 ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0390 {
0391     size_t                     len;
0392     ngx_int_t                  rc;
0393     ngx_buf_t                 *b;
0394     ngx_uint_t                 i, index;
0395     ngx_chain_t               *cl, **ll;
0396     ngx_table_elt_t           *param;
0397     ngx_http_ssi_ctx_t        *ctx, *mctx;
0398     ngx_http_ssi_block_t      *bl;
0399     ngx_http_ssi_param_t      *prm;
0400     ngx_http_ssi_command_t    *cmd;
0401     ngx_http_ssi_loc_conf_t   *slcf;
0402     ngx_http_ssi_main_conf_t  *smcf;
0403     ngx_str_t                 *params[NGX_HTTP_SSI_MAX_PARAMS + 1];
0404 
0405     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
0406 
0407     if (ctx == NULL
0408         || (in == NULL
0409             && ctx->buf == NULL
0410             && ctx->in == NULL
0411             && ctx->busy == NULL))
0412     {
0413         return ngx_http_next_body_filter(r, in);
0414     }
0415 
0416     /* add the incoming chain to the chain ctx->in */
0417 
0418     if (in) {
0419         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
0420             return NGX_ERROR;
0421         }
0422     }
0423 
0424     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0425                    "http ssi filter \"%V?%V\"", &r->uri, &r->args);
0426 
0427     if (ctx->wait) {
0428 
0429         if (r != r->connection->data) {
0430             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0431                            "http ssi filter wait \"%V?%V\" non-active",
0432                            &ctx->wait->uri, &ctx->wait->args);
0433 
0434             return NGX_AGAIN;
0435         }
0436 
0437         if (ctx->wait->done) {
0438             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0439                            "http ssi filter wait \"%V?%V\" done",
0440                            &ctx->wait->uri, &ctx->wait->args);
0441 
0442             ctx->wait = NULL;
0443 
0444         } else {
0445             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0446                            "http ssi filter wait \"%V?%V\"",
0447                            &ctx->wait->uri, &ctx->wait->args);
0448 
0449             return ngx_http_next_body_filter(r, NULL);
0450         }
0451     }
0452 
0453     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
0454 
0455     while (ctx->in || ctx->buf) {
0456 
0457         if (ctx->buf == NULL) {
0458             ctx->buf = ctx->in->buf;
0459             ctx->in = ctx->in->next;
0460             ctx->pos = ctx->buf->pos;
0461         }
0462 
0463         if (ctx->state == ssi_start_state) {
0464             ctx->copy_start = ctx->pos;
0465             ctx->copy_end = ctx->pos;
0466         }
0467 
0468         b = NULL;
0469 
0470         while (ctx->pos < ctx->buf->last) {
0471 
0472             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0473                            "saved: %uz state: %ui", ctx->saved, ctx->state);
0474 
0475             rc = ngx_http_ssi_parse(r, ctx);
0476 
0477             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0478                            "parse: %i, looked: %uz %p-%p",
0479                            rc, ctx->looked, ctx->copy_start, ctx->copy_end);
0480 
0481             if (rc == NGX_ERROR) {
0482                 return rc;
0483             }
0484 
0485             if (ctx->copy_start != ctx->copy_end) {
0486 
0487                 if (ctx->output) {
0488 
0489                     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0490                                    "saved: %uz", ctx->saved);
0491 
0492                     if (ctx->saved) {
0493 
0494                         if (ctx->free) {
0495                             cl = ctx->free;
0496                             ctx->free = ctx->free->next;
0497                             b = cl->buf;
0498                             ngx_memzero(b, sizeof(ngx_buf_t));
0499 
0500                         } else {
0501                             b = ngx_calloc_buf(r->pool);
0502                             if (b == NULL) {
0503                                 return NGX_ERROR;
0504                             }
0505 
0506                             cl = ngx_alloc_chain_link(r->pool);
0507                             if (cl == NULL) {
0508                                 return NGX_ERROR;
0509                             }
0510 
0511                             cl->buf = b;
0512                         }
0513 
0514                         b->memory = 1;
0515                         b->pos = ngx_http_ssi_string;
0516                         b->last = ngx_http_ssi_string + ctx->saved;
0517 
0518                         *ctx->last_out = cl;
0519                         ctx->last_out = &cl->next;
0520 
0521                         ctx->saved = 0;
0522                     }
0523 
0524                     if (ctx->free) {
0525                         cl = ctx->free;
0526                         ctx->free = ctx->free->next;
0527                         b = cl->buf;
0528 
0529                     } else {
0530                         b = ngx_alloc_buf(r->pool);
0531                         if (b == NULL) {
0532                             return NGX_ERROR;
0533                         }
0534 
0535                         cl = ngx_alloc_chain_link(r->pool);
0536                         if (cl == NULL) {
0537                             return NGX_ERROR;
0538                         }
0539 
0540                         cl->buf = b;
0541                     }
0542 
0543                     ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
0544 
0545                     b->pos = ctx->copy_start;
0546                     b->last = ctx->copy_end;
0547                     b->shadow = NULL;
0548                     b->last_buf = 0;
0549                     b->recycled = 0;
0550 
0551                     if (b->in_file) {
0552                         if (slcf->min_file_chunk < (size_t) (b->last - b->pos))
0553                         {
0554                             b->file_last = b->file_pos
0555                                                    + (b->last - ctx->buf->pos);
0556                             b->file_pos += b->pos - ctx->buf->pos;
0557 
0558                         } else {
0559                             b->in_file = 0;
0560                         }
0561                     }
0562 
0563                     cl->next = NULL;
0564                     *ctx->last_out = cl;
0565                     ctx->last_out = &cl->next;
0566 
0567                 } else {
0568                     if (ctx->block
0569                         && ctx->saved + (ctx->copy_end - ctx->copy_start))
0570                     {
0571                         b = ngx_create_temp_buf(r->pool,
0572                                ctx->saved + (ctx->copy_end - ctx->copy_start));
0573 
0574                         if (b == NULL) {
0575                             return NGX_ERROR;
0576                         }
0577 
0578                         if (ctx->saved) {
0579                             b->last = ngx_cpymem(b->pos, ngx_http_ssi_string,
0580                                                  ctx->saved);
0581                         }
0582 
0583                         b->last = ngx_cpymem(b->last, ctx->copy_start,
0584                                              ctx->copy_end - ctx->copy_start);
0585 
0586                         cl = ngx_alloc_chain_link(r->pool);
0587                         if (cl == NULL) {
0588                             return NGX_ERROR;
0589                         }
0590 
0591                         cl->buf = b;
0592                         cl->next = NULL;
0593 
0594                         b = NULL;
0595 
0596                         mctx = ngx_http_get_module_ctx(r->main,
0597                                                    ngx_http_ssi_filter_module);
0598                         bl = mctx->blocks->elts;
0599                         for (ll = &bl[mctx->blocks->nelts - 1].bufs;
0600                              *ll;
0601                              ll = &(*ll)->next)
0602                         {
0603                             /* void */
0604                         }
0605 
0606                         *ll = cl;
0607                     }
0608 
0609                     ctx->saved = 0;
0610                 }
0611             }
0612 
0613             if (ctx->state == ssi_start_state) {
0614                 ctx->copy_start = ctx->pos;
0615                 ctx->copy_end = ctx->pos;
0616 
0617             } else {
0618                 ctx->copy_start = NULL;
0619                 ctx->copy_end = NULL;
0620             }
0621 
0622             if (rc == NGX_AGAIN) {
0623                 continue;
0624             }
0625 
0626 
0627             b = NULL;
0628 
0629             if (rc == NGX_OK) {
0630 
0631                 smcf = ngx_http_get_module_main_conf(r,
0632                                                    ngx_http_ssi_filter_module);
0633 
0634                 cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,
0635                                     ctx->command.len);
0636 
0637                 if (cmd == NULL) {
0638                     if (ctx->output) {
0639                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0640                                       "invalid SSI command: \"%V\"",
0641                                       &ctx->command);
0642                         goto ssi_error;
0643                     }
0644 
0645                     continue;
0646                 }
0647 
0648                 if (!ctx->output && !cmd->block) {
0649 
0650                     if (ctx->block) {
0651 
0652                         /* reconstruct the SSI command text */
0653 
0654                         len = 5 + ctx->command.len + 4;
0655 
0656                         param = ctx->params.elts;
0657                         for (i = 0; i < ctx->params.nelts; i++) {
0658                             len += 1 + param[i].key.len + 2
0659                                 + param[i].value.len + 1;
0660                         }
0661 
0662                         b = ngx_create_temp_buf(r->pool, len);
0663 
0664                         if (b == NULL) {
0665                             return NGX_ERROR;
0666                         }
0667 
0668                         cl = ngx_alloc_chain_link(r->pool);
0669                         if (cl == NULL) {
0670                             return NGX_ERROR;
0671                         }
0672 
0673                         cl->buf = b;
0674                         cl->next = NULL;
0675 
0676                         *b->last++ = '<';
0677                         *b->last++ = '!';
0678                         *b->last++ = '-';
0679                         *b->last++ = '-';
0680                         *b->last++ = '#';
0681 
0682                         b->last = ngx_cpymem(b->last, ctx->command.data,
0683                                              ctx->command.len);
0684 
0685                         for (i = 0; i < ctx->params.nelts; i++) {
0686                             *b->last++ = ' ';
0687                             b->last = ngx_cpymem(b->last, param[i].key.data,
0688                                                  param[i].key.len);
0689                             *b->last++ = '=';
0690                             *b->last++ = '"';
0691                             b->last = ngx_cpymem(b->last, param[i].value.data,
0692                                                  param[i].value.len);
0693                             *b->last++ = '"';
0694                         }
0695 
0696                         *b->last++ = ' ';
0697                         *b->last++ = '-';
0698                         *b->last++ = '-';
0699                         *b->last++ = '>';
0700 
0701                         mctx = ngx_http_get_module_ctx(r->main,
0702                                                    ngx_http_ssi_filter_module);
0703                         bl = mctx->blocks->elts;
0704                         for (ll = &bl[mctx->blocks->nelts - 1].bufs;
0705                              *ll;
0706                              ll = &(*ll)->next)
0707                         {
0708                             /* void */
0709                         }
0710 
0711                         *ll = cl;
0712 
0713                         b = NULL;
0714 
0715                         continue;
0716                     }
0717 
0718                     if (cmd->conditional == 0) {
0719                         continue;
0720                     }
0721                 }
0722 
0723                 if (cmd->conditional
0724                     && (ctx->conditional == 0
0725                         || ctx->conditional > cmd->conditional))
0726                 {
0727                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0728                                   "invalid context of SSI command: \"%V\"",
0729                                   &ctx->command);
0730                     goto ssi_error;
0731                 }
0732 
0733                 if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {
0734                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0735                                   "too many SSI command parameters: \"%V\"",
0736                                   &ctx->command);
0737                     goto ssi_error;
0738                 }
0739 
0740                 ngx_memzero(params,
0741                            (NGX_HTTP_SSI_MAX_PARAMS + 1) * sizeof(ngx_str_t *));
0742 
0743                 param = ctx->params.elts;
0744 
0745                 for (i = 0; i < ctx->params.nelts; i++) {
0746 
0747                     for (prm = cmd->params; prm->name.len; prm++) {
0748 
0749                         if (param[i].key.len != prm->name.len
0750                             || ngx_strncmp(param[i].key.data, prm->name.data,
0751                                            prm->name.len) != 0)
0752                         {
0753                             continue;
0754                         }
0755 
0756                         if (!prm->multiple) {
0757                             if (params[prm->index]) {
0758                                 ngx_log_error(NGX_LOG_ERR,
0759                                               r->connection->log, 0,
0760                                               "duplicate \"%V\" parameter "
0761                                               "in \"%V\" SSI command",
0762                                               &param[i].key, &ctx->command);
0763 
0764                                 goto ssi_error;
0765                             }
0766 
0767                             params[prm->index] = &param[i].value;
0768 
0769                             break;
0770                         }
0771 
0772                         for (index = prm->index; params[index]; index++) {
0773                             /* void */
0774                         }
0775 
0776                         params[index] = &param[i].value;
0777 
0778                         break;
0779                     }
0780 
0781                     if (prm->name.len == 0) {
0782                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0783                                       "invalid parameter name: \"%V\" "
0784                                       "in \"%V\" SSI command",
0785                                       &param[i].key, &ctx->command);
0786 
0787                         goto ssi_error;
0788                     }
0789                 }
0790 
0791                 for (prm = cmd->params; prm->name.len; prm++) {
0792                     if (prm->mandatory && params[prm->index] == 0) {
0793                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0794                                       "mandatory \"%V\" parameter is absent "
0795                                       "in \"%V\" SSI command",
0796                                       &prm->name, &ctx->command);
0797 
0798                         goto ssi_error;
0799                     }
0800                 }
0801 
0802                 if (cmd->flush && ctx->out) {
0803 
0804                     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0805                                    "ssi flush");
0806 
0807                     if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {
0808                         return NGX_ERROR;
0809                     }
0810                 }
0811 
0812                 rc = cmd->handler(r, ctx, params);
0813 
0814                 if (rc == NGX_OK) {
0815                     continue;
0816                 }
0817 
0818                 if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {
0819                     ngx_http_ssi_buffered(r, ctx);
0820                     return rc;
0821                 }
0822             }
0823 
0824 
0825             /* rc == NGX_HTTP_SSI_ERROR */
0826 
0827     ssi_error:
0828 
0829             if (slcf->silent_errors) {
0830                 continue;
0831             }
0832 
0833             if (ctx->free) {
0834                 cl = ctx->free;
0835                 ctx->free = ctx->free->next;
0836                 b = cl->buf;
0837                 ngx_memzero(b, sizeof(ngx_buf_t));
0838 
0839             } else {
0840                 b = ngx_calloc_buf(r->pool);
0841                 if (b == NULL) {
0842                     return NGX_ERROR;
0843                 }
0844 
0845                 cl = ngx_alloc_chain_link(r->pool);
0846                 if (cl == NULL) {
0847                     return NGX_ERROR;
0848                 }
0849 
0850                 cl->buf = b;
0851             }
0852 
0853             b->memory = 1;
0854             b->pos = ctx->errmsg.data;
0855             b->last = ctx->errmsg.data + ctx->errmsg.len;
0856 
0857             cl->next = NULL;
0858             *ctx->last_out = cl;
0859             ctx->last_out = &cl->next;
0860 
0861             continue;
0862         }
0863 
0864         if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {
0865             if (b == NULL) {
0866                 if (ctx->free) {
0867                     cl = ctx->free;
0868                     ctx->free = ctx->free->next;
0869                     b = cl->buf;
0870                     ngx_memzero(b, sizeof(ngx_buf_t));
0871 
0872                 } else {
0873                     b = ngx_calloc_buf(r->pool);
0874                     if (b == NULL) {
0875                         return NGX_ERROR;
0876                     }
0877 
0878                     cl = ngx_alloc_chain_link(r->pool);
0879                     if (cl == NULL) {
0880                         return NGX_ERROR;
0881                     }
0882 
0883                     cl->buf = b;
0884                 }
0885 
0886                 b->sync = 1;
0887 
0888                 cl->next = NULL;
0889                 *ctx->last_out = cl;
0890                 ctx->last_out = &cl->next;
0891             }
0892 
0893             b->last_buf = ctx->buf->last_buf;
0894             b->shadow = ctx->buf;
0895 
0896             if (slcf->ignore_recycled_buffers == 0)  {
0897                 b->recycled = ctx->buf->recycled;
0898             }
0899         }
0900 
0901         ctx->buf = NULL;
0902 
0903         ctx->saved = ctx->looked;
0904     }
0905 
0906     if (ctx->out == NULL && ctx->busy == NULL) {
0907         return NGX_OK;
0908     }
0909 
0910     return ngx_http_ssi_output(r, ctx);
0911 }
0912 
0913 
0914 static ngx_int_t
0915 ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
0916 {
0917     ngx_int_t     rc;
0918     ngx_buf_t    *b;
0919     ngx_chain_t  *cl;
0920 
0921 #if 1
0922     b = NULL;
0923     for (cl = ctx->out; cl; cl = cl->next) {
0924         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0925                        "ssi out: %p %p", cl->buf, cl->buf->pos);
0926         if (cl->buf == b) {
0927             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
0928                           "the same buf was used in ssi");
0929             ngx_debug_point();
0930             return NGX_ERROR;
0931         }
0932         b = cl->buf;
0933     }
0934 #endif
0935 
0936     rc = ngx_http_next_body_filter(r, ctx->out);
0937 
0938     if (ctx->busy == NULL) {
0939         ctx->busy = ctx->out;
0940 
0941     } else {
0942         for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
0943         cl->next = ctx->out;
0944     }
0945 
0946     ctx->out = NULL;
0947     ctx->last_out = &ctx->out;
0948 
0949     while (ctx->busy) {
0950 
0951         cl = ctx->busy;
0952         b = cl->buf;
0953 
0954         if (ngx_buf_size(b) != 0) {
0955             break;
0956         }
0957 
0958         if (b->shadow) {
0959             b->shadow->pos = b->shadow->last;
0960         }
0961 
0962         ctx->busy = cl->next;
0963 
0964         if (ngx_buf_in_memory(b) || b->in_file) {
0965             /* add data bufs only to the free buf chain */
0966 
0967             cl->next = ctx->free;
0968             ctx->free = cl;
0969         }
0970     }
0971 
0972     ngx_http_ssi_buffered(r, ctx);
0973 
0974     return rc;
0975 }
0976 
0977 
0978 static void
0979 ngx_http_ssi_buffered(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
0980 {
0981     if (ctx->in || ctx->buf) {
0982         r->buffered |= NGX_HTTP_SSI_BUFFERED;
0983 
0984     } else {
0985         r->buffered &= ~NGX_HTTP_SSI_BUFFERED;
0986     }
0987 }
0988 
0989 
0990 static ngx_int_t
0991 ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
0992 {
0993     u_char                *p, *value, *last, *copy_end, ch;
0994     size_t                 looked;
0995     ngx_http_ssi_state_e   state;
0996 
0997     state = ctx->state;
0998     looked = ctx->looked;
0999     last = ctx->buf->last;
1000     copy_end = ctx->copy_end;
1001 
1002     for (p = ctx->pos; p < last; p++) {
1003 
1004         ch = *p;
1005 
1006         if (state == ssi_start_state) {
1007 
1008             /* the tight loop */
1009 
1010             for ( ;; ) {
1011                 if (ch == '<') {
1012                     copy_end = p;
1013                     looked = 1;
1014                     state = ssi_tag_state;
1015 
1016                     goto tag_started;
1017                 }
1018 
1019                 if (++p == last) {
1020                     break;
1021                 }
1022 
1023                 ch = *p;
1024             }
1025 
1026             ctx->state = state;
1027             ctx->pos = p;
1028             ctx->looked = looked;
1029             ctx->copy_end = p;
1030 
1031             if (ctx->copy_start == NULL) {
1032                 ctx->copy_start = ctx->buf->pos;
1033             }
1034 
1035             return NGX_AGAIN;
1036 
1037         tag_started:
1038 
1039             continue;
1040         }
1041 
1042         switch (state) {
1043 
1044         case ssi_start_state:
1045             /* not reached */
1046             break;
1047 
1048         case ssi_tag_state:
1049             switch (ch) {
1050             case '!':
1051                 looked = 2;
1052                 state = ssi_comment0_state;
1053                 break;
1054 
1055             case '<':
1056                 copy_end = p;
1057                 break;
1058 
1059             default:
1060                 copy_end = p;
1061                 looked = 0;
1062                 state = ssi_start_state;
1063                 break;
1064             }
1065 
1066             break;
1067 
1068         case ssi_comment0_state:
1069             switch (ch) {
1070             case '-':
1071                 looked = 3;
1072                 state = ssi_comment1_state;
1073                 break;
1074 
1075             case '<':
1076                 copy_end = p;
1077                 looked = 1;
1078                 state = ssi_tag_state;
1079                 break;
1080 
1081             default:
1082                 copy_end = p;
1083                 looked = 0;
1084                 state = ssi_start_state;
1085                 break;
1086             }
1087 
1088             break;
1089 
1090         case ssi_comment1_state:
1091             switch (ch) {
1092             case '-':
1093                 looked = 4;
1094                 state = ssi_sharp_state;
1095                 break;
1096 
1097             case '<':
1098                 copy_end = p;
1099                 looked = 1;
1100                 state = ssi_tag_state;
1101                 break;
1102 
1103             default:
1104                 copy_end = p;
1105                 looked = 0;
1106                 state = ssi_start_state;
1107                 break;
1108             }
1109 
1110             break;
1111 
1112         case ssi_sharp_state:
1113             switch (ch) {
1114             case '#':
1115                 if (p - ctx->pos < 4) {
1116                     ctx->saved = 0;
1117                 }
1118                 looked = 0;
1119                 state = ssi_precommand_state;
1120                 break;
1121 
1122             case '<':
1123                 copy_end = p;
1124                 looked = 1;
1125                 state = ssi_tag_state;
1126                 break;
1127 
1128             default:
1129                 copy_end = p;
1130                 looked = 0;
1131                 state = ssi_start_state;
1132                 break;
1133             }
1134 
1135             break;
1136 
1137         case ssi_precommand_state:
1138             switch (ch) {
1139             case ' ':
1140             case CR:
1141             case LF:
1142             case '\t':
1143                 break;
1144 
1145             default:
1146                 ctx->command.len = 1;
1147                 ctx->command.data = ngx_pnalloc(r->pool,
1148                                                 NGX_HTTP_SSI_COMMAND_LEN);
1149                 if (ctx->command.data == NULL) {
1150                     return NGX_ERROR;
1151                 }
1152 
1153                 ctx->command.data[0] = ch;
1154 
1155                 ctx->key = 0;
1156                 ctx->key = ngx_hash(ctx->key, ch);
1157 
1158                 ctx->params.nelts = 0;
1159 
1160                 state = ssi_command_state;
1161                 break;
1162             }
1163 
1164             break;
1165 
1166         case ssi_command_state:
1167             switch (ch) {
1168             case ' ':
1169             case CR:
1170             case LF:
1171             case '\t':
1172                 state = ssi_preparam_state;
1173                 break;
1174 
1175             case '-':
1176                 state = ssi_comment_end0_state;
1177                 break;
1178 
1179             default:
1180                 if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {
1181                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1182                                   "the \"%V%c...\" SSI command is too long",
1183                                   &ctx->command, ch);
1184 
1185                     state = ssi_error_state;
1186                     break;
1187                 }
1188 
1189                 ctx->command.data[ctx->command.len++] = ch;
1190                 ctx->key = ngx_hash(ctx->key, ch);
1191             }
1192 
1193             break;
1194 
1195         case ssi_preparam_state:
1196             switch (ch) {
1197             case ' ':
1198             case CR:
1199             case LF:
1200             case '\t':
1201                 break;
1202 
1203             case '-':
1204                 state = ssi_comment_end0_state;
1205                 break;
1206 
1207             default:
1208                 ctx->param = ngx_array_push(&ctx->params);
1209                 if (ctx->param == NULL) {
1210                     return NGX_ERROR;
1211                 }
1212 
1213                 ctx->param->key.len = 1;
1214                 ctx->param->key.data = ngx_pnalloc(r->pool,
1215                                                    NGX_HTTP_SSI_PARAM_LEN);
1216                 if (ctx->param->key.data == NULL) {
1217                     return NGX_ERROR;
1218                 }
1219 
1220                 ctx->param->key.data[0] = ch;
1221 
1222                 ctx->param->value.len = 0;
1223 
1224                 if (ctx->value_buf == NULL) {
1225                     ctx->param->value.data = ngx_pnalloc(r->pool,
1226                                                          ctx->value_len + 1);
1227                     if (ctx->param->value.data == NULL) {
1228                         return NGX_ERROR;
1229                     }
1230 
1231                 } else {
1232                     ctx->param->value.data = ctx->value_buf;
1233                 }
1234 
1235                 state = ssi_param_state;
1236                 break;
1237             }
1238 
1239             break;
1240 
1241         case ssi_param_state:
1242             switch (ch) {
1243             case ' ':
1244             case CR:
1245             case LF:
1246             case '\t':
1247                 state = ssi_preequal_state;
1248                 break;
1249 
1250             case '=':
1251                 state = ssi_prevalue_state;
1252                 break;
1253 
1254             case '-':
1255                 state = ssi_error_end0_state;
1256 
1257                 ctx->param->key.data[ctx->param->key.len++] = ch;
1258                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1259                               "invalid \"%V\" parameter in \"%V\" SSI command",
1260                               &ctx->param->key, &ctx->command);
1261                 break;
1262 
1263             default:
1264                 if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {
1265                     state = ssi_error_state;
1266                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1267                                   "too long \"%V%c...\" parameter in "
1268                                   "\"%V\" SSI command",
1269                                   &ctx->param->key, ch, &ctx->command);
1270                     break;
1271                 }
1272 
1273                 ctx->param->key.data[ctx->param->key.len++] = ch;
1274             }
1275 
1276             break;
1277 
1278         case ssi_preequal_state:
1279             switch (ch) {
1280             case ' ':
1281             case CR:
1282             case LF:
1283             case '\t':
1284                 break;
1285 
1286             case '=':
1287                 state = ssi_prevalue_state;
1288                 break;
1289 
1290             default:
1291                 if (ch == '-') {
1292                     state = ssi_error_end0_state;
1293                 } else {
1294                     state = ssi_error_state;
1295                 }
1296 
1297                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1298                               "unexpected \"%c\" symbol after \"%V\" "
1299                               "parameter in \"%V\" SSI command",
1300                               ch, &ctx->param->key, &ctx->command);
1301                 break;
1302             }
1303 
1304             break;
1305 
1306         case ssi_prevalue_state:
1307             switch (ch) {
1308             case ' ':
1309             case CR:
1310             case LF:
1311             case '\t':
1312                 break;
1313 
1314             case '"':
1315                 state = ssi_double_quoted_value_state;
1316                 break;
1317 
1318             case '\'':
1319                 state = ssi_quoted_value_state;
1320                 break;
1321 
1322             default:
1323                 if (ch == '-') {
1324                     state = ssi_error_end0_state;
1325                 } else {
1326                     state = ssi_error_state;
1327                 }
1328 
1329                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1330                               "unexpected \"%c\" symbol before value of "
1331                               "\"%V\" parameter in \"%V\" SSI command",
1332                               ch, &ctx->param->key, &ctx->command);
1333                 break;
1334             }
1335 
1336             break;
1337 
1338         case ssi_double_quoted_value_state:
1339             switch (ch) {
1340             case '"':
1341                 state = ssi_postparam_state;
1342                 break;
1343 
1344             case '\\':
1345                 ctx->saved_state = ssi_double_quoted_value_state;
1346                 state = ssi_quoted_symbol_state;
1347 
1348                 /* fall through */
1349 
1350             default:
1351                 if (ctx->param->value.len == ctx->value_len) {
1352                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1353                                   "too long \"%V%c...\" value of \"%V\" "
1354                                   "parameter in \"%V\" SSI command",
1355                                   &ctx->param->value, ch, &ctx->param->key,
1356                                   &ctx->command);
1357                     state = ssi_error_state;
1358                     break;
1359                 }
1360 
1361                 ctx->param->value.data[ctx->param->value.len++] = ch;
1362             }
1363 
1364             break;
1365 
1366         case ssi_quoted_value_state:
1367             switch (ch) {
1368             case '\'':
1369                 state = ssi_postparam_state;
1370                 break;
1371 
1372             case '\\':
1373                 ctx->saved_state = ssi_quoted_value_state;
1374                 state = ssi_quoted_symbol_state;
1375 
1376                 /* fall through */
1377 
1378             default:
1379                 if (ctx->param->value.len == ctx->value_len) {
1380                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1381                                   "too long \"%V%c...\" value of \"%V\" "
1382                                   "parameter in \"%V\" SSI command",
1383                                   &ctx->param->value, ch, &ctx->param->key,
1384                                   &ctx->command);
1385                     state = ssi_error_state;
1386                     break;
1387                 }
1388 
1389                 ctx->param->value.data[ctx->param->value.len++] = ch;
1390             }
1391 
1392             break;
1393 
1394         case ssi_quoted_symbol_state:
1395             state = ctx->saved_state;
1396 
1397             if (ctx->param->value.len == ctx->value_len) {
1398                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1399                               "too long \"%V%c...\" value of \"%V\" "
1400                               "parameter in \"%V\" SSI command",
1401                               &ctx->param->value, ch, &ctx->param->key,
1402                               &ctx->command);
1403                 state = ssi_error_state;
1404                 break;
1405             }
1406 
1407             ctx->param->value.data[ctx->param->value.len++] = ch;
1408 
1409             break;
1410 
1411         case ssi_postparam_state:
1412 
1413             if (ctx->param->value.len + 1 < ctx->value_len / 2) {
1414                 value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);
1415                 if (value == NULL) {
1416                     return NGX_ERROR;
1417                 }
1418 
1419                 ngx_memcpy(value, ctx->param->value.data,
1420                            ctx->param->value.len);
1421 
1422                 ctx->value_buf = ctx->param->value.data;
1423                 ctx->param->value.data = value;
1424 
1425             } else {
1426                 ctx->value_buf = NULL;
1427             }
1428 
1429             switch (ch) {
1430             case ' ':
1431             case CR:
1432             case LF:
1433             case '\t':
1434                 state = ssi_preparam_state;
1435                 break;
1436 
1437             case '-':
1438                 state = ssi_comment_end0_state;
1439                 break;
1440 
1441             default:
1442                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1443                               "unexpected \"%c\" symbol after \"%V\" value "
1444                               "of \"%V\" parameter in \"%V\" SSI command",
1445                               ch, &ctx->param->value, &ctx->param->key,
1446                               &ctx->command);
1447                 state = ssi_error_state;
1448                 break;
1449             }
1450 
1451             break;
1452 
1453         case ssi_comment_end0_state:
1454             switch (ch) {
1455             case '-':
1456                 state = ssi_comment_end1_state;
1457                 break;
1458 
1459             default:
1460                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1461                               "unexpected \"%c\" symbol in \"%V\" SSI command",
1462                               ch, &ctx->command);
1463                 state = ssi_error_state;
1464                 break;
1465             }
1466 
1467             break;
1468 
1469         case ssi_comment_end1_state:
1470             switch (ch) {
1471             case '>':
1472                 ctx->state = ssi_start_state;
1473                 ctx->pos = p + 1;
1474                 ctx->looked = looked;
1475                 ctx->copy_end = copy_end;
1476 
1477                 if (ctx->copy_start == NULL && copy_end) {
1478                     ctx->copy_start = ctx->buf->pos;
1479                 }
1480 
1481                 return NGX_OK;
1482 
1483             default:
1484                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1485                               "unexpected \"%c\" symbol in \"%V\" SSI command",
1486                               ch, &ctx->command);
1487                 state = ssi_error_state;
1488                 break;
1489             }
1490 
1491             break;
1492 
1493         case ssi_error_state:
1494             switch (ch) {
1495             case '-':
1496                 state = ssi_error_end0_state;
1497                 break;
1498 
1499             default:
1500                 break;
1501             }
1502 
1503             break;
1504 
1505         case ssi_error_end0_state:
1506             switch (ch) {
1507             case '-':
1508                 state = ssi_error_end1_state;
1509                 break;
1510 
1511             default:
1512                 state = ssi_error_state;
1513                 break;
1514             }
1515 
1516             break;
1517 
1518         case ssi_error_end1_state:
1519             switch (ch) {
1520             case '>':
1521                 ctx->state = ssi_start_state;
1522                 ctx->pos = p + 1;
1523                 ctx->looked = looked;
1524                 ctx->copy_end = copy_end;
1525 
1526                 if (ctx->copy_start == NULL && copy_end) {
1527                     ctx->copy_start = ctx->buf->pos;
1528                 }
1529 
1530                 return NGX_HTTP_SSI_ERROR;
1531 
1532             default:
1533                 state = ssi_error_state;
1534                 break;
1535             }
1536 
1537             break;
1538         }
1539     }
1540 
1541     ctx->state = state;
1542     ctx->pos = p;
1543     ctx->looked = looked;
1544 
1545     ctx->copy_end = (state == ssi_start_state) ? p : copy_end;
1546 
1547     if (ctx->copy_start == NULL && ctx->copy_end) {
1548         ctx->copy_start = ctx->buf->pos;
1549     }
1550 
1551     return NGX_AGAIN;
1552 }
1553 
1554 
1555 static ngx_str_t *
1556 ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name,
1557     ngx_uint_t key)
1558 {
1559     ngx_uint_t           i;
1560     ngx_list_part_t     *part;
1561     ngx_http_ssi_var_t  *var;
1562     ngx_http_ssi_ctx_t  *ctx;
1563 
1564     ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
1565 
1566 #if (NGX_PCRE)
1567     {
1568     ngx_str_t  *value;
1569 
1570     if (key >= '0' && key <= '9') {
1571         i = key - '0';
1572 
1573         if (i < ctx->ncaptures) {
1574             value = ngx_palloc(r->pool, sizeof(ngx_str_t));
1575             if (value == NULL) {
1576                 return NULL;
1577             }
1578 
1579             i *= 2;
1580 
1581             value->data = ctx->captures_data + ctx->captures[i];
1582             value->len = ctx->captures[i + 1] - ctx->captures[i];
1583 
1584             return value;
1585         }
1586     }
1587     }
1588 #endif
1589 
1590     if (ctx->variables == NULL) {
1591         return NULL;
1592     }
1593 
1594     part = &ctx->variables->part;
1595     var = part->elts;
1596 
1597     for (i = 0; /* void */ ; i++) {
1598 
1599         if (i >= part->nelts) {
1600             if (part->next == NULL) {
1601                 break;
1602             }
1603 
1604             part = part->next;
1605             var = part->elts;
1606             i = 0;
1607         }
1608 
1609         if (name->len != var[i].name.len) {
1610             continue;
1611         }
1612 
1613         if (key != var[i].key) {
1614             continue;
1615         }
1616 
1617         if (ngx_strncmp(name->data, var[i].name.data, name->len) == 0) {
1618             return &var[i].value;
1619         }
1620     }
1621 
1622     return NULL;
1623 }
1624 
1625 
1626 static ngx_int_t
1627 ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1628     ngx_str_t *text, ngx_uint_t flags)
1629 {
1630     u_char                      ch, *p, **value, *data, *part_data;
1631     size_t                     *size, len, prefix, part_len;
1632     ngx_str_t                   var, *val;
1633     ngx_uint_t                  i, n, bracket, quoted, key;
1634     ngx_array_t                 lengths, values;
1635     ngx_http_variable_value_t  *vv;
1636 
1637     n = ngx_http_script_variables_count(text);
1638 
1639     if (n == 0) {
1640 
1641         data = text->data;
1642         p = data;
1643 
1644         if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') {
1645 
1646             for (prefix = r->uri.len; prefix; prefix--) {
1647                 if (r->uri.data[prefix - 1] == '/') {
1648                     break;
1649                 }
1650             }
1651 
1652             if (prefix) {
1653                 len = prefix + text->len;
1654 
1655                 data = ngx_pnalloc(r->pool, len);
1656                 if (data == NULL) {
1657                     return NGX_ERROR;
1658                 }
1659 
1660                 p = ngx_copy(data, r->uri.data, prefix);
1661             }
1662         }
1663 
1664         quoted = 0;
1665 
1666         for (i = 0; i < text->len; i++) {
1667             ch = text->data[i];
1668 
1669             if (!quoted) {
1670 
1671                 if (ch == '\\') {
1672                     quoted = 1;
1673                     continue;
1674                 }
1675 
1676             } else {
1677                 quoted = 0;
1678 
1679                 if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
1680                     *p++ = '\\';
1681                 }
1682             }
1683 
1684             *p++ = ch;
1685         }
1686 
1687         text->len = p - data;
1688         text->data = data;
1689 
1690         return NGX_OK;
1691     }
1692 
1693     if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
1694         return NGX_ERROR;
1695     }
1696 
1697     if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
1698         return NGX_ERROR;
1699     }
1700 
1701     len = 0;
1702     i = 0;
1703 
1704     while (i < text->len) {
1705 
1706         if (text->data[i] == '$') {
1707 
1708             var.len = 0;
1709 
1710             if (++i == text->len) {
1711                 goto invalid_variable;
1712             }
1713 
1714             if (text->data[i] == '{') {
1715                 bracket = 1;
1716 
1717                 if (++i == text->len) {
1718                     goto invalid_variable;
1719                 }
1720 
1721                 var.data = &text->data[i];
1722 
1723             } else {
1724                 bracket = 0;
1725                 var.data = &text->data[i];
1726             }
1727 
1728             for ( /* void */ ; i < text->len; i++, var.len++) {
1729                 ch = text->data[i];
1730 
1731                 if (ch == '}' && bracket) {
1732                     i++;
1733                     bracket = 0;
1734                     break;
1735                 }
1736 
1737                 if ((ch >= 'A' && ch <= 'Z')
1738                     || (ch >= 'a' && ch <= 'z')
1739                     || (ch >= '0' && ch <= '9')
1740                     || ch == '_')
1741                 {
1742                     continue;
1743                 }
1744 
1745                 break;
1746             }
1747 
1748             if (bracket) {
1749                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1750                               "the closing bracket in \"%V\" "
1751                               "variable is missing", &var);
1752                 return NGX_HTTP_SSI_ERROR;
1753             }
1754 
1755             if (var.len == 0) {
1756                 goto invalid_variable;
1757             }
1758 
1759             key = ngx_hash_strlow(var.data, var.data, var.len);
1760 
1761             val = ngx_http_ssi_get_variable(r, &var, key);
1762 
1763             if (val == NULL) {
1764                 vv = ngx_http_get_variable(r, &var, key);
1765                 if (vv == NULL) {
1766                     return NGX_ERROR;
1767                 }
1768 
1769                 if (vv->not_found) {
1770                     continue;
1771                 }
1772 
1773                 part_data = vv->data;
1774                 part_len = vv->len;
1775 
1776             } else {
1777                 part_data = val->data;
1778                 part_len = val->len;
1779             }
1780 
1781         } else {
1782             part_data = &text->data[i];
1783             quoted = 0;
1784 
1785             for (p = part_data; i < text->len; i++) {
1786                 ch = text->data[i];
1787 
1788                 if (!quoted) {
1789 
1790                     if (ch == '\\') {
1791                         quoted = 1;
1792                         continue;
1793                     }
1794 
1795                     if (ch == '$') {
1796                         break;
1797                     }
1798 
1799                 } else {
1800                     quoted = 0;
1801 
1802                     if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
1803                         *p++ = '\\';
1804                     }
1805                 }
1806 
1807                 *p++ = ch;
1808             }
1809 
1810             part_len = p - part_data;
1811         }
1812 
1813         len += part_len;
1814 
1815         size = ngx_array_push(&lengths);
1816         if (size == NULL) {
1817             return NGX_ERROR;
1818         }
1819 
1820         *size = part_len;
1821 
1822         value = ngx_array_push(&values);
1823         if (value == NULL) {
1824             return NGX_ERROR;
1825         }
1826 
1827         *value = part_data;
1828     }
1829 
1830     prefix = 0;
1831 
1832     size = lengths.elts;
1833     value = values.elts;
1834 
1835     if (flags & NGX_HTTP_SSI_ADD_PREFIX) {
1836         for (i = 0; i < values.nelts; i++) {
1837             if (size[i] != 0) {
1838                 if (*value[i] != '/') {
1839                     for (prefix = r->uri.len; prefix; prefix--) {
1840                         if (r->uri.data[prefix - 1] == '/') {
1841                             len += prefix;
1842                             break;
1843                         }
1844                     }
1845                 }
1846 
1847                 break;
1848             }
1849         }
1850     }
1851 
1852     p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
1853     if (p == NULL) {
1854         return NGX_ERROR;
1855     }
1856 
1857     text->len = len;
1858     text->data = p;
1859 
1860     p = ngx_copy(p, r->uri.data, prefix);
1861 
1862     for (i = 0; i < values.nelts; i++) {
1863         p = ngx_copy(p, value[i], size[i]);
1864     }
1865 
1866     return NGX_OK;
1867 
1868 invalid_variable:
1869 
1870     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1871                   "invalid variable name in \"%V\"", text);
1872 
1873     return NGX_HTTP_SSI_ERROR;
1874 }
1875 
1876 
1877 static ngx_int_t
1878 ngx_http_ssi_regex_match(ngx_http_request_t *r, ngx_str_t *pattern,
1879     ngx_str_t *str)
1880 {
1881 #if (NGX_PCRE)
1882     int                   rc, *captures;
1883     u_char               *p, errstr[NGX_MAX_CONF_ERRSTR];
1884     size_t                size;
1885     ngx_str_t            *vv, name, value;
1886     ngx_uint_t            i, n, key;
1887     ngx_http_ssi_ctx_t   *ctx;
1888     ngx_http_ssi_var_t   *var;
1889     ngx_regex_compile_t   rgc;
1890 
1891     ngx_memzero(&rgc, sizeof(ngx_regex_compile_t));
1892 
1893     rgc.pattern = *pattern;
1894     rgc.pool = r->pool;
1895     rgc.err.len = NGX_MAX_CONF_ERRSTR;
1896     rgc.err.data = errstr;
1897 
1898     if (ngx_regex_compile(&rgc) != NGX_OK) {
1899         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%V", &rgc.err);
1900         return NGX_HTTP_SSI_ERROR;
1901     }
1902 
1903     n = (rgc.captures + 1) * 3;
1904 
1905     captures = ngx_palloc(r->pool, n * sizeof(int));
1906     if (captures == NULL) {
1907         return NGX_ERROR;
1908     }
1909 
1910     rc = ngx_regex_exec(rgc.regex, str, captures, n);
1911 
1912     if (rc < NGX_REGEX_NO_MATCHED) {
1913         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
1914                       ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"",
1915                       rc, str, pattern);
1916         return NGX_HTTP_SSI_ERROR;
1917     }
1918 
1919     if (rc == NGX_REGEX_NO_MATCHED) {
1920         return NGX_DECLINED;
1921     }
1922 
1923     ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
1924 
1925     ctx->ncaptures = rc;
1926     ctx->captures = captures;
1927     ctx->captures_data = str->data;
1928 
1929     if (rgc.named_captures > 0) {
1930 
1931         if (ctx->variables == NULL) {
1932             ctx->variables = ngx_list_create(r->pool, 4,
1933                                              sizeof(ngx_http_ssi_var_t));
1934             if (ctx->variables == NULL) {
1935                 return NGX_ERROR;
1936             }
1937         }
1938 
1939         size = rgc.name_size;
1940         p = rgc.names;
1941 
1942         for (i = 0; i < (ngx_uint_t) rgc.named_captures; i++, p += size) {
1943 
1944             name.data = &p[2];
1945             name.len = ngx_strlen(name.data);
1946 
1947             n = 2 * ((p[0] << 8) + p[1]);
1948 
1949             value.data = &str->data[captures[n]];
1950             value.len = captures[n + 1] - captures[n];
1951 
1952             key = ngx_hash_strlow(name.data, name.data, name.len);
1953 
1954             vv = ngx_http_ssi_get_variable(r, &name, key);
1955 
1956             if (vv) {
1957                 *vv = value;
1958                 continue;
1959             }
1960 
1961             var = ngx_list_push(ctx->variables);
1962             if (var == NULL) {
1963                 return NGX_ERROR;
1964             }
1965 
1966             var->name = name;
1967             var->key = key;
1968             var->value = value;
1969         }
1970     }
1971 
1972     return NGX_OK;
1973 
1974 #else
1975 
1976     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
1977                   "the using of the regex \"%V\" in SSI requires PCRE library",
1978                   pattern);
1979     return NGX_HTTP_SSI_ERROR;
1980 
1981 #endif
1982 }
1983 
1984 
1985 static ngx_int_t
1986 ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1987     ngx_str_t **params)
1988 {
1989     ngx_int_t                    rc;
1990     ngx_str_t                   *uri, *file, *wait, *set, *stub, args;
1991     ngx_buf_t                   *b;
1992     ngx_uint_t                   flags, i, key;
1993     ngx_chain_t                 *cl, *tl, **ll, *out;
1994     ngx_http_request_t          *sr;
1995     ngx_http_ssi_var_t          *var;
1996     ngx_http_ssi_ctx_t          *mctx;
1997     ngx_http_ssi_block_t        *bl;
1998     ngx_http_post_subrequest_t  *psr;
1999 
2000     uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
2001     file = params[NGX_HTTP_SSI_INCLUDE_FILE];
2002     wait = params[NGX_HTTP_SSI_INCLUDE_WAIT];
2003     set = params[NGX_HTTP_SSI_INCLUDE_SET];
2004     stub = params[NGX_HTTP_SSI_INCLUDE_STUB];
2005 
2006     if (uri && file) {
2007         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2008                       "inclusion may be either virtual=\"%V\" or file=\"%V\"",
2009                       uri, file);
2010         return NGX_HTTP_SSI_ERROR;
2011     }
2012 
2013     if (uri == NULL && file == NULL) {
2014         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2015                       "no parameter in \"include\" SSI command");
2016         return NGX_HTTP_SSI_ERROR;
2017     }
2018 
2019     if (set && stub) {
2020         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2021                       "\"set\" and \"stub\" cannot be used together "
2022                       "in \"include\" SSI command");
2023         return NGX_HTTP_SSI_ERROR;
2024     }
2025 
2026     if (wait) {
2027         if (uri == NULL) {
2028             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2029                           "\"wait\" cannot be used with file=\"%V\"", file);
2030             return NGX_HTTP_SSI_ERROR;
2031         }
2032 
2033         if (wait->len == 2
2034             && ngx_strncasecmp(wait->data, (u_char *) "no", 2) == 0)
2035         {
2036             wait = NULL;
2037 
2038         } else if (wait->len != 3
2039                    || ngx_strncasecmp(wait->data, (u_char *) "yes", 3) != 0)
2040         {
2041             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2042                           "invalid value \"%V\" in the \"wait\" parameter",
2043                           wait);
2044             return NGX_HTTP_SSI_ERROR;
2045         }
2046     }
2047 
2048     if (uri == NULL) {
2049         uri = file;
2050         wait = (ngx_str_t *) -1;
2051     }
2052 
2053     rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);
2054 
2055     if (rc != NGX_OK) {
2056         return rc;
2057     }
2058 
2059     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2060                    "ssi include: \"%V\"", uri);
2061 
2062     ngx_str_null(&args);
2063     flags = NGX_HTTP_LOG_UNSAFE;
2064 
2065     if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
2066         return NGX_HTTP_SSI_ERROR;
2067     }
2068 
2069     psr = NULL;
2070 
2071     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
2072 
2073     if (stub) {
2074         if (mctx->blocks) {
2075             bl = mctx->blocks->elts;
2076             for (i = 0; i < mctx->blocks->nelts; i++) {
2077                 if (stub->len == bl[i].name.len
2078                     && ngx_strncmp(stub->data, bl[i].name.data, stub->len) == 0)
2079                 {
2080                     goto found;
2081                 }
2082             }
2083         }
2084 
2085         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2086                       "\"stub\"=\"%V\" for \"include\" not found", stub);
2087         return NGX_HTTP_SSI_ERROR;
2088 
2089     found:
2090 
2091         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
2092         if (psr == NULL) {
2093             return NGX_ERROR;
2094         }
2095 
2096         psr->handler = ngx_http_ssi_stub_output;
2097 
2098         if (bl[i].count++) {
2099 
2100             out = NULL;
2101             ll = &out;
2102 
2103             for (tl = bl[i].bufs; tl; tl = tl->next) {
2104 
2105                 if (ctx->free) {
2106                     cl = ctx->free;
2107                     ctx->free = ctx->free->next;
2108                     b = cl->buf;
2109 
2110                 } else {
2111                     b = ngx_alloc_buf(r->pool);
2112                     if (b == NULL) {
2113                         return NGX_ERROR;
2114                     }
2115 
2116                     cl = ngx_alloc_chain_link(r->pool);
2117                     if (cl == NULL) {
2118                         return NGX_ERROR;
2119                     }
2120 
2121                     cl->buf = b;
2122                 }
2123 
2124                 ngx_memcpy(b, tl->buf, sizeof(ngx_buf_t));
2125 
2126                 b->pos = b->start;
2127 
2128                 *ll = cl;
2129                 cl->next = NULL;
2130                 ll = &cl->next;
2131             }
2132 
2133             psr->data = out;
2134 
2135         } else {
2136             psr->data = bl[i].bufs;
2137         }
2138     }
2139 
2140     if (wait) {
2141         flags |= NGX_HTTP_SUBREQUEST_WAITED;
2142     }
2143 
2144     if (set) {
2145         key = ngx_hash_strlow(set->data, set->data, set->len);
2146 
2147         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
2148         if (psr == NULL) {
2149             return NGX_ERROR;
2150         }
2151 
2152         psr->handler = ngx_http_ssi_set_variable;
2153         psr->data = ngx_http_ssi_get_variable(r, set, key);
2154 
2155         if (psr->data == NULL) {
2156 
2157             if (mctx->variables == NULL) {
2158                 mctx->variables = ngx_list_create(r->pool, 4,
2159                                                   sizeof(ngx_http_ssi_var_t));
2160                 if (mctx->variables == NULL) {
2161                     return NGX_ERROR;
2162                 }
2163             }
2164 
2165             var = ngx_list_push(mctx->variables);
2166             if (var == NULL) {
2167                 return NGX_ERROR;
2168             }
2169 
2170             var->name = *set;
2171             var->key = key;
2172             var->value = ngx_http_ssi_null_string;
2173             psr->data = &var->value;
2174         }
2175 
2176         flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;
2177     }
2178 
2179     if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {
2180         return NGX_HTTP_SSI_ERROR;
2181     }
2182 
2183     if (wait == NULL && set == NULL) {
2184         return NGX_OK;
2185     }
2186 
2187     if (ctx->wait == NULL) {
2188         ctx->wait = sr;
2189 
2190         return NGX_AGAIN;
2191 
2192     } else {
2193         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2194                       "can only wait for one subrequest at a time");
2195     }
2196 
2197     return NGX_OK;
2198 }
2199 
2200 
2201 static ngx_int_t
2202 ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc)
2203 {
2204     ngx_chain_t  *out;
2205 
2206     if (rc == NGX_ERROR || r->connection->error || r->request_output) {
2207         return rc;
2208     }
2209 
2210     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2211                    "ssi stub output: \"%V?%V\"", &r->uri, &r->args);
2212 
2213     out = data;
2214 
2215     if (!r->header_sent) {
2216         r->headers_out.content_type_len =
2217                                       r->parent->headers_out.content_type_len;
2218         r->headers_out.content_type = r->parent->headers_out.content_type;
2219 
2220         if (ngx_http_send_header(r) == NGX_ERROR) {
2221             return NGX_ERROR;
2222         }
2223     }
2224 
2225     return ngx_http_output_filter(r, out);
2226 }
2227 
2228 
2229 static ngx_int_t
2230 ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc)
2231 {
2232     ngx_str_t  *value = data;
2233 
2234     if (r->headers_out.status < NGX_HTTP_SPECIAL_RESPONSE
2235         && r->out && r->out->buf)
2236     {
2237         value->len = r->out->buf->last - r->out->buf->pos;
2238         value->data = r->out->buf->pos;
2239     }
2240 
2241     return rc;
2242 }
2243 
2244 
2245 static ngx_int_t
2246 ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2247     ngx_str_t **params)
2248 {
2249     u_char                     *p;
2250     uintptr_t                   len;
2251     ngx_buf_t                  *b;
2252     ngx_str_t                  *var, *value, *enc, text;
2253     ngx_uint_t                  key;
2254     ngx_chain_t                *cl;
2255     ngx_http_variable_value_t  *vv;
2256 
2257     var = params[NGX_HTTP_SSI_ECHO_VAR];
2258 
2259     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2260                    "ssi echo \"%V\"", var);
2261 
2262     key = ngx_hash_strlow(var->data, var->data, var->len);
2263 
2264     value = ngx_http_ssi_get_variable(r, var, key);
2265 
2266     if (value == NULL) {
2267         vv = ngx_http_get_variable(r, var, key);
2268 
2269         if (vv == NULL) {
2270             return NGX_HTTP_SSI_ERROR;
2271         }
2272 
2273         if (!vv->not_found) {
2274             text.data = vv->data;
2275             text.len = vv->len;
2276             value = &text;
2277         }
2278     }
2279 
2280     if (value == NULL) {
2281         value = params[NGX_HTTP_SSI_ECHO_DEFAULT];
2282 
2283         if (value == NULL) {
2284             value = &ngx_http_ssi_none;
2285 
2286         } else if (value->len == 0) {
2287             return NGX_OK;
2288         }
2289 
2290     } else {
2291         if (value->len == 0) {
2292             return NGX_OK;
2293         }
2294     }
2295 
2296     enc = params[NGX_HTTP_SSI_ECHO_ENCODING];
2297 
2298     if (enc) {
2299         if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) {
2300 
2301             ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;
2302 
2303         } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {
2304 
2305             ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;
2306 
2307         } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {
2308 
2309             ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
2310 
2311         } else {
2312             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2313                           "unknown encoding \"%V\" in the \"echo\" command",
2314                           enc);
2315         }
2316     }
2317 
2318     p = value->data;
2319 
2320     switch (ctx->encoding) {
2321 
2322     case NGX_HTTP_SSI_URL_ENCODING:
2323         len = 2 * ngx_escape_uri(NULL, value->data, value->len,
2324                                  NGX_ESCAPE_HTML);
2325 
2326         if (len) {
2327             p = ngx_pnalloc(r->pool, value->len + len);
2328             if (p == NULL) {
2329                 return NGX_HTTP_SSI_ERROR;
2330             }
2331 
2332             (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);
2333         }
2334 
2335         len += value->len;
2336         break;
2337 
2338     case NGX_HTTP_SSI_ENTITY_ENCODING:
2339         len = ngx_escape_html(NULL, value->data, value->len);
2340 
2341         if (len) {
2342             p = ngx_pnalloc(r->pool, value->len + len);
2343             if (p == NULL) {
2344                 return NGX_HTTP_SSI_ERROR;
2345             }
2346 
2347             (void) ngx_escape_html(p, value->data, value->len);
2348         }
2349 
2350         len += value->len;
2351         break;
2352 
2353     default: /* NGX_HTTP_SSI_NO_ENCODING */
2354         len = value->len;
2355         break;
2356     }
2357 
2358     b = ngx_calloc_buf(r->pool);
2359     if (b == NULL) {
2360         return NGX_HTTP_SSI_ERROR;
2361     }
2362 
2363     cl = ngx_alloc_chain_link(r->pool);
2364     if (cl == NULL) {
2365         return NGX_HTTP_SSI_ERROR;
2366     }
2367 
2368     b->memory = 1;
2369     b->pos = p;
2370     b->last = p + len;
2371 
2372     cl->buf = b;
2373     cl->next = NULL;
2374     *ctx->last_out = cl;
2375     ctx->last_out = &cl->next;
2376 
2377     return NGX_OK;
2378 }
2379 
2380 
2381 static ngx_int_t
2382 ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2383     ngx_str_t **params)
2384 {
2385     ngx_str_t  *value;
2386 
2387     value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];
2388 
2389     if (value) {
2390         ctx->timefmt.len = value->len;
2391         ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);
2392         if (ctx->timefmt.data == NULL) {
2393             return NGX_ERROR;
2394         }
2395 
2396         ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1);
2397     }
2398 
2399     value = params[NGX_HTTP_SSI_CONFIG_ERRMSG];
2400 
2401     if (value) {
2402         ctx->errmsg = *value;
2403     }
2404 
2405     return NGX_OK;
2406 }
2407 
2408 
2409 static ngx_int_t
2410 ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2411     ngx_str_t **params)
2412 {
2413     ngx_int_t            rc;
2414     ngx_str_t           *name, *value, *vv;
2415     ngx_uint_t           key;
2416     ngx_http_ssi_var_t  *var;
2417     ngx_http_ssi_ctx_t  *mctx;
2418 
2419     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
2420 
2421     if (mctx->variables == NULL) {
2422         mctx->variables = ngx_list_create(r->pool, 4,
2423                                           sizeof(ngx_http_ssi_var_t));
2424         if (mctx->variables == NULL) {
2425             return NGX_ERROR;
2426         }
2427     }
2428 
2429     name = params[NGX_HTTP_SSI_SET_VAR];
2430     value = params[NGX_HTTP_SSI_SET_VALUE];
2431 
2432     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2433                    "ssi set \"%V\" \"%V\"", name, value);
2434 
2435     rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);
2436 
2437     if (rc != NGX_OK) {
2438         return rc;
2439     }
2440 
2441     key = ngx_hash_strlow(name->data, name->data, name->len);
2442 
2443     vv = ngx_http_ssi_get_variable(r, name, key);
2444 
2445     if (vv) {
2446         *vv = *value;
2447         return NGX_OK;
2448     }
2449 
2450     var = ngx_list_push(mctx->variables);
2451     if (var == NULL) {
2452         return NGX_ERROR;
2453     }
2454 
2455     var->name = *name;
2456     var->key = key;
2457     var->value = *value;
2458 
2459     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2460                    "set: \"%V\"=\"%V\"", name, value);
2461 
2462     return NGX_OK;
2463 }
2464 
2465 
2466 static ngx_int_t
2467 ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2468     ngx_str_t **params)
2469 {
2470     u_char       *p, *last;
2471     ngx_str_t    *expr, left, right;
2472     ngx_int_t     rc;
2473     ngx_uint_t    negative, noregex, flags;
2474 
2475     if (ctx->command.len == 2) {
2476         if (ctx->conditional) {
2477             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2478                           "the \"if\" command inside the \"if\" command");
2479             return NGX_HTTP_SSI_ERROR;
2480         }
2481     }
2482 
2483     if (ctx->output_chosen) {
2484         ctx->output = 0;
2485         return NGX_OK;
2486     }
2487 
2488     expr = params[NGX_HTTP_SSI_IF_EXPR];
2489 
2490     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2491                    "ssi if expr=\"%V\"", expr);
2492 
2493     left.data = expr->data;
2494     last = expr->data + expr->len;
2495 
2496     for (p = left.data; p < last; p++) {
2497         if (*p >= 'A' && *p <= 'Z') {
2498             *p |= 0x20;
2499             continue;
2500         }
2501 
2502         if ((*p >= 'a' && *p <= 'z')
2503              || (*p >= '0' && *p <= '9')
2504              || *p == '$' || *p == '{' || *p == '}' || *p == '_'
2505              || *p == '"' || *p == '\'')
2506         {
2507             continue;
2508         }
2509 
2510         break;
2511     }
2512 
2513     left.len = p - left.data;
2514 
2515     while (p < last && *p == ' ') {
2516         p++;
2517     }
2518 
2519     flags = 0;
2520 
2521     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2522                    "left: \"%V\"", &left);
2523 
2524     rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);
2525 
2526     if (rc != NGX_OK) {
2527         return rc;
2528     }
2529 
2530     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2531                    "evaluated left: \"%V\"", &left);
2532 
2533     if (p == last) {
2534         if (left.len) {
2535             ctx->output = 1;
2536             ctx->output_chosen = 1;
2537 
2538         } else {
2539             ctx->output = 0;
2540         }
2541 
2542         ctx->conditional = NGX_HTTP_SSI_COND_IF;
2543 
2544         return NGX_OK;
2545     }
2546 
2547     if (p < last && *p == '=') {
2548         negative = 0;
2549         p++;
2550 
2551     } else if (p + 1 < last && *p == '!' && *(p + 1) == '=') {
2552         negative = 1;
2553         p += 2;
2554 
2555     } else {
2556         goto invalid_expression;
2557     }
2558 
2559     while (p < last && *p == ' ') {
2560         p++;
2561     }
2562 
2563     if (p < last - 1 && *p == '/') {
2564         if (*(last - 1) != '/') {
2565             goto invalid_expression;
2566         }
2567 
2568         noregex = 0;
2569         flags = NGX_HTTP_SSI_ADD_ZERO;
2570         last--;
2571         p++;
2572 
2573     } else {
2574         noregex = 1;
2575         flags = 0;
2576 
2577         if (p < last - 1 && p[0] == '\\' && p[1] == '/') {
2578             p++;
2579         }
2580     }
2581 
2582     right.len = last - p;
2583     right.data = p;
2584 
2585     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2586                    "right: \"%V\"", &right);
2587 
2588     rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);
2589 
2590     if (rc != NGX_OK) {
2591         return rc;
2592     }
2593 
2594     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2595                    "evaluated right: \"%V\"", &right);
2596 
2597     if (noregex) {
2598         if (left.len != right.len) {
2599             rc = -1;
2600 
2601         } else {
2602             rc = ngx_strncmp(left.data, right.data, right.len);
2603         }
2604 
2605     } else {
2606         right.data[right.len] = '\0';
2607 
2608         rc = ngx_http_ssi_regex_match(r, &right, &left);
2609 
2610         if (rc == NGX_OK) {
2611             rc = 0;
2612         } else if (rc == NGX_DECLINED) {
2613             rc = -1;
2614         } else {
2615             return rc;
2616         }
2617     }
2618 
2619     if ((rc == 0 && !negative) || (rc != 0 && negative)) {
2620         ctx->output = 1;
2621         ctx->output_chosen = 1;
2622 
2623     } else {
2624         ctx->output = 0;
2625     }
2626 
2627     ctx->conditional = NGX_HTTP_SSI_COND_IF;
2628 
2629     return NGX_OK;
2630 
2631 invalid_expression:
2632 
2633     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2634                   "invalid expression in \"%V\"", expr);
2635 
2636     return NGX_HTTP_SSI_ERROR;
2637 }
2638 
2639 
2640 static ngx_int_t
2641 ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2642     ngx_str_t **params)
2643 {
2644     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2645                    "ssi else");
2646 
2647     if (ctx->output_chosen) {
2648         ctx->output = 0;
2649     } else {
2650         ctx->output = 1;
2651     }
2652 
2653     ctx->conditional = NGX_HTTP_SSI_COND_ELSE;
2654 
2655     return NGX_OK;
2656 }
2657 
2658 
2659 static ngx_int_t
2660 ngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2661     ngx_str_t **params)
2662 {
2663     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2664                    "ssi endif");
2665 
2666     ctx->output = 1;
2667     ctx->output_chosen = 0;
2668     ctx->conditional = 0;
2669 
2670     return NGX_OK;
2671 }
2672 
2673 
2674 static ngx_int_t
2675 ngx_http_ssi_block(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2676     ngx_str_t **params)
2677 {
2678     ngx_http_ssi_ctx_t    *mctx;
2679     ngx_http_ssi_block_t  *bl;
2680 
2681     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2682                    "ssi block");
2683 
2684     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
2685 
2686     if (mctx->blocks == NULL) {
2687         mctx->blocks = ngx_array_create(r->pool, 4,
2688                                         sizeof(ngx_http_ssi_block_t));
2689         if (mctx->blocks == NULL) {
2690             return NGX_HTTP_SSI_ERROR;
2691         }
2692     }
2693 
2694     bl = ngx_array_push(mctx->blocks);
2695     if (bl == NULL) {
2696         return NGX_HTTP_SSI_ERROR;
2697     }
2698 
2699     bl->name = *params[NGX_HTTP_SSI_BLOCK_NAME];
2700     bl->bufs = NULL;
2701     bl->count = 0;
2702 
2703     ctx->output = 0;
2704     ctx->block = 1;
2705 
2706     return NGX_OK;
2707 }
2708 
2709 
2710 static ngx_int_t
2711 ngx_http_ssi_endblock(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2712     ngx_str_t **params)
2713 {
2714     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2715                    "ssi endblock");
2716 
2717     ctx->output = 1;
2718     ctx->block = 0;
2719 
2720     return NGX_OK;
2721 }
2722 
2723 
2724 static ngx_int_t
2725 ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
2726     ngx_http_variable_value_t *v, uintptr_t gmt)
2727 {
2728     time_t               now;
2729     ngx_http_ssi_ctx_t  *ctx;
2730     ngx_str_t           *timefmt;
2731     struct tm            tm;
2732     char                 buf[NGX_HTTP_SSI_DATE_LEN];
2733 
2734     v->valid = 1;
2735     v->no_cacheable = 0;
2736     v->not_found = 0;
2737 
2738     now = ngx_time();
2739 
2740     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
2741 
2742     timefmt = ctx ? &ctx->timefmt : &ngx_http_ssi_timefmt;
2743 
2744     if (timefmt->len == sizeof("%s") - 1
2745         && timefmt->data[0] == '%' && timefmt->data[1] == 's')
2746     {
2747         v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);
2748         if (v->data == NULL) {
2749             return NGX_ERROR;
2750         }
2751 
2752         v->len = ngx_sprintf(v->data, "%T", now) - v->data;
2753 
2754         return NGX_OK;
2755     }
2756 
2757     if (gmt) {
2758         ngx_libc_gmtime(now, &tm);
2759     } else {
2760         ngx_libc_localtime(now, &tm);
2761     }
2762 
2763     v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,
2764                       (char *) timefmt->data, &tm);
2765     if (v->len == 0) {
2766         return NGX_ERROR;
2767     }
2768 
2769     v->data = ngx_pnalloc(r->pool, v->len);
2770     if (v->data == NULL) {
2771         return NGX_ERROR;
2772     }
2773 
2774     ngx_memcpy(v->data, buf, v->len);
2775 
2776     return NGX_OK;
2777 }
2778 
2779 
2780 static ngx_int_t
2781 ngx_http_ssi_preconfiguration(ngx_conf_t *cf)
2782 {
2783     ngx_int_t                  rc;
2784     ngx_http_variable_t       *var, *v;
2785     ngx_http_ssi_command_t    *cmd;
2786     ngx_http_ssi_main_conf_t  *smcf;
2787 
2788     for (v = ngx_http_ssi_vars; v->name.len; v++) {
2789         var = ngx_http_add_variable(cf, &v->name, v->flags);
2790         if (var == NULL) {
2791             return NGX_ERROR;
2792         }
2793 
2794         var->get_handler = v->get_handler;
2795         var->data = v->data;
2796     }
2797 
2798     smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);
2799 
2800     for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {
2801         rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,
2802                               NGX_HASH_READONLY_KEY);
2803 
2804         if (rc == NGX_OK) {
2805             continue;
2806         }
2807 
2808         if (rc == NGX_BUSY) {
2809             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2810                                "conflicting SSI command \"%V\"", &cmd->name);
2811         }
2812 
2813         return NGX_ERROR;
2814     }
2815 
2816     return NGX_OK;
2817 }
2818 
2819 
2820 static void *
2821 ngx_http_ssi_create_main_conf(ngx_conf_t *cf)
2822 {
2823     ngx_http_ssi_main_conf_t  *smcf;
2824 
2825     smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));
2826     if (smcf == NULL) {
2827         return NULL;
2828     }
2829 
2830     smcf->commands.pool = cf->pool;
2831     smcf->commands.temp_pool = cf->temp_pool;
2832 
2833     if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {
2834         return NULL;
2835     }
2836 
2837     return smcf;
2838 }
2839 
2840 
2841 static char *
2842 ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)
2843 {
2844     ngx_http_ssi_main_conf_t *smcf = conf;
2845 
2846     ngx_hash_init_t  hash;
2847 
2848     hash.hash = &smcf->hash;
2849     hash.key = ngx_hash_key;
2850     hash.max_size = 1024;
2851     hash.bucket_size = ngx_cacheline_size;
2852     hash.name = "ssi_command_hash";
2853     hash.pool = cf->pool;
2854     hash.temp_pool = NULL;
2855 
2856     if (ngx_hash_init(&hash, smcf->commands.keys.elts,
2857                       smcf->commands.keys.nelts)
2858         != NGX_OK)
2859     {
2860         return NGX_CONF_ERROR;
2861     }
2862 
2863     return NGX_CONF_OK;
2864 }
2865 
2866 
2867 static void *
2868 ngx_http_ssi_create_loc_conf(ngx_conf_t *cf)
2869 {
2870     ngx_http_ssi_loc_conf_t  *slcf;
2871 
2872     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
2873     if (slcf == NULL) {
2874         return NULL;
2875     }
2876 
2877     /*
2878      * set by ngx_pcalloc():
2879      *
2880      *     conf->types = { NULL };
2881      *     conf->types_keys = NULL;
2882      */
2883 
2884     slcf->enable = NGX_CONF_UNSET;
2885     slcf->silent_errors = NGX_CONF_UNSET;
2886     slcf->ignore_recycled_buffers = NGX_CONF_UNSET;
2887     slcf->last_modified = NGX_CONF_UNSET;
2888 
2889     slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;
2890     slcf->value_len = NGX_CONF_UNSET_SIZE;
2891 
2892     return slcf;
2893 }
2894 
2895 
2896 static char *
2897 ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
2898 {
2899     ngx_http_ssi_loc_conf_t *prev = parent;
2900     ngx_http_ssi_loc_conf_t *conf = child;
2901 
2902     ngx_conf_merge_value(conf->enable, prev->enable, 0);
2903     ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
2904     ngx_conf_merge_value(conf->ignore_recycled_buffers,
2905                          prev->ignore_recycled_buffers, 0);
2906     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
2907 
2908     ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
2909     ngx_conf_merge_size_value(conf->value_len, prev->value_len, 255);
2910 
2911     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
2912                              &prev->types_keys, &prev->types,
2913                              ngx_http_html_default_types)
2914         != NGX_OK)
2915     {
2916         return NGX_CONF_ERROR;
2917     }
2918 
2919     return NGX_CONF_OK;
2920 }
2921 
2922 
2923 static ngx_int_t
2924 ngx_http_ssi_filter_init(ngx_conf_t *cf)
2925 {
2926     ngx_http_next_header_filter = ngx_http_top_header_filter;
2927     ngx_http_top_header_filter = ngx_http_ssi_header_filter;
2928 
2929     ngx_http_next_body_filter = ngx_http_top_body_filter;
2930     ngx_http_top_body_filter = ngx_http_ssi_body_filter;
2931 
2932     return NGX_OK;
2933 }