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