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