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