~ [ 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.0.11 ] ~ [ nginx-1.1.12 ] ~

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

~ [ 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.