~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_ssi_filter_module.c

Version: ~ [ nginx-1.4.1 ] ~ [ nginx-1.5.0 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.