Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_ssi_filter_module.c
Go to the documentation of this file.
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 {
24 
26 
28  size_t value_len;
29 
32 
33 
34 typedef struct {
39 
40 
41 typedef struct {
46 
47 
48 typedef enum {
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"),
124  |NGX_CONF_FLAG,
127  offsetof(ngx_http_ssi_loc_conf_t, enable),
128  NULL },
129 
130  { ngx_string("ssi_silent_errors"),
134  offsetof(ngx_http_ssi_loc_conf_t, silent_errors),
135  NULL },
136 
137  { ngx_string("ssi_ignore_recycled_buffers"),
141  offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),
142  NULL },
143 
144  { ngx_string("ssi_min_file_chunk"),
148  offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),
149  NULL },
150 
151  { ngx_string("ssi_value_length"),
155  offsetof(ngx_http_ssi_loc_conf_t, value_len),
156  NULL },
157 
158  { ngx_string("ssi_types"),
162  offsetof(ngx_http_ssi_loc_conf_t, types_keys),
164 
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 
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 */
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,
311 
312  { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,
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 {
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 
347  ctx->output = 1;
348 
349  ctx->params.elts = ctx->params_array;
350  ctx->params.size = sizeof(ngx_table_elt_t);
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) {
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;
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 
408  "http ssi filter \"%V?%V\"", &r->uri, &r->args);
409 
410  if (ctx->wait) {
411 
412  if (r != r->connection->data) {
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) {
422  "http ssi filter wait \"%V?%V\" done",
423  &ctx->wait->uri, &ctx->wait->args);
424 
425  ctx->wait = NULL;
426 
427  } else {
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 
456  "saved: %d state: %d", ctx->saved, ctx->state);
457 
458  rc = ngx_http_ssi_parse(r, ctx);
459 
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 
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 
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) {
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  {
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) {
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]) {
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) {
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) {
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 
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) {
908  "ssi out: %p %p", cl->buf, cl->buf->pos);
909  if (cl->buf == b) {
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) {
966 
967  } else {
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,
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) {
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,
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;
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;
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 
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 '"':
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 
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 
1322  switch (ch) {
1323  case '"':
1324  state = ssi_postparam_state;
1325  break;
1326 
1327  case '\\':
1329  state = ssi_quoted_symbol_state;
1330 
1331  /* fall through */
1332 
1333  default:
1334  if (ctx->param->value.len == ctx->value_len) {
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 
1350  switch (ch) {
1351  case '\'':
1352  state = ssi_postparam_state;
1353  break;
1354 
1355  case '\\':
1357  state = ssi_quoted_symbol_state;
1358 
1359  /* fall through */
1360 
1361  default:
1362  if (ctx->param->value.len == ctx->value_len) {
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 
1378  state = ctx->saved_state;
1379 
1380  if (ctx->param->value.len == ctx->value_len) {
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:
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 
1437  switch (ch) {
1438  case '-':
1439  state = ssi_comment_end1_state;
1440  break;
1441 
1442  default:
1444  "unexpected \"%c\" symbol in \"%V\" SSI command",
1445  ch, &ctx->command);
1446  state = ssi_error_state;
1447  break;
1448  }
1449 
1450  break;
1451 
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:
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 >= '0' && key <= '9') {
1554  i = key - '0';
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;
1620 
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 >= '0' && ch <= '9')
1724  || ch == '_')
1725  {
1726  continue;
1727  }
1728 
1729  break;
1730  }
1731 
1732  if (bracket) {
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 
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) {
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 
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;
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) {
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) {
2002  "no parameter in \"include\" SSI command");
2003  return NGX_HTTP_SSI_ERROR;
2004  }
2005 
2006  if (set && stub) {
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) {
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  {
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 
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 
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 
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 {
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 
2210  "ssi stub output: \"%V?%V\"", &r->uri, &r->args);
2211 
2212  out = data;
2213 
2214  if (!r->header_sent) {
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;
2253 
2254  var = params[NGX_HTTP_SSI_ECHO_VAR];
2255 
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 
2299 
2300  } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {
2301 
2303 
2304  } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {
2305 
2307 
2308  } else {
2310  "unknown encoding \"%V\" in the \"echo\" command",
2311  enc);
2312  }
2313  }
2314 
2315  p = value->data;
2316 
2317  switch (ctx->encoding) {
2318 
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 
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 
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 
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) {
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 
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 >= '0' && *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 
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 
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 
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 
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 
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 
2624 
2625  return NGX_OK;
2626 
2627 invalid_expression:
2628 
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 {
2641  "ssi else");
2642 
2643  if (ctx->output_chosen) {
2644  ctx->output = 0;
2645  } else {
2646  ctx->output = 1;
2647  }
2648 
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 {
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;
2676 
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 {
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;
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,
2797 
2798  if (rc == NGX_OK) {
2799  continue;
2800  }
2801 
2802  if (rc == NGX_BUSY) {
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 {
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 
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;
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 {
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;
2881 
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);
2898  prev->ignore_recycled_buffers, 0);
2899 
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,
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 }