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_null_string, NULL, NULL, 0, 0, 0 }
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         if (!slcf->last_modified) {
0374             ngx_http_clear_last_modified(r);
0375             ngx_http_clear_etag(r);
0376 
0377         } else {
0378             ngx_http_weak_etag(r);
0379         }
0380     }
0381 
0382     return ngx_http_next_header_filter(r);
0383 }
0384 
0385 
0386 static ngx_int_t
0387 ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0388 {
0389     size_t                     len;
0390     ngx_int_t                  rc;
0391     ngx_buf_t                 *b;
0392     ngx_uint_t                 i, index;
0393     ngx_chain_t               *cl, **ll;
0394     ngx_table_elt_t           *param;
0395     ngx_http_ssi_ctx_t        *ctx, *mctx;
0396     ngx_http_ssi_block_t      *bl;
0397     ngx_http_ssi_param_t      *prm;
0398     ngx_http_ssi_command_t    *cmd;
0399     ngx_http_ssi_loc_conf_t   *slcf;
0400     ngx_http_ssi_main_conf_t  *smcf;
0401     ngx_str_t                 *params[NGX_HTTP_SSI_MAX_PARAMS + 1];
0402 
0403     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
0404 
0405     if (ctx == NULL
0406         || (in == NULL
0407             && ctx->buf == NULL
0408             && ctx->in == NULL
0409             && ctx->busy == NULL))
0410     {
0411         return ngx_http_next_body_filter(r, in);
0412     }
0413 
0414     /* add the incoming chain to the chain ctx->in */
0415 
0416     if (in) {
0417         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
0418             return NGX_ERROR;
0419         }
0420     }
0421 
0422     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0423                    "http ssi filter \"%V?%V\"", &r->uri, &r->args);
0424 
0425     if (ctx->wait) {
0426 
0427         if (r != r->connection->data) {
0428             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0429                            "http ssi filter wait \"%V?%V\" non-active",
0430                            &ctx->wait->uri, &ctx->wait->args);
0431 
0432             return NGX_AGAIN;
0433         }
0434 
0435         if (ctx->wait->done) {
0436             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0437                            "http ssi filter wait \"%V?%V\" done",
0438                            &ctx->wait->uri, &ctx->wait->args);
0439 
0440             ctx->wait = NULL;
0441 
0442         } else {
0443             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0444                            "http ssi filter wait \"%V?%V\"",
0445                            &ctx->wait->uri, &ctx->wait->args);
0446 
0447             return ngx_http_next_body_filter(r, NULL);
0448         }
0449     }
0450 
0451     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
0452 
0453     while (ctx->in || ctx->buf) {
0454 
0455         if (ctx->buf == NULL) {
0456             ctx->buf = ctx->in->buf;
0457             ctx->in = ctx->in->next;
0458             ctx->pos = ctx->buf->pos;
0459         }
0460 
0461         if (ctx->state == ssi_start_state) {
0462             ctx->copy_start = ctx->pos;
0463             ctx->copy_end = ctx->pos;
0464         }
0465 
0466         b = NULL;
0467 
0468         while (ctx->pos < ctx->buf->last) {
0469 
0470             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0471                            "saved: %uz state: %ui", ctx->saved, ctx->state);
0472 
0473             rc = ngx_http_ssi_parse(r, ctx);
0474 
0475             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0476                            "parse: %i, looked: %uz %p-%p",
0477                            rc, ctx->looked, ctx->copy_start, ctx->copy_end);
0478 
0479             if (rc == NGX_ERROR) {
0480                 return rc;
0481             }
0482 
0483             if (ctx->copy_start != ctx->copy_end) {
0484 
0485                 if (ctx->output) {
0486 
0487                     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0488                                    "saved: %uz", ctx->saved);
0489 
0490                     if (ctx->saved) {
0491 
0492                         if (ctx->free) {
0493                             cl = ctx->free;
0494                             ctx->free = ctx->free->next;
0495                             b = cl->buf;
0496                             ngx_memzero(b, sizeof(ngx_buf_t));
0497 
0498                         } else {
0499                             b = ngx_calloc_buf(r->pool);
0500                             if (b == NULL) {
0501                                 return NGX_ERROR;
0502                             }
0503 
0504                             cl = ngx_alloc_chain_link(r->pool);
0505                             if (cl == NULL) {
0506                                 return NGX_ERROR;
0507                             }
0508 
0509                             cl->buf = b;
0510                         }
0511 
0512                         b->memory = 1;
0513                         b->pos = ngx_http_ssi_string;
0514                         b->last = ngx_http_ssi_string + ctx->saved;
0515 
0516                         *ctx->last_out = cl;
0517                         ctx->last_out = &cl->next;
0518 
0519                         ctx->saved = 0;
0520                     }
0521 
0522                     if (ctx->free) {
0523                         cl = ctx->free;
0524                         ctx->free = ctx->free->next;
0525                         b = cl->buf;
0526 
0527                     } else {
0528                         b = ngx_alloc_buf(r->pool);
0529                         if (b == NULL) {
0530                             return NGX_ERROR;
0531                         }
0532 
0533                         cl = ngx_alloc_chain_link(r->pool);
0534                         if (cl == NULL) {
0535                             return NGX_ERROR;
0536                         }
0537 
0538                         cl->buf = b;
0539                     }
0540 
0541                     ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
0542 
0543                     b->pos = ctx->copy_start;
0544                     b->last = ctx->copy_end;
0545                     b->shadow = NULL;
0546                     b->last_buf = 0;
0547                     b->recycled = 0;
0548 
0549                     if (b->in_file) {
0550                         if (slcf->min_file_chunk < (size_t) (b->last - b->pos))
0551                         {
0552                             b->file_last = b->file_pos
0553                                                    + (b->last - ctx->buf->pos);
0554                             b->file_pos += b->pos - ctx->buf->pos;
0555 
0556                         } else {
0557                             b->in_file = 0;
0558                         }
0559                     }
0560 
0561                     cl->next = NULL;
0562                     *ctx->last_out = cl;
0563                     ctx->last_out = &cl->next;
0564 
0565                 } else {
0566                     if (ctx->block
0567                         && ctx->saved + (ctx->copy_end - ctx->copy_start))
0568                     {
0569                         b = ngx_create_temp_buf(r->pool,
0570                                ctx->saved + (ctx->copy_end - ctx->copy_start));
0571 
0572                         if (b == NULL) {
0573                             return NGX_ERROR;
0574                         }
0575 
0576                         if (ctx->saved) {
0577                             b->last = ngx_cpymem(b->pos, ngx_http_ssi_string,
0578                                                  ctx->saved);
0579                         }
0580 
0581                         b->last = ngx_cpymem(b->last, ctx->copy_start,
0582                                              ctx->copy_end - ctx->copy_start);
0583 
0584                         cl = ngx_alloc_chain_link(r->pool);
0585                         if (cl == NULL) {
0586                             return NGX_ERROR;
0587                         }
0588 
0589                         cl->buf = b;
0590                         cl->next = NULL;
0591 
0592                         b = NULL;
0593 
0594                         mctx = ngx_http_get_module_ctx(r->main,
0595                                                    ngx_http_ssi_filter_module);
0596                         bl = mctx->blocks->elts;
0597                         for (ll = &bl[mctx->blocks->nelts - 1].bufs;
0598                              *ll;
0599                              ll = &(*ll)->next)
0600                         {
0601                             /* void */
0602                         }
0603 
0604                         *ll = cl;
0605                     }
0606 
0607                     ctx->saved = 0;
0608                 }
0609             }
0610 
0611             if (ctx->state == ssi_start_state) {
0612                 ctx->copy_start = ctx->pos;
0613                 ctx->copy_end = ctx->pos;
0614 
0615             } else {
0616                 ctx->copy_start = NULL;
0617                 ctx->copy_end = NULL;
0618             }
0619 
0620             if (rc == NGX_AGAIN) {
0621                 continue;
0622             }
0623 
0624 
0625             b = NULL;
0626 
0627             if (rc == NGX_OK) {
0628 
0629                 smcf = ngx_http_get_module_main_conf(r,
0630                                                    ngx_http_ssi_filter_module);
0631 
0632                 cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,
0633                                     ctx->command.len);
0634 
0635                 if (cmd == NULL) {
0636                     if (ctx->output) {
0637                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0638                                       "invalid SSI command: \"%V\"",
0639                                       &ctx->command);
0640                         goto ssi_error;
0641                     }
0642 
0643                     continue;
0644                 }
0645 
0646                 if (!ctx->output && !cmd->block) {
0647 
0648                     if (ctx->block) {
0649 
0650                         /* reconstruct the SSI command text */
0651 
0652                         len = 5 + ctx->command.len + 4;
0653 
0654                         param = ctx->params.elts;
0655                         for (i = 0; i < ctx->params.nelts; i++) {
0656                             len += 1 + param[i].key.len + 2
0657                                 + param[i].value.len + 1;
0658                         }
0659 
0660                         b = ngx_create_temp_buf(r->pool, len);
0661 
0662                         if (b == NULL) {
0663                             return NGX_ERROR;
0664                         }
0665 
0666                         cl = ngx_alloc_chain_link(r->pool);
0667                         if (cl == NULL) {
0668                             return NGX_ERROR;
0669                         }
0670 
0671                         cl->buf = b;
0672                         cl->next = NULL;
0673 
0674                         *b->last++ = '<';
0675                         *b->last++ = '!';
0676                         *b->last++ = '-';
0677                         *b->last++ = '-';
0678                         *b->last++ = '#';
0679 
0680                         b->last = ngx_cpymem(b->last, ctx->command.data,
0681                                              ctx->command.len);
0682 
0683                         for (i = 0; i < ctx->params.nelts; i++) {
0684                             *b->last++ = ' ';
0685                             b->last = ngx_cpymem(b->last, param[i].key.data,
0686                                                  param[i].key.len);
0687                             *b->last++ = '=';
0688                             *b->last++ = '"';
0689                             b->last = ngx_cpymem(b->last, param[i].value.data,
0690                                                  param[i].value.len);
0691                             *b->last++ = '"';
0692                         }
0693 
0694                         *b->last++ = ' ';
0695                         *b->last++ = '-';
0696                         *b->last++ = '-';
0697                         *b->last++ = '>';
0698 
0699                         mctx = ngx_http_get_module_ctx(r->main,
0700                                                    ngx_http_ssi_filter_module);
0701                         bl = mctx->blocks->elts;
0702                         for (ll = &bl[mctx->blocks->nelts - 1].bufs;
0703                              *ll;
0704                              ll = &(*ll)->next)
0705                         {
0706                             /* void */
0707                         }
0708 
0709                         *ll = cl;
0710 
0711                         b = NULL;
0712 
0713                         continue;
0714                     }
0715 
0716                     if (cmd->conditional == 0) {
0717                         continue;
0718                     }
0719                 }
0720 
0721                 if (cmd->conditional
0722                     && (ctx->conditional == 0
0723                         || ctx->conditional > cmd->conditional))
0724                 {
0725                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0726                                   "invalid context of SSI command: \"%V\"",
0727                                   &ctx->command);
0728                     goto ssi_error;
0729                 }
0730 
0731                 if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {
0732                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0733                                   "too many SSI command parameters: \"%V\"",
0734                                   &ctx->command);
0735                     goto ssi_error;
0736                 }
0737 
0738                 ngx_memzero(params,
0739                            (NGX_HTTP_SSI_MAX_PARAMS + 1) * sizeof(ngx_str_t *));
0740 
0741                 param = ctx->params.elts;
0742 
0743                 for (i = 0; i < ctx->params.nelts; i++) {
0744 
0745                     for (prm = cmd->params; prm->name.len; prm++) {
0746 
0747                         if (param[i].key.len != prm->name.len
0748                             || ngx_strncmp(param[i].key.data, prm->name.data,
0749                                            prm->name.len) != 0)
0750                         {
0751                             continue;
0752                         }
0753 
0754                         if (!prm->multiple) {
0755                             if (params[prm->index]) {
0756                                 ngx_log_error(NGX_LOG_ERR,
0757                                               r->connection->log, 0,
0758                                               "duplicate \"%V\" parameter "
0759                                               "in \"%V\" SSI command",
0760                                               &param[i].key, &ctx->command);
0761 
0762                                 goto ssi_error;
0763                             }
0764 
0765                             params[prm->index] = &param[i].value;
0766 
0767                             break;
0768                         }
0769 
0770                         for (index = prm->index; params[index]; index++) {
0771                             /* void */
0772                         }
0773 
0774                         params[index] = &param[i].value;
0775 
0776                         break;
0777                     }
0778 
0779                     if (prm->name.len == 0) {
0780                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0781                                       "invalid parameter name: \"%V\" "
0782                                       "in \"%V\" SSI command",
0783                                       &param[i].key, &ctx->command);
0784 
0785                         goto ssi_error;
0786                     }
0787                 }
0788 
0789                 for (prm = cmd->params; prm->name.len; prm++) {
0790                     if (prm->mandatory && params[prm->index] == 0) {
0791                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0792                                       "mandatory \"%V\" parameter is absent "
0793                                       "in \"%V\" SSI command",
0794                                       &prm->name, &ctx->command);
0795 
0796                         goto ssi_error;
0797                     }
0798                 }
0799 
0800                 if (cmd->flush && ctx->out) {
0801 
0802                     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0803                                    "ssi flush");
0804 
0805                     if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {
0806                         return NGX_ERROR;
0807                     }
0808                 }
0809 
0810                 rc = cmd->handler(r, ctx, params);
0811 
0812                 if (rc == NGX_OK) {
0813                     continue;
0814                 }
0815 
0816                 if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {
0817                     ngx_http_ssi_buffered(r, ctx);
0818                     return rc;
0819                 }
0820             }
0821 
0822 
0823             /* rc == NGX_HTTP_SSI_ERROR */
0824 
0825     ssi_error:
0826 
0827             if (slcf->silent_errors) {
0828                 continue;
0829             }
0830 
0831             if (ctx->free) {
0832                 cl = ctx->free;
0833                 ctx->free = ctx->free->next;
0834                 b = cl->buf;
0835                 ngx_memzero(b, sizeof(ngx_buf_t));
0836 
0837             } else {
0838                 b = ngx_calloc_buf(r->pool);
0839                 if (b == NULL) {
0840                     return NGX_ERROR;
0841                 }
0842 
0843                 cl = ngx_alloc_chain_link(r->pool);
0844                 if (cl == NULL) {
0845                     return NGX_ERROR;
0846                 }
0847 
0848                 cl->buf = b;
0849             }
0850 
0851             b->memory = 1;
0852             b->pos = ctx->errmsg.data;
0853             b->last = ctx->errmsg.data + ctx->errmsg.len;
0854 
0855             cl->next = NULL;
0856             *ctx->last_out = cl;
0857             ctx->last_out = &cl->next;
0858 
0859             continue;
0860         }
0861 
0862         if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {
0863             if (b == NULL) {
0864                 if (ctx->free) {
0865                     cl = ctx->free;
0866                     ctx->free = ctx->free->next;
0867                     b = cl->buf;
0868                     ngx_memzero(b, sizeof(ngx_buf_t));
0869 
0870                 } else {
0871                     b = ngx_calloc_buf(r->pool);
0872                     if (b == NULL) {
0873                         return NGX_ERROR;
0874                     }
0875 
0876                     cl = ngx_alloc_chain_link(r->pool);
0877                     if (cl == NULL) {
0878                         return NGX_ERROR;
0879                     }
0880 
0881                     cl->buf = b;
0882                 }
0883 
0884                 b->sync = 1;
0885 
0886                 cl->next = NULL;
0887                 *ctx->last_out = cl;
0888                 ctx->last_out = &cl->next;
0889             }
0890 
0891             b->last_buf = ctx->buf->last_buf;
0892             b->shadow = ctx->buf;
0893 
0894             if (slcf->ignore_recycled_buffers == 0)  {
0895                 b->recycled = ctx->buf->recycled;
0896             }
0897         }
0898 
0899         ctx->buf = NULL;
0900 
0901         ctx->saved = ctx->looked;
0902     }
0903 
0904     if (ctx->out == NULL && ctx->busy == NULL) {
0905         return NGX_OK;
0906     }
0907 
0908     return ngx_http_ssi_output(r, ctx);
0909 }
0910 
0911 
0912 static ngx_int_t
0913 ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
0914 {
0915     ngx_int_t     rc;
0916     ngx_buf_t    *b;
0917     ngx_chain_t  *cl;
0918 
0919 #if 1
0920     b = NULL;
0921     for (cl = ctx->out; cl; cl = cl->next) {
0922         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0923                        "ssi out: %p %p", cl->buf, cl->buf->pos);
0924         if (cl->buf == b) {
0925             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
0926                           "the same buf was used in ssi");
0927             ngx_debug_point();
0928             return NGX_ERROR;
0929         }
0930         b = cl->buf;
0931     }
0932 #endif
0933 
0934     rc = ngx_http_next_body_filter(r, ctx->out);
0935 
0936     if (ctx->busy == NULL) {
0937         ctx->busy = ctx->out;
0938 
0939     } else {
0940         for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
0941         cl->next = ctx->out;
0942     }
0943 
0944     ctx->out = NULL;
0945     ctx->last_out = &ctx->out;
0946 
0947     while (ctx->busy) {
0948 
0949         cl = ctx->busy;
0950         b = cl->buf;
0951 
0952         if (ngx_buf_size(b) != 0) {
0953             break;
0954         }
0955 
0956         if (b->shadow) {
0957             b->shadow->pos = b->shadow->last;
0958         }
0959 
0960         ctx->busy = cl->next;
0961 
0962         if (ngx_buf_in_memory(b) || b->in_file) {
0963             /* add data bufs only to the free buf chain */
0964 
0965             cl->next = ctx->free;
0966             ctx->free = cl;
0967         }
0968     }
0969 
0970     ngx_http_ssi_buffered(r, ctx);
0971 
0972     return rc;
0973 }
0974 
0975 
0976 static void
0977 ngx_http_ssi_buffered(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
0978 {
0979     if (ctx->in || ctx->buf) {
0980         r->buffered |= NGX_HTTP_SSI_BUFFERED;
0981 
0982     } else {
0983         r->buffered &= ~NGX_HTTP_SSI_BUFFERED;
0984     }
0985 }
0986 
0987 
0988 static ngx_int_t
0989 ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
0990 {
0991     u_char                *p, *value, *last, *copy_end, ch;
0992     size_t                 looked;
0993     ngx_http_ssi_state_e   state;
0994 
0995     state = ctx->state;
0996     looked = ctx->looked;
0997     last = ctx->buf->last;
0998     copy_end = ctx->copy_end;
0999 
1000     for (p = ctx->pos; p < last; p++) {
1001 
1002         ch = *p;
1003 
1004         if (state == ssi_start_state) {
1005 
1006             /* the tight loop */
1007 
1008             for ( ;; ) {
1009                 if (ch == '<') {
1010                     copy_end = p;
1011                     looked = 1;
1012                     state = ssi_tag_state;
1013 
1014                     goto tag_started;
1015                 }
1016 
1017                 if (++p == last) {
1018                     break;
1019                 }
1020 
1021                 ch = *p;
1022             }
1023 
1024             ctx->state = state;
1025             ctx->pos = p;
1026             ctx->looked = looked;
1027             ctx->copy_end = p;
1028 
1029             if (ctx->copy_start == NULL) {
1030                 ctx->copy_start = ctx->buf->pos;
1031             }
1032 
1033             return NGX_AGAIN;
1034 
1035         tag_started:
1036 
1037             continue;
1038         }
1039 
1040         switch (state) {
1041 
1042         case ssi_start_state:
1043             /* not reached */
1044             break;
1045 
1046         case ssi_tag_state:
1047             switch (ch) {
1048             case '!':
1049                 looked = 2;
1050                 state = ssi_comment0_state;
1051                 break;
1052 
1053             case '<':
1054                 copy_end = p;
1055                 break;
1056 
1057             default:
1058                 copy_end = p;
1059                 looked = 0;
1060                 state = ssi_start_state;
1061                 break;
1062             }
1063 
1064             break;
1065 
1066         case ssi_comment0_state:
1067             switch (ch) {
1068             case '-':
1069                 looked = 3;
1070                 state = ssi_comment1_state;
1071                 break;
1072 
1073             case '<':
1074                 copy_end = p;
1075                 looked = 1;
1076                 state = ssi_tag_state;
1077                 break;
1078 
1079             default:
1080                 copy_end = p;
1081                 looked = 0;
1082                 state = ssi_start_state;
1083                 break;
1084             }
1085 
1086             break;
1087 
1088         case ssi_comment1_state:
1089             switch (ch) {
1090             case '-':
1091                 looked = 4;
1092                 state = ssi_sharp_state;
1093                 break;
1094 
1095             case '<':
1096                 copy_end = p;
1097                 looked = 1;
1098                 state = ssi_tag_state;
1099                 break;
1100 
1101             default:
1102                 copy_end = p;
1103                 looked = 0;
1104                 state = ssi_start_state;
1105                 break;
1106             }
1107 
1108             break;
1109 
1110         case ssi_sharp_state:
1111             switch (ch) {
1112             case '#':
1113                 if (p - ctx->pos < 4) {
1114                     ctx->saved = 0;
1115                 }
1116                 looked = 0;
1117                 state = ssi_precommand_state;
1118                 break;
1119 
1120             case '<':
1121                 copy_end = p;
1122                 looked = 1;
1123                 state = ssi_tag_state;
1124                 break;
1125 
1126             default:
1127                 copy_end = p;
1128                 looked = 0;
1129                 state = ssi_start_state;
1130                 break;
1131             }
1132 
1133             break;
1134 
1135         case ssi_precommand_state:
1136             switch (ch) {
1137             case ' ':
1138             case CR:
1139             case LF:
1140             case '\t':
1141                 break;
1142 
1143             default:
1144                 ctx->command.len = 1;
1145                 ctx->command.data = ngx_pnalloc(r->pool,
1146                                                 NGX_HTTP_SSI_COMMAND_LEN);
1147                 if (ctx->command.data == NULL) {
1148                     return NGX_ERROR;
1149                 }
1150 
1151                 ctx->command.data[0] = ch;
1152 
1153                 ctx->key = 0;
1154                 ctx->key = ngx_hash(ctx->key, ch);
1155 
1156                 ctx->params.nelts = 0;
1157 
1158                 state = ssi_command_state;
1159                 break;
1160             }
1161 
1162             break;
1163 
1164         case ssi_command_state:
1165             switch (ch) {
1166             case ' ':
1167             case CR:
1168             case LF:
1169             case '\t':
1170                 state = ssi_preparam_state;
1171                 break;
1172 
1173             case '-':
1174                 state = ssi_comment_end0_state;
1175                 break;
1176 
1177             default:
1178                 if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {
1179                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1180                                   "the \"%V%c...\" SSI command is too long",
1181                                   &ctx->command, ch);
1182 
1183                     state = ssi_error_state;
1184                     break;
1185                 }
1186 
1187                 ctx->command.data[ctx->command.len++] = ch;
1188                 ctx->key = ngx_hash(ctx->key, ch);
1189             }
1190 
1191             break;
1192 
1193         case ssi_preparam_state:
1194             switch (ch) {
1195             case ' ':
1196             case CR:
1197             case LF:
1198             case '\t':
1199                 break;
1200 
1201             case '-':
1202                 state = ssi_comment_end0_state;
1203                 break;
1204 
1205             default:
1206                 ctx->param = ngx_array_push(&ctx->params);
1207                 if (ctx->param == NULL) {
1208                     return NGX_ERROR;
1209                 }
1210 
1211                 ctx->param->key.len = 1;
1212                 ctx->param->key.data = ngx_pnalloc(r->pool,
1213                                                    NGX_HTTP_SSI_PARAM_LEN);
1214                 if (ctx->param->key.data == NULL) {
1215                     return NGX_ERROR;
1216                 }
1217 
1218                 ctx->param->key.data[0] = ch;
1219 
1220                 ctx->param->value.len = 0;
1221 
1222                 if (ctx->value_buf == NULL) {
1223                     ctx->param->value.data = ngx_pnalloc(r->pool,
1224                                                          ctx->value_len + 1);
1225                     if (ctx->param->value.data == NULL) {
1226                         return NGX_ERROR;
1227                     }
1228 
1229                 } else {
1230                     ctx->param->value.data = ctx->value_buf;
1231                 }
1232 
1233                 state = ssi_param_state;
1234                 break;
1235             }
1236 
1237             break;
1238 
1239         case ssi_param_state:
1240             switch (ch) {
1241             case ' ':
1242             case CR:
1243             case LF:
1244             case '\t':
1245                 state = ssi_preequal_state;
1246                 break;
1247 
1248             case '=':
1249                 state = ssi_prevalue_state;
1250                 break;
1251 
1252             case '-':
1253                 state = ssi_error_end0_state;
1254 
1255                 ctx->param->key.data[ctx->param->key.len++] = ch;
1256                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1257                               "invalid \"%V\" parameter in \"%V\" SSI command",
1258                               &ctx->param->key, &ctx->command);
1259                 break;
1260 
1261             default:
1262                 if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {
1263                     state = ssi_error_state;
1264                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1265                                   "too long \"%V%c...\" parameter in "
1266                                   "\"%V\" SSI command",
1267                                   &ctx->param->key, ch, &ctx->command);
1268                     break;
1269                 }
1270 
1271                 ctx->param->key.data[ctx->param->key.len++] = ch;
1272             }
1273 
1274             break;
1275 
1276         case ssi_preequal_state:
1277             switch (ch) {
1278             case ' ':
1279             case CR:
1280             case LF:
1281             case '\t':
1282                 break;
1283 
1284             case '=':
1285                 state = ssi_prevalue_state;
1286                 break;
1287 
1288             default:
1289                 if (ch == '-') {
1290                     state = ssi_error_end0_state;
1291                 } else {
1292                     state = ssi_error_state;
1293                 }
1294 
1295                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1296                               "unexpected \"%c\" symbol after \"%V\" "
1297                               "parameter in \"%V\" SSI command",
1298                               ch, &ctx->param->key, &ctx->command);
1299                 break;
1300             }
1301 
1302             break;
1303 
1304         case ssi_prevalue_state:
1305             switch (ch) {
1306             case ' ':
1307             case CR:
1308             case LF:
1309             case '\t':
1310                 break;
1311 
1312             case '"':
1313                 state = ssi_double_quoted_value_state;
1314                 break;
1315 
1316             case '\'':
1317                 state = ssi_quoted_value_state;
1318                 break;
1319 
1320             default:
1321                 if (ch == '-') {
1322                     state = ssi_error_end0_state;
1323                 } else {
1324                     state = ssi_error_state;
1325                 }
1326 
1327                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1328                               "unexpected \"%c\" symbol before value of "
1329                               "\"%V\" parameter in \"%V\" SSI command",
1330                               ch, &ctx->param->key, &ctx->command);
1331                 break;
1332             }
1333 
1334             break;
1335 
1336         case ssi_double_quoted_value_state:
1337             switch (ch) {
1338             case '"':
1339                 state = ssi_postparam_state;
1340                 break;
1341 
1342             case '\\':
1343                 ctx->saved_state = ssi_double_quoted_value_state;
1344                 state = ssi_quoted_symbol_state;
1345 
1346                 /* fall through */
1347 
1348             default:
1349                 if (ctx->param->value.len == ctx->value_len) {
1350                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1351                                   "too long \"%V%c...\" value of \"%V\" "
1352                                   "parameter in \"%V\" SSI command",
1353                                   &ctx->param->value, ch, &ctx->param->key,
1354                                   &ctx->command);
1355                     state = ssi_error_state;
1356                     break;
1357                 }
1358 
1359                 ctx->param->value.data[ctx->param->value.len++] = ch;
1360             }
1361 
1362             break;
1363 
1364         case ssi_quoted_value_state:
1365             switch (ch) {
1366             case '\'':
1367                 state = ssi_postparam_state;
1368                 break;
1369 
1370             case '\\':
1371                 ctx->saved_state = ssi_quoted_value_state;
1372                 state = ssi_quoted_symbol_state;
1373 
1374                 /* fall through */
1375 
1376             default:
1377                 if (ctx->param->value.len == ctx->value_len) {
1378                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1379                                   "too long \"%V%c...\" value of \"%V\" "
1380                                   "parameter in \"%V\" SSI command",
1381                                   &ctx->param->value, ch, &ctx->param->key,
1382                                   &ctx->command);
1383                     state = ssi_error_state;
1384                     break;
1385                 }
1386 
1387                 ctx->param->value.data[ctx->param->value.len++] = ch;
1388             }
1389 
1390             break;
1391 
1392         case ssi_quoted_symbol_state:
1393             state = ctx->saved_state;
1394 
1395             if (ctx->param->value.len == ctx->value_len) {
1396                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1397                               "too long \"%V%c...\" value of \"%V\" "
1398                               "parameter in \"%V\" SSI command",
1399                               &ctx->param->value, ch, &ctx->param->key,
1400                               &ctx->command);
1401                 state = ssi_error_state;
1402                 break;
1403             }
1404 
1405             ctx->param->value.data[ctx->param->value.len++] = ch;
1406 
1407             break;
1408 
1409         case ssi_postparam_state:
1410 
1411             if (ctx->param->value.len + 1 < ctx->value_len / 2) {
1412                 value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);
1413                 if (value == NULL) {
1414                     return NGX_ERROR;
1415                 }
1416 
1417                 ngx_memcpy(value, ctx->param->value.data,
1418                            ctx->param->value.len);
1419 
1420                 ctx->value_buf = ctx->param->value.data;
1421                 ctx->param->value.data = value;
1422 
1423             } else {
1424                 ctx->value_buf = NULL;
1425             }
1426 
1427             switch (ch) {
1428             case ' ':
1429             case CR:
1430             case LF:
1431             case '\t':
1432                 state = ssi_preparam_state;
1433                 break;
1434 
1435             case '-':
1436                 state = ssi_comment_end0_state;
1437                 break;
1438 
1439             default:
1440                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1441                               "unexpected \"%c\" symbol after \"%V\" value "
1442                               "of \"%V\" parameter in \"%V\" SSI command",
1443                               ch, &ctx->param->value, &ctx->param->key,
1444                               &ctx->command);
1445                 state = ssi_error_state;
1446                 break;
1447             }
1448 
1449             break;
1450 
1451         case ssi_comment_end0_state:
1452             switch (ch) {
1453             case '-':
1454                 state = ssi_comment_end1_state;
1455                 break;
1456 
1457             default:
1458                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1459                               "unexpected \"%c\" symbol in \"%V\" SSI command",
1460                               ch, &ctx->command);
1461                 state = ssi_error_state;
1462                 break;
1463             }
1464 
1465             break;
1466 
1467         case ssi_comment_end1_state:
1468             switch (ch) {
1469             case '>':
1470                 ctx->state = ssi_start_state;
1471                 ctx->pos = p + 1;
1472                 ctx->looked = looked;
1473                 ctx->copy_end = copy_end;
1474 
1475                 if (ctx->copy_start == NULL && copy_end) {
1476                     ctx->copy_start = ctx->buf->pos;
1477                 }
1478 
1479                 return NGX_OK;
1480 
1481             default:
1482                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1483                               "unexpected \"%c\" symbol in \"%V\" SSI command",
1484                               ch, &ctx->command);
1485                 state = ssi_error_state;
1486                 break;
1487             }
1488 
1489             break;
1490 
1491         case ssi_error_state:
1492             switch (ch) {
1493             case '-':
1494                 state = ssi_error_end0_state;
1495                 break;
1496 
1497             default:
1498                 break;
1499             }
1500 
1501             break;
1502 
1503         case ssi_error_end0_state:
1504             switch (ch) {
1505             case '-':
1506                 state = ssi_error_end1_state;
1507                 break;
1508 
1509             default:
1510                 state = ssi_error_state;
1511                 break;
1512             }
1513 
1514             break;
1515 
1516         case ssi_error_end1_state:
1517             switch (ch) {
1518             case '>':
1519                 ctx->state = ssi_start_state;
1520                 ctx->pos = p + 1;
1521                 ctx->looked = looked;
1522                 ctx->copy_end = copy_end;
1523 
1524                 if (ctx->copy_start == NULL && copy_end) {
1525                     ctx->copy_start = ctx->buf->pos;
1526                 }
1527 
1528                 return NGX_HTTP_SSI_ERROR;
1529 
1530             default:
1531                 state = ssi_error_state;
1532                 break;
1533             }
1534 
1535             break;
1536         }
1537     }
1538 
1539     ctx->state = state;
1540     ctx->pos = p;
1541     ctx->looked = looked;
1542 
1543     ctx->copy_end = (state == ssi_start_state) ? p : copy_end;
1544 
1545     if (ctx->copy_start == NULL && ctx->copy_end) {
1546         ctx->copy_start = ctx->buf->pos;
1547     }
1548 
1549     return NGX_AGAIN;
1550 }
1551 
1552 
1553 static ngx_str_t *
1554 ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name,
1555     ngx_uint_t key)
1556 {
1557     ngx_uint_t           i;
1558     ngx_list_part_t     *part;
1559     ngx_http_ssi_var_t  *var;
1560     ngx_http_ssi_ctx_t  *ctx;
1561 
1562     ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
1563 
1564 #if (NGX_PCRE)
1565     {
1566     ngx_str_t  *value;
1567 
1568     if (key >= '0' && key <= '9') {
1569         i = key - '0';
1570 
1571         if (i < ctx->ncaptures) {
1572             value = ngx_palloc(r->pool, sizeof(ngx_str_t));
1573             if (value == NULL) {
1574                 return NULL;
1575             }
1576 
1577             i *= 2;
1578 
1579             value->data = ctx->captures_data + ctx->captures[i];
1580             value->len = ctx->captures[i + 1] - ctx->captures[i];
1581 
1582             return value;
1583         }
1584     }
1585     }
1586 #endif
1587 
1588     if (ctx->variables == NULL) {
1589         return NULL;
1590     }
1591 
1592     part = &ctx->variables->part;
1593     var = part->elts;
1594 
1595     for (i = 0; /* void */ ; i++) {
1596 
1597         if (i >= part->nelts) {
1598             if (part->next == NULL) {
1599                 break;
1600             }
1601 
1602             part = part->next;
1603             var = part->elts;
1604             i = 0;
1605         }
1606 
1607         if (name->len != var[i].name.len) {
1608             continue;
1609         }
1610 
1611         if (key != var[i].key) {
1612             continue;
1613         }
1614 
1615         if (ngx_strncmp(name->data, var[i].name.data, name->len) == 0) {
1616             return &var[i].value;
1617         }
1618     }
1619 
1620     return NULL;
1621 }
1622 
1623 
1624 static ngx_int_t
1625 ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1626     ngx_str_t *text, ngx_uint_t flags)
1627 {
1628     u_char                      ch, *p, **value, *data, *part_data;
1629     size_t                     *size, len, prefix, part_len;
1630     ngx_str_t                   var, *val;
1631     ngx_int_t                   key;
1632     ngx_uint_t                  i, n, bracket, quoted;
1633     ngx_array_t                 lengths, values;
1634     ngx_http_variable_value_t  *vv;
1635 
1636     n = ngx_http_script_variables_count(text);
1637 
1638     if (n == 0) {
1639 
1640         data = text->data;
1641         p = data;
1642 
1643         if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') {
1644 
1645             for (prefix = r->uri.len; prefix; prefix--) {
1646                 if (r->uri.data[prefix - 1] == '/') {
1647                     break;
1648                 }
1649             }
1650 
1651             if (prefix) {
1652                 len = prefix + text->len;
1653 
1654                 data = ngx_pnalloc(r->pool, len);
1655                 if (data == NULL) {
1656                     return NGX_ERROR;
1657                 }
1658 
1659                 p = ngx_copy(data, r->uri.data, prefix);
1660             }
1661         }
1662 
1663         quoted = 0;
1664 
1665         for (i = 0; i < text->len; i++) {
1666             ch = text->data[i];
1667 
1668             if (!quoted) {
1669 
1670                 if (ch == '\\') {
1671                     quoted = 1;
1672                     continue;
1673                 }
1674 
1675             } else {
1676                 quoted = 0;
1677 
1678                 if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
1679                     *p++ = '\\';
1680                 }
1681             }
1682 
1683             *p++ = ch;
1684         }
1685 
1686         text->len = p - data;
1687         text->data = data;
1688 
1689         return NGX_OK;
1690     }
1691 
1692     if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
1693         return NGX_ERROR;
1694     }
1695 
1696     if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
1697         return NGX_ERROR;
1698     }
1699 
1700     len = 0;
1701     i = 0;
1702 
1703     while (i < text->len) {
1704 
1705         if (text->data[i] == '$') {
1706 
1707             var.len = 0;
1708 
1709             if (++i == text->len) {
1710                 goto invalid_variable;
1711             }
1712 
1713             if (text->data[i] == '{') {
1714                 bracket = 1;
1715 
1716                 if (++i == text->len) {
1717                     goto invalid_variable;
1718                 }
1719 
1720                 var.data = &text->data[i];
1721 
1722             } else {
1723                 bracket = 0;
1724                 var.data = &text->data[i];
1725             }
1726 
1727             for ( /* void */ ; i < text->len; i++, var.len++) {
1728                 ch = text->data[i];
1729 
1730                 if (ch == '}' && bracket) {
1731                     i++;
1732                     bracket = 0;
1733                     break;
1734                 }
1735 
1736                 if ((ch >= 'A' && ch <= 'Z')
1737                     || (ch >= 'a' && ch <= 'z')
1738                     || (ch >= '0' && ch <= '9')
1739                     || ch == '_')
1740                 {
1741                     continue;
1742                 }
1743 
1744                 break;
1745             }
1746 
1747             if (bracket) {
1748                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1749                               "the closing bracket in \"%V\" "
1750                               "variable is missing", &var);
1751                 return NGX_HTTP_SSI_ERROR;
1752             }
1753 
1754             if (var.len == 0) {
1755                 goto invalid_variable;
1756             }
1757 
1758             key = ngx_hash_strlow(var.data, var.data, var.len);
1759 
1760             val = ngx_http_ssi_get_variable(r, &var, key);
1761 
1762             if (val == NULL) {
1763                 vv = ngx_http_get_variable(r, &var, key);
1764                 if (vv == NULL) {
1765                     return NGX_ERROR;
1766                 }
1767 
1768                 if (vv->not_found) {
1769                     continue;
1770                 }
1771 
1772                 part_data = vv->data;
1773                 part_len = vv->len;
1774 
1775             } else {
1776                 part_data = val->data;
1777                 part_len = val->len;
1778             }
1779 
1780         } else {
1781             part_data = &text->data[i];
1782             quoted = 0;
1783 
1784             for (p = part_data; i < text->len; i++) {
1785                 ch = text->data[i];
1786 
1787                 if (!quoted) {
1788 
1789                     if (ch == '\\') {
1790                         quoted = 1;
1791                         continue;
1792                     }
1793 
1794                     if (ch == '$') {
1795                         break;
1796                     }
1797 
1798                 } else {
1799                     quoted = 0;
1800 
1801                     if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
1802                         *p++ = '\\';
1803                     }
1804                 }
1805 
1806                 *p++ = ch;
1807             }
1808 
1809             part_len = p - part_data;
1810         }
1811 
1812         len += part_len;
1813 
1814         size = ngx_array_push(&lengths);
1815         if (size == NULL) {
1816             return NGX_ERROR;
1817         }
1818 
1819         *size = part_len;
1820 
1821         value = ngx_array_push(&values);
1822         if (value == NULL) {
1823             return NGX_ERROR;
1824         }
1825 
1826         *value = part_data;
1827     }
1828 
1829     prefix = 0;
1830 
1831     size = lengths.elts;
1832     value = values.elts;
1833 
1834     if (flags & NGX_HTTP_SSI_ADD_PREFIX) {
1835         for (i = 0; i < values.nelts; i++) {
1836             if (size[i] != 0) {
1837                 if (*value[i] != '/') {
1838                     for (prefix = r->uri.len; prefix; prefix--) {
1839                         if (r->uri.data[prefix - 1] == '/') {
1840                             len += prefix;
1841                             break;
1842                         }
1843                     }
1844                 }
1845 
1846                 break;
1847             }
1848         }
1849     }
1850 
1851     p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
1852     if (p == NULL) {
1853         return NGX_ERROR;
1854     }
1855 
1856     text->len = len;
1857     text->data = p;
1858 
1859     p = ngx_copy(p, r->uri.data, prefix);
1860 
1861     for (i = 0; i < values.nelts; i++) {
1862         p = ngx_copy(p, value[i], size[i]);
1863     }
1864 
1865     return NGX_OK;
1866 
1867 invalid_variable:
1868 
1869     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1870                   "invalid variable name in \"%V\"", text);
1871 
1872     return NGX_HTTP_SSI_ERROR;
1873 }
1874 
1875 
1876 static ngx_int_t
1877 ngx_http_ssi_regex_match(ngx_http_request_t *r, ngx_str_t *pattern,
1878     ngx_str_t *str)
1879 {
1880 #if (NGX_PCRE)
1881     int                   rc, *captures;
1882     u_char               *p, errstr[NGX_MAX_CONF_ERRSTR];
1883     size_t                size;
1884     ngx_int_t             key;
1885     ngx_str_t            *vv, name, value;
1886     ngx_uint_t            i, n;
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, key;
1990     ngx_str_t                   *uri, *file, *wait, *set, *stub, args;
1991     ngx_buf_t                   *b;
1992     ngx_uint_t                   flags, i;
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->upstream) {
2235         value->len = r->upstream->buffer.last - r->upstream->buffer.pos;
2236         value->data = r->upstream->buffer.pos;
2237     }
2238 
2239     return rc;
2240 }
2241 
2242 
2243 static ngx_int_t
2244 ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2245     ngx_str_t **params)
2246 {
2247     u_char                     *p;
2248     uintptr_t                   len;
2249     ngx_int_t                   key;
2250     ngx_buf_t                  *b;
2251     ngx_str_t                  *var, *value, *enc, text;
2252     ngx_chain_t                *cl;
2253     ngx_http_variable_value_t  *vv;
2254 
2255     var = params[NGX_HTTP_SSI_ECHO_VAR];
2256 
2257     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2258                    "ssi echo \"%V\"", var);
2259 
2260     key = ngx_hash_strlow(var->data, var->data, var->len);
2261 
2262     value = ngx_http_ssi_get_variable(r, var, key);
2263 
2264     if (value == NULL) {
2265         vv = ngx_http_get_variable(r, var, key);
2266 
2267         if (vv == NULL) {
2268             return NGX_HTTP_SSI_ERROR;
2269         }
2270 
2271         if (!vv->not_found) {
2272             text.data = vv->data;
2273             text.len = vv->len;
2274             value = &text;
2275         }
2276     }
2277 
2278     if (value == NULL) {
2279         value = params[NGX_HTTP_SSI_ECHO_DEFAULT];
2280 
2281         if (value == NULL) {
2282             value = &ngx_http_ssi_none;
2283 
2284         } else if (value->len == 0) {
2285             return NGX_OK;
2286         }
2287 
2288     } else {
2289         if (value->len == 0) {
2290             return NGX_OK;
2291         }
2292     }
2293 
2294     enc = params[NGX_HTTP_SSI_ECHO_ENCODING];
2295 
2296     if (enc) {
2297         if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) {
2298 
2299             ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;
2300 
2301         } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {
2302 
2303             ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;
2304 
2305         } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {
2306 
2307             ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
2308 
2309         } else {
2310             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2311                           "unknown encoding \"%V\" in the \"echo\" command",
2312                           enc);
2313         }
2314     }
2315 
2316     p = value->data;
2317 
2318     switch (ctx->encoding) {
2319 
2320     case NGX_HTTP_SSI_URL_ENCODING:
2321         len = 2 * ngx_escape_uri(NULL, value->data, value->len,
2322                                  NGX_ESCAPE_HTML);
2323 
2324         if (len) {
2325             p = ngx_pnalloc(r->pool, value->len + len);
2326             if (p == NULL) {
2327                 return NGX_HTTP_SSI_ERROR;
2328             }
2329 
2330             (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);
2331         }
2332 
2333         len += value->len;
2334         break;
2335 
2336     case NGX_HTTP_SSI_ENTITY_ENCODING:
2337         len = ngx_escape_html(NULL, value->data, value->len);
2338 
2339         if (len) {
2340             p = ngx_pnalloc(r->pool, value->len + len);
2341             if (p == NULL) {
2342                 return NGX_HTTP_SSI_ERROR;
2343             }
2344 
2345             (void) ngx_escape_html(p, value->data, value->len);
2346         }
2347 
2348         len += value->len;
2349         break;
2350 
2351     default: /* NGX_HTTP_SSI_NO_ENCODING */
2352         len = value->len;
2353         break;
2354     }
2355 
2356     b = ngx_calloc_buf(r->pool);
2357     if (b == NULL) {
2358         return NGX_HTTP_SSI_ERROR;
2359     }
2360 
2361     cl = ngx_alloc_chain_link(r->pool);
2362     if (cl == NULL) {
2363         return NGX_HTTP_SSI_ERROR;
2364     }
2365 
2366     b->memory = 1;
2367     b->pos = p;
2368     b->last = p + len;
2369 
2370     cl->buf = b;
2371     cl->next = NULL;
2372     *ctx->last_out = cl;
2373     ctx->last_out = &cl->next;
2374 
2375     return NGX_OK;
2376 }
2377 
2378 
2379 static ngx_int_t
2380 ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2381     ngx_str_t **params)
2382 {
2383     ngx_str_t  *value;
2384 
2385     value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];
2386 
2387     if (value) {
2388         ctx->timefmt.len = value->len;
2389         ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);
2390         if (ctx->timefmt.data == NULL) {
2391             return NGX_HTTP_SSI_ERROR;
2392         }
2393 
2394         ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1);
2395     }
2396 
2397     value = params[NGX_HTTP_SSI_CONFIG_ERRMSG];
2398 
2399     if (value) {
2400         ctx->errmsg = *value;
2401     }
2402 
2403     return NGX_OK;
2404 }
2405 
2406 
2407 static ngx_int_t
2408 ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2409     ngx_str_t **params)
2410 {
2411     ngx_int_t            key, rc;
2412     ngx_str_t           *name, *value, *vv;
2413     ngx_http_ssi_var_t  *var;
2414     ngx_http_ssi_ctx_t  *mctx;
2415 
2416     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
2417 
2418     if (mctx->variables == NULL) {
2419         mctx->variables = ngx_list_create(r->pool, 4,
2420                                           sizeof(ngx_http_ssi_var_t));
2421         if (mctx->variables == NULL) {
2422             return NGX_ERROR;
2423         }
2424     }
2425 
2426     name = params[NGX_HTTP_SSI_SET_VAR];
2427     value = params[NGX_HTTP_SSI_SET_VALUE];
2428 
2429     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2430                    "ssi set \"%V\" \"%V\"", name, value);
2431 
2432     rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);
2433 
2434     if (rc != NGX_OK) {
2435         return rc;
2436     }
2437 
2438     key = ngx_hash_strlow(name->data, name->data, name->len);
2439 
2440     vv = ngx_http_ssi_get_variable(r, name, key);
2441 
2442     if (vv) {
2443         *vv = *value;
2444         return NGX_OK;
2445     }
2446 
2447     var = ngx_list_push(mctx->variables);
2448     if (var == NULL) {
2449         return NGX_ERROR;
2450     }
2451 
2452     var->name = *name;
2453     var->key = key;
2454     var->value = *value;
2455 
2456     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2457                    "set: \"%V\"=\"%V\"", name, value);
2458 
2459     return NGX_OK;
2460 }
2461 
2462 
2463 static ngx_int_t
2464 ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2465     ngx_str_t **params)
2466 {
2467     u_char       *p, *last;
2468     ngx_str_t    *expr, left, right;
2469     ngx_int_t     rc;
2470     ngx_uint_t    negative, noregex, flags;
2471 
2472     if (ctx->command.len == 2) {
2473         if (ctx->conditional) {
2474             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2475                           "the \"if\" command inside the \"if\" command");
2476             return NGX_HTTP_SSI_ERROR;
2477         }
2478     }
2479 
2480     if (ctx->output_chosen) {
2481         ctx->output = 0;
2482         return NGX_OK;
2483     }
2484 
2485     expr = params[NGX_HTTP_SSI_IF_EXPR];
2486 
2487     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2488                    "ssi if expr=\"%V\"", expr);
2489 
2490     left.data = expr->data;
2491     last = expr->data + expr->len;
2492 
2493     for (p = left.data; p < last; p++) {
2494         if (*p >= 'A' && *p <= 'Z') {
2495             *p |= 0x20;
2496             continue;
2497         }
2498 
2499         if ((*p >= 'a' && *p <= 'z')
2500              || (*p >= '0' && *p <= '9')
2501              || *p == '$' || *p == '{' || *p == '}' || *p == '_'
2502              || *p == '"' || *p == '\'')
2503         {
2504             continue;
2505         }
2506 
2507         break;
2508     }
2509 
2510     left.len = p - left.data;
2511 
2512     while (p < last && *p == ' ') {
2513         p++;
2514     }
2515 
2516     flags = 0;
2517 
2518     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2519                    "left: \"%V\"", &left);
2520 
2521     rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);
2522 
2523     if (rc != NGX_OK) {
2524         return rc;
2525     }
2526 
2527     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2528                    "evaluated left: \"%V\"", &left);
2529 
2530     if (p == last) {
2531         if (left.len) {
2532             ctx->output = 1;
2533             ctx->output_chosen = 1;
2534 
2535         } else {
2536             ctx->output = 0;
2537         }
2538 
2539         ctx->conditional = NGX_HTTP_SSI_COND_IF;
2540 
2541         return NGX_OK;
2542     }
2543 
2544     if (p < last && *p == '=') {
2545         negative = 0;
2546         p++;
2547 
2548     } else if (p + 1 < last && *p == '!' && *(p + 1) == '=') {
2549         negative = 1;
2550         p += 2;
2551 
2552     } else {
2553         goto invalid_expression;
2554     }
2555 
2556     while (p < last && *p == ' ') {
2557         p++;
2558     }
2559 
2560     if (p < last - 1 && *p == '/') {
2561         if (*(last - 1) != '/') {
2562             goto invalid_expression;
2563         }
2564 
2565         noregex = 0;
2566         flags = NGX_HTTP_SSI_ADD_ZERO;
2567         last--;
2568         p++;
2569 
2570     } else {
2571         noregex = 1;
2572         flags = 0;
2573 
2574         if (p < last - 1 && p[0] == '\\' && p[1] == '/') {
2575             p++;
2576         }
2577     }
2578 
2579     right.len = last - p;
2580     right.data = p;
2581 
2582     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2583                    "right: \"%V\"", &right);
2584 
2585     rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);
2586 
2587     if (rc != NGX_OK) {
2588         return rc;
2589     }
2590 
2591     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2592                    "evaluated right: \"%V\"", &right);
2593 
2594     if (noregex) {
2595         if (left.len != right.len) {
2596             rc = -1;
2597 
2598         } else {
2599             rc = ngx_strncmp(left.data, right.data, right.len);
2600         }
2601 
2602     } else {
2603         right.data[right.len] = '\0';
2604 
2605         rc = ngx_http_ssi_regex_match(r, &right, &left);
2606 
2607         if (rc == NGX_OK) {
2608             rc = 0;
2609         } else if (rc == NGX_DECLINED) {
2610             rc = -1;
2611         } else {
2612             return rc;
2613         }
2614     }
2615 
2616     if ((rc == 0 && !negative) || (rc != 0 && negative)) {
2617         ctx->output = 1;
2618         ctx->output_chosen = 1;
2619 
2620     } else {
2621         ctx->output = 0;
2622     }
2623 
2624     ctx->conditional = NGX_HTTP_SSI_COND_IF;
2625 
2626     return NGX_OK;
2627 
2628 invalid_expression:
2629 
2630     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2631                   "invalid expression in \"%V\"", expr);
2632 
2633     return NGX_HTTP_SSI_ERROR;
2634 }
2635 
2636 
2637 static ngx_int_t
2638 ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2639     ngx_str_t **params)
2640 {
2641     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2642                    "ssi else");
2643 
2644     if (ctx->output_chosen) {
2645         ctx->output = 0;
2646     } else {
2647         ctx->output = 1;
2648     }
2649 
2650     ctx->conditional = NGX_HTTP_SSI_COND_ELSE;
2651 
2652     return NGX_OK;
2653 }
2654 
2655 
2656 static ngx_int_t
2657 ngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2658     ngx_str_t **params)
2659 {
2660     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2661                    "ssi endif");
2662 
2663     ctx->output = 1;
2664     ctx->output_chosen = 0;
2665     ctx->conditional = 0;
2666 
2667     return NGX_OK;
2668 }
2669 
2670 
2671 static ngx_int_t
2672 ngx_http_ssi_block(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2673     ngx_str_t **params)
2674 {
2675     ngx_http_ssi_ctx_t    *mctx;
2676     ngx_http_ssi_block_t  *bl;
2677 
2678     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2679                    "ssi block");
2680 
2681     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
2682 
2683     if (mctx->blocks == NULL) {
2684         mctx->blocks = ngx_array_create(r->pool, 4,
2685                                         sizeof(ngx_http_ssi_block_t));
2686         if (mctx->blocks == NULL) {
2687             return NGX_HTTP_SSI_ERROR;
2688         }
2689     }
2690 
2691     bl = ngx_array_push(mctx->blocks);
2692     if (bl == NULL) {
2693         return NGX_HTTP_SSI_ERROR;
2694     }
2695 
2696     bl->name = *params[NGX_HTTP_SSI_BLOCK_NAME];
2697     bl->bufs = NULL;
2698     bl->count = 0;
2699 
2700     ctx->output = 0;
2701     ctx->block = 1;
2702 
2703     return NGX_OK;
2704 }
2705 
2706 
2707 static ngx_int_t
2708 ngx_http_ssi_endblock(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2709     ngx_str_t **params)
2710 {
2711     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2712                    "ssi endblock");
2713 
2714     ctx->output = 1;
2715     ctx->block = 0;
2716 
2717     return NGX_OK;
2718 }
2719 
2720 
2721 static ngx_int_t
2722 ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
2723     ngx_http_variable_value_t *v, uintptr_t gmt)
2724 {
2725     time_t               now;
2726     ngx_http_ssi_ctx_t  *ctx;
2727     ngx_str_t           *timefmt;
2728     struct tm            tm;
2729     char                 buf[NGX_HTTP_SSI_DATE_LEN];
2730 
2731     v->valid = 1;
2732     v->no_cacheable = 0;
2733     v->not_found = 0;
2734 
2735     now = ngx_time();
2736 
2737     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
2738 
2739     timefmt = ctx ? &ctx->timefmt : &ngx_http_ssi_timefmt;
2740 
2741     if (timefmt->len == sizeof("%s") - 1
2742         && timefmt->data[0] == '%' && timefmt->data[1] == 's')
2743     {
2744         v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);
2745         if (v->data == NULL) {
2746             return NGX_ERROR;
2747         }
2748 
2749         v->len = ngx_sprintf(v->data, "%T", now) - v->data;
2750 
2751         return NGX_OK;
2752     }
2753 
2754     if (gmt) {
2755         ngx_libc_gmtime(now, &tm);
2756     } else {
2757         ngx_libc_localtime(now, &tm);
2758     }
2759 
2760     v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,
2761                       (char *) timefmt->data, &tm);
2762     if (v->len == 0) {
2763         return NGX_ERROR;
2764     }
2765 
2766     v->data = ngx_pnalloc(r->pool, v->len);
2767     if (v->data == NULL) {
2768         return NGX_ERROR;
2769     }
2770 
2771     ngx_memcpy(v->data, buf, v->len);
2772 
2773     return NGX_OK;
2774 }
2775 
2776 
2777 static ngx_int_t
2778 ngx_http_ssi_preconfiguration(ngx_conf_t *cf)
2779 {
2780     ngx_int_t                  rc;
2781     ngx_http_variable_t       *var, *v;
2782     ngx_http_ssi_command_t    *cmd;
2783     ngx_http_ssi_main_conf_t  *smcf;
2784 
2785     for (v = ngx_http_ssi_vars; v->name.len; v++) {
2786         var = ngx_http_add_variable(cf, &v->name, v->flags);
2787         if (var == NULL) {
2788             return NGX_ERROR;
2789         }
2790 
2791         var->get_handler = v->get_handler;
2792         var->data = v->data;
2793     }
2794 
2795     smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);
2796 
2797     for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {
2798         rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,
2799                               NGX_HASH_READONLY_KEY);
2800 
2801         if (rc == NGX_OK) {
2802             continue;
2803         }
2804 
2805         if (rc == NGX_BUSY) {
2806             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2807                                "conflicting SSI command \"%V\"", &cmd->name);
2808         }
2809 
2810         return NGX_ERROR;
2811     }
2812 
2813     return NGX_OK;
2814 }
2815 
2816 
2817 static void *
2818 ngx_http_ssi_create_main_conf(ngx_conf_t *cf)
2819 {
2820     ngx_http_ssi_main_conf_t  *smcf;
2821 
2822     smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));
2823     if (smcf == NULL) {
2824         return NULL;
2825     }
2826 
2827     smcf->commands.pool = cf->pool;
2828     smcf->commands.temp_pool = cf->temp_pool;
2829 
2830     if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {
2831         return NULL;
2832     }
2833 
2834     return smcf;
2835 }
2836 
2837 
2838 static char *
2839 ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)
2840 {
2841     ngx_http_ssi_main_conf_t *smcf = conf;
2842 
2843     ngx_hash_init_t  hash;
2844 
2845     hash.hash = &smcf->hash;
2846     hash.key = ngx_hash_key;
2847     hash.max_size = 1024;
2848     hash.bucket_size = ngx_cacheline_size;
2849     hash.name = "ssi_command_hash";
2850     hash.pool = cf->pool;
2851     hash.temp_pool = NULL;
2852 
2853     if (ngx_hash_init(&hash, smcf->commands.keys.elts,
2854                       smcf->commands.keys.nelts)
2855         != NGX_OK)
2856     {
2857         return NGX_CONF_ERROR;
2858     }
2859 
2860     return NGX_CONF_OK;
2861 }
2862 
2863 
2864 static void *
2865 ngx_http_ssi_create_loc_conf(ngx_conf_t *cf)
2866 {
2867     ngx_http_ssi_loc_conf_t  *slcf;
2868 
2869     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
2870     if (slcf == NULL) {
2871         return NULL;
2872     }
2873 
2874     /*
2875      * set by ngx_pcalloc():
2876      *
2877      *     conf->types = { NULL };
2878      *     conf->types_keys = NULL;
2879      */
2880 
2881     slcf->enable = NGX_CONF_UNSET;
2882     slcf->silent_errors = NGX_CONF_UNSET;
2883     slcf->ignore_recycled_buffers = NGX_CONF_UNSET;
2884     slcf->last_modified = NGX_CONF_UNSET;
2885 
2886     slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;
2887     slcf->value_len = NGX_CONF_UNSET_SIZE;
2888 
2889     return slcf;
2890 }
2891 
2892 
2893 static char *
2894 ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
2895 {
2896     ngx_http_ssi_loc_conf_t *prev = parent;
2897     ngx_http_ssi_loc_conf_t *conf = child;
2898 
2899     ngx_conf_merge_value(conf->enable, prev->enable, 0);
2900     ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
2901     ngx_conf_merge_value(conf->ignore_recycled_buffers,
2902                          prev->ignore_recycled_buffers, 0);
2903     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
2904 
2905     ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
2906     ngx_conf_merge_size_value(conf->value_len, prev->value_len, 255);
2907 
2908     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
2909                              &prev->types_keys, &prev->types,
2910                              ngx_http_html_default_types)
2911         != NGX_OK)
2912     {
2913         return NGX_CONF_ERROR;
2914     }
2915 
2916     return NGX_CONF_OK;
2917 }
2918 
2919 
2920 static ngx_int_t
2921 ngx_http_ssi_filter_init(ngx_conf_t *cf)
2922 {
2923     ngx_http_next_header_filter = ngx_http_top_header_filter;
2924     ngx_http_top_header_filter = ngx_http_ssi_header_filter;
2925 
2926     ngx_http_next_body_filter = ngx_http_top_body_filter;
2927     ngx_http_top_body_filter = ngx_http_ssi_body_filter;
2928 
2929     return NGX_OK;
2930 }