ngx_http_ssi_filter_module.c revision e18a033b
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
20typedef 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
35typedef struct {
36    ngx_str_t     name;
37    ngx_uint_t    key;
38    ngx_str_t     value;
39} ngx_http_ssi_var_t;
40
41
42typedef struct {
43    ngx_str_t     name;
44    ngx_chain_t  *bufs;
45    ngx_uint_t    count;
46} ngx_http_ssi_block_t;
47
48
49typedef 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
73static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,
74    ngx_http_ssi_ctx_t *ctx);
75static void ngx_http_ssi_buffered(ngx_http_request_t *r,
76    ngx_http_ssi_ctx_t *ctx);
77static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
78    ngx_http_ssi_ctx_t *ctx);
79static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,
80    ngx_str_t *name, ngx_uint_t key);
81static 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);
83static ngx_int_t ngx_http_ssi_regex_match(ngx_http_request_t *r,
84    ngx_str_t *pattern, ngx_str_t *str);
85
86static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
87    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
88static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,
89    ngx_int_t rc);
90static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,
91    ngx_int_t rc);
92static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
93    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
94static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
95    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
96static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,
97    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
98static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
99    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
100static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,
101    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
102static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
103    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
104static ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,
105    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
106static ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,
107    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
108
109static 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
112static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);
113static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);
114static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);
115static void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);
116static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,
117    void *parent, void *child);
118static ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);
119
120
121static 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
178static 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
193ngx_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
209static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
210static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
211
212
213static u_char ngx_http_ssi_string[] = "<!--";
214
215static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
216static ngx_str_t ngx_http_ssi_timefmt = ngx_string("%A, %d-%b-%Y %H:%M:%S %Z");
217static 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
241static 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
251static 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
259static 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
266static 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
273static 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
279static 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
285static ngx_http_ssi_param_t  ngx_http_ssi_no_params[] = {
286    { ngx_null_string, 0, 0, 0 }
287};
288
289
290static 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
316static 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
329static ngx_int_t
330ngx_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
386static ngx_int_t
387ngx_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
912static ngx_int_t
913ngx_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
976static void
977ngx_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
988static ngx_int_t
989ngx_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
1553static ngx_str_t *
1554ngx_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
1624static ngx_int_t
1625ngx_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
1867invalid_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
1876static ngx_int_t
1877ngx_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
1985static ngx_int_t
1986ngx_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
2201static ngx_int_t
2202ngx_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
2229static ngx_int_t
2230ngx_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
2243static ngx_int_t
2244ngx_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
2379static ngx_int_t
2380ngx_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
2407static ngx_int_t
2408ngx_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
2463static ngx_int_t
2464ngx_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
2628invalid_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
2637static ngx_int_t
2638ngx_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
2656static ngx_int_t
2657ngx_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
2671static ngx_int_t
2672ngx_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
2707static ngx_int_t
2708ngx_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
2721static ngx_int_t
2722ngx_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
2777static ngx_int_t
2778ngx_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
2817static void *
2818ngx_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
2838static char *
2839ngx_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
2864static void *
2865ngx_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
2893static char *
2894ngx_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
2920static ngx_int_t
2921ngx_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}
2931