nginx 0.7.8
[nginx-catap.git] / src / http / modules / ngx_http_ssi_filter_module.c
blob5de41f3252432dbb215a1a8d368512ea32c69434
2 /*
3 * Copyright (C) Igor Sysoev
4 */
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
11 #define NGX_HTTP_SSI_ERROR 1
13 #define NGX_HTTP_SSI_DATE_LEN 2048
15 #define NGX_HTTP_SSI_ADD_PREFIX 1
16 #define NGX_HTTP_SSI_ADD_ZERO 2
17 #define NGX_HTTP_SSI_EXPR_TEST 4
20 typedef struct {
21 ngx_flag_t enable;
22 ngx_flag_t silent_errors;
23 ngx_flag_t ignore_recycled_buffers;
25 ngx_array_t *types; /* array of ngx_str_t */
27 size_t min_file_chunk;
28 size_t value_len;
29 } ngx_http_ssi_loc_conf_t;
32 typedef struct {
33 ngx_str_t name;
34 ngx_uint_t key;
35 ngx_str_t value;
36 } ngx_http_ssi_var_t;
39 typedef struct {
40 ngx_str_t name;
41 ngx_chain_t *bufs;
42 ngx_uint_t count;
43 } ngx_http_ssi_block_t;
46 typedef enum {
47 ssi_start_state = 0,
48 ssi_tag_state,
49 ssi_comment0_state,
50 ssi_comment1_state,
51 ssi_sharp_state,
52 ssi_precommand_state,
53 ssi_command_state,
54 ssi_preparam_state,
55 ssi_param_state,
56 ssi_preequal_state,
57 ssi_prevalue_state,
58 ssi_double_quoted_value_state,
59 ssi_quoted_value_state,
60 ssi_quoted_symbol_state,
61 ssi_postparam_state,
62 ssi_comment_end0_state,
63 ssi_comment_end1_state,
64 ssi_error_state,
65 ssi_error_end0_state,
66 ssi_error_end1_state
67 } ngx_http_ssi_state_e;
70 static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,
71 ngx_http_ssi_ctx_t *ctx);
72 static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
73 ngx_http_ssi_ctx_t *ctx);
74 static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,
75 ngx_str_t *name, ngx_uint_t key);
76 static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,
77 ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);
79 static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
80 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
81 static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,
82 ngx_int_t rc);
83 static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,
84 ngx_int_t rc);
85 static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
86 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
87 static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
88 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
89 static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,
90 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
91 static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
92 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
93 static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,
94 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
95 static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
96 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
97 static ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,
98 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
99 static ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,
100 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
102 static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
103 ngx_http_variable_value_t *v, uintptr_t gmt);
105 static char *ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
107 static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);
108 static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);
109 static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);
110 static void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);
111 static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,
112 void *parent, void *child);
113 static ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);
116 static ngx_command_t ngx_http_ssi_filter_commands[] = {
118 { ngx_string("ssi"),
119 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
120 |NGX_CONF_FLAG,
121 ngx_conf_set_flag_slot,
122 NGX_HTTP_LOC_CONF_OFFSET,
123 offsetof(ngx_http_ssi_loc_conf_t, enable),
124 NULL },
126 { ngx_string("ssi_silent_errors"),
127 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
128 ngx_conf_set_flag_slot,
129 NGX_HTTP_LOC_CONF_OFFSET,
130 offsetof(ngx_http_ssi_loc_conf_t, silent_errors),
131 NULL },
133 { ngx_string("ssi_ignore_recycled_buffers"),
134 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
135 ngx_conf_set_flag_slot,
136 NGX_HTTP_LOC_CONF_OFFSET,
137 offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),
138 NULL },
140 { ngx_string("ssi_min_file_chunk"),
141 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
142 ngx_conf_set_size_slot,
143 NGX_HTTP_LOC_CONF_OFFSET,
144 offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),
145 NULL },
147 { ngx_string("ssi_value_length"),
148 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
149 ngx_conf_set_size_slot,
150 NGX_HTTP_LOC_CONF_OFFSET,
151 offsetof(ngx_http_ssi_loc_conf_t, value_len),
152 NULL },
154 { ngx_string("ssi_types"),
155 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
156 ngx_http_ssi_types,
157 NGX_HTTP_LOC_CONF_OFFSET,
159 NULL },
161 ngx_null_command
166 static ngx_http_module_t ngx_http_ssi_filter_module_ctx = {
167 ngx_http_ssi_preconfiguration, /* preconfiguration */
168 ngx_http_ssi_filter_init, /* postconfiguration */
170 ngx_http_ssi_create_main_conf, /* create main configuration */
171 ngx_http_ssi_init_main_conf, /* init main configuration */
173 NULL, /* create server configuration */
174 NULL, /* merge server configuration */
176 ngx_http_ssi_create_loc_conf, /* create location configuration */
177 ngx_http_ssi_merge_loc_conf /* merge location configuration */
181 ngx_module_t ngx_http_ssi_filter_module = {
182 NGX_MODULE_V1,
183 &ngx_http_ssi_filter_module_ctx, /* module context */
184 ngx_http_ssi_filter_commands, /* module directives */
185 NGX_HTTP_MODULE, /* module type */
186 NULL, /* init master */
187 NULL, /* init module */
188 NULL, /* init process */
189 NULL, /* init thread */
190 NULL, /* exit thread */
191 NULL, /* exit process */
192 NULL, /* exit master */
193 NGX_MODULE_V1_PADDING
197 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
198 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
201 static u_char ngx_http_ssi_string[] = "<!--";
203 static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
204 static ngx_str_t ngx_http_ssi_null_string = ngx_null_string;
207 #define NGX_HTTP_SSI_INCLUDE_VIRTUAL 0
208 #define NGX_HTTP_SSI_INCLUDE_FILE 1
209 #define NGX_HTTP_SSI_INCLUDE_WAIT 2
210 #define NGX_HTTP_SSI_INCLUDE_SET 3
211 #define NGX_HTTP_SSI_INCLUDE_STUB 4
213 #define NGX_HTTP_SSI_ECHO_VAR 0
214 #define NGX_HTTP_SSI_ECHO_DEFAULT 1
215 #define NGX_HTTP_SSI_ECHO_ENCODING 2
217 #define NGX_HTTP_SSI_CONFIG_ERRMSG 0
218 #define NGX_HTTP_SSI_CONFIG_TIMEFMT 1
220 #define NGX_HTTP_SSI_SET_VAR 0
221 #define NGX_HTTP_SSI_SET_VALUE 1
223 #define NGX_HTTP_SSI_IF_EXPR 0
225 #define NGX_HTTP_SSI_BLOCK_NAME 0
228 static ngx_http_ssi_param_t ngx_http_ssi_include_params[] = {
229 { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },
230 { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },
231 { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT, 0, 0 },
232 { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET, 0, 0 },
233 { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB, 0, 0 },
234 { ngx_null_string, 0, 0, 0 }
238 static ngx_http_ssi_param_t ngx_http_ssi_echo_params[] = {
239 { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },
240 { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },
241 { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 },
242 { ngx_null_string, 0, 0, 0 }
246 static ngx_http_ssi_param_t ngx_http_ssi_config_params[] = {
247 { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0, 0 },
248 { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0, 0 },
249 { ngx_null_string, 0, 0, 0 }
253 static ngx_http_ssi_param_t ngx_http_ssi_set_params[] = {
254 { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1, 0 },
255 { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1, 0 },
256 { ngx_null_string, 0, 0, 0 }
260 static ngx_http_ssi_param_t ngx_http_ssi_if_params[] = {
261 { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1, 0 },
262 { ngx_null_string, 0, 0, 0 }
266 static ngx_http_ssi_param_t ngx_http_ssi_block_params[] = {
267 { ngx_string("name"), NGX_HTTP_SSI_BLOCK_NAME, 1, 0 },
268 { ngx_null_string, 0, 0, 0 }
272 static ngx_http_ssi_param_t ngx_http_ssi_no_params[] = {
273 { ngx_null_string, 0, 0, 0 }
277 static ngx_http_ssi_command_t ngx_http_ssi_commands[] = {
278 { ngx_string("include"), ngx_http_ssi_include,
279 ngx_http_ssi_include_params, 0, 0, 1 },
280 { ngx_string("echo"), ngx_http_ssi_echo,
281 ngx_http_ssi_echo_params, 0, 0, 0 },
282 { ngx_string("config"), ngx_http_ssi_config,
283 ngx_http_ssi_config_params, 0, 0, 0 },
284 { ngx_string("set"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0, 0 },
286 { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0, 0 },
287 { ngx_string("elif"), ngx_http_ssi_if, ngx_http_ssi_if_params,
288 NGX_HTTP_SSI_COND_IF, 0, 0 },
289 { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params,
290 NGX_HTTP_SSI_COND_IF, 0, 0 },
291 { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params,
292 NGX_HTTP_SSI_COND_ELSE, 0, 0 },
294 { ngx_string("block"), ngx_http_ssi_block,
295 ngx_http_ssi_block_params, 0, 0, 0 },
296 { ngx_string("endblock"), ngx_http_ssi_endblock,
297 ngx_http_ssi_no_params, 0, 1, 0 },
299 { ngx_null_string, NULL, NULL, 0, 0, 0 }
303 static ngx_http_variable_t ngx_http_ssi_vars[] = {
305 { ngx_string("date_local"), NULL, ngx_http_ssi_date_gmt_local_variable, 0,
306 NGX_HTTP_VAR_NOCACHEABLE, 0 },
308 { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,
309 NGX_HTTP_VAR_NOCACHEABLE, 0 },
311 { ngx_null_string, NULL, NULL, 0, 0, 0 }
316 static ngx_int_t
317 ngx_http_ssi_header_filter(ngx_http_request_t *r)
319 ngx_uint_t i;
320 ngx_str_t *type;
321 ngx_http_ssi_ctx_t *ctx;
322 ngx_http_ssi_loc_conf_t *slcf;
324 slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
326 if (!slcf->enable
327 || r->headers_out.content_type.len == 0
328 || r->headers_out.content_length_n == 0)
330 return ngx_http_next_header_filter(r);
334 type = slcf->types->elts;
335 for (i = 0; i < slcf->types->nelts; i++) {
336 if (r->headers_out.content_type.len >= type[i].len
337 && ngx_strncasecmp(r->headers_out.content_type.data,
338 type[i].data, type[i].len) == 0)
340 goto found;
344 return ngx_http_next_header_filter(r);
347 found:
349 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));
350 if (ctx == NULL) {
351 return NGX_ERROR;
354 ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);
357 ctx->value_len = slcf->value_len;
358 ctx->last_out = &ctx->out;
360 ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
361 ctx->output = 1;
363 ctx->params.elts = ctx->params_array;
364 ctx->params.size = sizeof(ngx_table_elt_t);
365 ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;
366 ctx->params.pool = r->pool;
368 ctx->timefmt.len = sizeof("%A, %d-%b-%Y %H:%M:%S %Z") - 1;
369 ctx->timefmt.data = (u_char *) "%A, %d-%b-%Y %H:%M:%S %Z";
371 ctx->errmsg.len =
372 sizeof("[an error occurred while processing the directive]") - 1;
373 ctx->errmsg.data = (u_char *)
374 "[an error occurred while processing the directive]";
376 r->filter_need_in_memory = 1;
378 if (r == r->main) {
379 ngx_http_clear_content_length(r);
380 ngx_http_clear_last_modified(r);
383 return ngx_http_next_header_filter(r);
387 static ngx_int_t
388 ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
390 size_t len;
391 ngx_int_t rc;
392 ngx_buf_t *b;
393 ngx_uint_t i, index;
394 ngx_chain_t *cl, **ll;
395 ngx_table_elt_t *param;
396 ngx_http_request_t *pr;
397 ngx_http_ssi_ctx_t *ctx, *mctx;
398 ngx_http_ssi_block_t *bl;
399 ngx_http_ssi_param_t *prm;
400 ngx_http_ssi_command_t *cmd;
401 ngx_http_ssi_loc_conf_t *slcf;
402 ngx_http_ssi_main_conf_t *smcf;
403 ngx_str_t *params[NGX_HTTP_SSI_MAX_PARAMS + 1];
405 ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
407 if (ctx == NULL
408 || (in == NULL
409 && ctx->buf == NULL
410 && ctx->in == NULL
411 && ctx->busy == NULL))
413 return ngx_http_next_body_filter(r, in);
416 /* add the incoming chain to the chain ctx->in */
418 if (in) {
419 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
420 return NGX_ERROR;
424 if (ctx->wait) {
425 if (r->connection->data != r) {
426 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
427 "http ssi filter \"%V\" wait", &r->uri);
428 return NGX_AGAIN;
431 for (pr = ctx->wait->parent; pr; pr = pr->parent) {
432 if (pr == r) {
433 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
434 "http ssi filter \"%V\" flush", &r->uri);
436 rc = ngx_http_next_body_filter(r, NULL);
438 if (ctx->wait->done) {
439 ctx->wait = NULL;
442 if (rc == NGX_ERROR || rc == NGX_AGAIN) {
443 return rc;
446 break;
450 if (ctx->wait == r) {
451 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
452 "http ssi filter \"%V\" continue", &r->uri);
453 ctx->wait = NULL;
457 slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
459 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
460 "http ssi filter \"%V\"", &r->uri);
462 while (ctx->in || ctx->buf) {
464 if (ctx->buf == NULL ){
465 ctx->buf = ctx->in->buf;
466 ctx->in = ctx->in->next;
467 ctx->pos = ctx->buf->pos;
470 if (ctx->state == ssi_start_state) {
471 ctx->copy_start = ctx->pos;
472 ctx->copy_end = ctx->pos;
475 b = NULL;
477 while (ctx->pos < ctx->buf->last) {
479 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
480 "saved: %d state: %d", ctx->saved, ctx->state);
482 rc = ngx_http_ssi_parse(r, ctx);
484 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
485 "parse: %d, looked: %d %p-%p",
486 rc, ctx->looked, ctx->copy_start, ctx->copy_end);
488 if (rc == NGX_ERROR) {
489 return rc;
492 if (ctx->copy_start != ctx->copy_end) {
494 if (ctx->output) {
496 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
497 "saved: %d", ctx->saved);
499 if (ctx->saved) {
501 if (ctx->free) {
502 cl = ctx->free;
503 ctx->free = ctx->free->next;
504 b = cl->buf;
505 ngx_memzero(b, sizeof(ngx_buf_t));
507 } else {
508 b = ngx_calloc_buf(r->pool);
509 if (b == NULL) {
510 return NGX_ERROR;
513 cl = ngx_alloc_chain_link(r->pool);
514 if (cl == NULL) {
515 return NGX_ERROR;
518 cl->buf = b;
521 b->memory = 1;
522 b->pos = ngx_http_ssi_string;
523 b->last = ngx_http_ssi_string + ctx->saved;
525 *ctx->last_out = cl;
526 ctx->last_out = &cl->next;
528 ctx->saved = 0;
531 if (ctx->free) {
532 cl = ctx->free;
533 ctx->free = ctx->free->next;
534 b = cl->buf;
536 } else {
537 b = ngx_alloc_buf(r->pool);
538 if (b == NULL) {
539 return NGX_ERROR;
542 cl = ngx_alloc_chain_link(r->pool);
543 if (cl == NULL) {
544 return NGX_ERROR;
547 cl->buf = b;
550 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
552 b->pos = ctx->copy_start;
553 b->last = ctx->copy_end;
554 b->shadow = NULL;
555 b->last_buf = 0;
556 b->recycled = 0;
558 if (b->in_file) {
559 if (slcf->min_file_chunk < (size_t) (b->last - b->pos))
561 b->file_last = b->file_pos
562 + (b->last - ctx->buf->pos);
563 b->file_pos += b->pos - ctx->buf->pos;
565 } else {
566 b->in_file = 0;
570 cl->next = NULL;
571 *ctx->last_out = cl;
572 ctx->last_out = &cl->next;
574 } else {
575 if (ctx->block
576 && ctx->saved + (ctx->copy_end - ctx->copy_start))
578 b = ngx_create_temp_buf(r->pool,
579 ctx->saved + (ctx->copy_end - ctx->copy_start));
581 if (b == NULL) {
582 return NGX_ERROR;
585 if (ctx->saved) {
586 b->last = ngx_cpymem(b->pos, ngx_http_ssi_string,
587 ctx->saved);
590 b->last = ngx_cpymem(b->last, ctx->copy_start,
591 ctx->copy_end - ctx->copy_start);
593 cl = ngx_alloc_chain_link(r->pool);
594 if (cl == NULL) {
595 return NGX_ERROR;
598 cl->buf = b;
599 cl->next = NULL;
601 b = NULL;
603 mctx = ngx_http_get_module_ctx(r->main,
604 ngx_http_ssi_filter_module);
605 bl = mctx->blocks->elts;
606 for (ll = &bl[mctx->blocks->nelts - 1].bufs;
607 *ll;
608 ll = &(*ll)->next)
610 /* void */
613 *ll = cl;
616 ctx->saved = 0;
620 if (ctx->state == ssi_start_state) {
621 ctx->copy_start = ctx->pos;
622 ctx->copy_end = ctx->pos;
624 } else {
625 ctx->copy_start = NULL;
626 ctx->copy_end = NULL;
629 if (rc == NGX_AGAIN) {
630 continue;
634 b = NULL;
636 if (rc == NGX_OK) {
638 smcf = ngx_http_get_module_main_conf(r,
639 ngx_http_ssi_filter_module);
641 cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,
642 ctx->command.len);
644 if (cmd == NULL) {
645 if (ctx->output) {
646 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
647 "invalid SSI command: \"%V\"",
648 &ctx->command);
649 goto ssi_error;
652 continue;
655 if (cmd->conditional
656 && (ctx->conditional == 0
657 || ctx->conditional > cmd->conditional))
659 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
660 "invalid context of SSI command: \"%V\"",
661 &ctx->command);
662 goto ssi_error;
665 if (!ctx->output && !cmd->block) {
667 if (ctx->block) {
669 /* reconstruct the SSI command text */
671 len = 5 + ctx->command.len + 4;
673 param = ctx->params.elts;
674 for (i = 0; i < ctx->params.nelts; i++) {
675 len += 1 + param[i].key.len + 2
676 + param[i].value.len + 1;
679 b = ngx_create_temp_buf(r->pool, len);
681 if (b == NULL) {
682 return NGX_ERROR;
685 cl = ngx_alloc_chain_link(r->pool);
686 if (cl == NULL) {
687 return NGX_ERROR;
690 cl->buf = b;
691 cl->next = NULL;
693 *b->last++ = '<';
694 *b->last++ = '!';
695 *b->last++ = '-';
696 *b->last++ = '-';
697 *b->last++ = '#';
699 b->last = ngx_cpymem(b->last, ctx->command.data,
700 ctx->command.len);
702 for (i = 0; i < ctx->params.nelts; i++) {
703 *b->last++ = ' ';
704 b->last = ngx_cpymem(b->last, param[i].key.data,
705 param[i].key.len);
706 *b->last++ = '=';
707 *b->last++ = '"';
708 b->last = ngx_cpymem(b->last, param[i].value.data,
709 param[i].value.len);
710 *b->last++ = '"';
713 *b->last++ = ' ';
714 *b->last++ = '-';
715 *b->last++ = '-';
716 *b->last++ = '>';
718 mctx = ngx_http_get_module_ctx(r->main,
719 ngx_http_ssi_filter_module);
720 bl = mctx->blocks->elts;
721 for (ll = &bl[mctx->blocks->nelts - 1].bufs;
722 *ll;
723 ll = &(*ll)->next)
725 /* void */
728 *ll = cl;
730 b = NULL;
732 continue;
735 if (cmd->conditional == 0) {
736 continue;
740 if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {
741 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
742 "too many SSI command paramters: \"%V\"",
743 &ctx->command);
744 goto ssi_error;
747 ngx_memzero(params,
748 (NGX_HTTP_SSI_MAX_PARAMS + 1) * sizeof(ngx_str_t *));
750 param = ctx->params.elts;
752 for (i = 0; i < ctx->params.nelts; i++) {
754 for (prm = cmd->params; prm->name.len; prm++) {
756 if (param[i].key.len != prm->name.len
757 || ngx_strncmp(param[i].key.data, prm->name.data,
758 prm->name.len) != 0)
760 continue;
763 if (!prm->multiple) {
764 if (params[prm->index]) {
765 ngx_log_error(NGX_LOG_ERR,
766 r->connection->log, 0,
767 "duplicate \"%V\" parameter "
768 "in \"%V\" SSI command",
769 &param[i].key, &ctx->command);
771 goto ssi_error;
774 params[prm->index] = &param[i].value;
776 break;
779 for (index = prm->index; params[index]; index++) {
780 /* void */
783 params[index] = &param[i].value;
785 break;
788 if (prm->name.len == 0) {
789 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
790 "invalid parameter name: \"%V\" "
791 "in \"%V\" SSI command",
792 &param[i].key, &ctx->command);
794 goto ssi_error;
798 for (prm = cmd->params; prm->name.len; prm++) {
799 if (prm->mandatory && params[prm->index] == 0) {
800 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
801 "mandatory \"%V\" parameter is absent "
802 "in \"%V\" SSI command",
803 &prm->name, &ctx->command);
805 goto ssi_error;
809 if (cmd->flush) {
811 if (ctx->out) {
812 rc = ngx_http_ssi_output(r, ctx);
814 } else {
815 rc = ngx_http_next_body_filter(r, NULL);
818 if (rc == NGX_ERROR) {
819 return NGX_ERROR;
823 rc = cmd->handler(r, ctx, params);
825 if (rc == NGX_OK) {
826 continue;
829 if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {
830 return rc;
835 /* rc == NGX_HTTP_SSI_ERROR */
837 ssi_error:
839 if (slcf->silent_errors) {
840 continue;
843 if (ctx->free) {
844 cl = ctx->free;
845 ctx->free = ctx->free->next;
846 b = cl->buf;
847 ngx_memzero(b, sizeof(ngx_buf_t));
849 } else {
850 b = ngx_calloc_buf(r->pool);
851 if (b == NULL) {
852 return NGX_ERROR;
855 cl = ngx_alloc_chain_link(r->pool);
856 if (cl == NULL) {
857 return NGX_ERROR;
860 cl->buf = b;
863 b->memory = 1;
864 b->pos = ctx->errmsg.data;
865 b->last = ctx->errmsg.data + ctx->errmsg.len;
867 cl->next = NULL;
868 *ctx->last_out = cl;
869 ctx->last_out = &cl->next;
871 continue;
874 if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {
875 if (b == NULL) {
876 if (ctx->free) {
877 cl = ctx->free;
878 ctx->free = ctx->free->next;
879 b = cl->buf;
880 ngx_memzero(b, sizeof(ngx_buf_t));
882 } else {
883 b = ngx_calloc_buf(r->pool);
884 if (b == NULL) {
885 return NGX_ERROR;
888 cl = ngx_alloc_chain_link(r->pool);
889 if (cl == NULL) {
890 return NGX_ERROR;
893 cl->buf = b;
896 b->sync = 1;
898 cl->next = NULL;
899 *ctx->last_out = cl;
900 ctx->last_out = &cl->next;
903 b->last_buf = ctx->buf->last_buf;
904 b->shadow = ctx->buf;
906 if (slcf->ignore_recycled_buffers == 0) {
907 b->recycled = ctx->buf->recycled;
911 ctx->buf = NULL;
913 ctx->saved = ctx->looked;
916 if (ctx->out == NULL && ctx->busy == NULL) {
917 return NGX_OK;
920 return ngx_http_ssi_output(r, ctx);
924 static ngx_int_t
925 ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
927 ngx_int_t rc;
928 ngx_buf_t *b;
929 ngx_chain_t *cl;
931 #if 1
932 b = NULL;
933 for (cl = ctx->out; cl; cl = cl->next) {
934 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
935 "ssi out: %p %p", cl->buf, cl->buf->pos);
936 if (cl->buf == b) {
937 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
938 "the same buf was used in ssi");
939 ngx_debug_point();
940 return NGX_ERROR;
942 b = cl->buf;
944 #endif
946 rc = ngx_http_next_body_filter(r, ctx->out);
948 if (ctx->busy == NULL) {
949 ctx->busy = ctx->out;
951 } else {
952 for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
953 cl->next = ctx->out;
956 ctx->out = NULL;
957 ctx->last_out = &ctx->out;
959 while (ctx->busy) {
961 cl = ctx->busy;
962 b = cl->buf;
964 if (ngx_buf_size(b) != 0) {
965 break;
968 #if (NGX_HAVE_WRITE_ZEROCOPY)
969 if (b->zerocopy_busy) {
970 break;
972 #endif
974 if (b->shadow) {
975 b->shadow->pos = b->shadow->last;
978 ctx->busy = cl->next;
980 if (ngx_buf_in_memory(b) || b->in_file) {
981 /* add data bufs only to the free buf chain */
983 cl->next = ctx->free;
984 ctx->free = cl;
988 if (ctx->in || ctx->buf) {
989 r->buffered |= NGX_HTTP_SSI_BUFFERED;
991 } else {
992 r->buffered &= ~NGX_HTTP_SSI_BUFFERED;
995 return rc;
999 static ngx_int_t
1000 ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
1002 u_char *p, *value, *last, *copy_end, ch;
1003 size_t looked;
1004 ngx_http_ssi_state_e state;
1006 state = ctx->state;
1007 looked = ctx->looked;
1008 last = ctx->buf->last;
1009 copy_end = ctx->copy_end;
1011 for (p = ctx->pos; p < last; p++) {
1013 ch = *p;
1015 if (state == ssi_start_state) {
1017 /* the tight loop */
1019 for ( ;; ) {
1020 if (ch == '<') {
1021 copy_end = p;
1022 looked = 1;
1023 state = ssi_tag_state;
1025 goto tag_started;
1028 if (++p == last) {
1029 break;
1032 ch = *p;
1035 ctx->state = state;
1036 ctx->pos = p;
1037 ctx->looked = looked;
1038 ctx->copy_end = p;
1040 if (ctx->copy_start == NULL) {
1041 ctx->copy_start = ctx->buf->pos;
1044 return NGX_AGAIN;
1046 tag_started:
1048 continue;
1051 switch (state) {
1053 case ssi_start_state:
1054 break;
1056 case ssi_tag_state:
1057 switch (ch) {
1058 case '!':
1059 looked = 2;
1060 state = ssi_comment0_state;
1061 break;
1063 case '<':
1064 copy_end = p;
1065 break;
1067 default:
1068 copy_end = p;
1069 looked = 0;
1070 state = ssi_start_state;
1071 break;
1074 break;
1076 case ssi_comment0_state:
1077 switch (ch) {
1078 case '-':
1079 looked = 3;
1080 state = ssi_comment1_state;
1081 break;
1083 case '<':
1084 copy_end = p;
1085 looked = 1;
1086 state = ssi_tag_state;
1087 break;
1089 default:
1090 copy_end = p;
1091 looked = 0;
1092 state = ssi_start_state;
1093 break;
1096 break;
1098 case ssi_comment1_state:
1099 switch (ch) {
1100 case '-':
1101 looked = 4;
1102 state = ssi_sharp_state;
1103 break;
1105 case '<':
1106 copy_end = p;
1107 looked = 1;
1108 state = ssi_tag_state;
1109 break;
1111 default:
1112 copy_end = p;
1113 looked = 0;
1114 state = ssi_start_state;
1115 break;
1118 break;
1120 case ssi_sharp_state:
1121 switch (ch) {
1122 case '#':
1123 if (p - ctx->pos < 4) {
1124 ctx->saved = 0;
1126 looked = 0;
1127 state = ssi_precommand_state;
1128 break;
1130 case '<':
1131 copy_end = p;
1132 looked = 1;
1133 state = ssi_tag_state;
1134 break;
1136 default:
1137 copy_end = p;
1138 looked = 0;
1139 state = ssi_start_state;
1140 break;
1143 break;
1145 case ssi_precommand_state:
1146 switch (ch) {
1147 case ' ':
1148 case CR:
1149 case LF:
1150 case '\t':
1151 break;
1153 default:
1154 ctx->command.len = 1;
1155 ctx->command.data = ngx_pnalloc(r->pool,
1156 NGX_HTTP_SSI_COMMAND_LEN);
1157 if (ctx->command.data == NULL) {
1158 return NGX_ERROR;
1161 ctx->command.data[0] = ch;
1163 ctx->key = 0;
1164 ctx->key = ngx_hash(ctx->key, ch);
1166 ctx->params.nelts = 0;
1168 state = ssi_command_state;
1169 break;
1172 break;
1174 case ssi_command_state:
1175 switch (ch) {
1176 case ' ':
1177 case CR:
1178 case LF:
1179 case '\t':
1180 state = ssi_preparam_state;
1181 break;
1183 case '-':
1184 state = ssi_comment_end0_state;
1185 break;
1187 default:
1188 if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {
1189 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1190 "the \"%V%c...\" SSI command is too long",
1191 &ctx->command, ch);
1193 state = ssi_error_state;
1194 break;
1197 ctx->command.data[ctx->command.len++] = ch;
1198 ctx->key = ngx_hash(ctx->key, ch);
1201 break;
1203 case ssi_preparam_state:
1204 switch (ch) {
1205 case ' ':
1206 case CR:
1207 case LF:
1208 case '\t':
1209 break;
1211 case '-':
1212 state = ssi_comment_end0_state;
1213 break;
1215 default:
1216 ctx->param = ngx_array_push(&ctx->params);
1217 if (ctx->param == NULL) {
1218 return NGX_ERROR;
1221 ctx->param->key.len = 1;
1222 ctx->param->key.data = ngx_pnalloc(r->pool,
1223 NGX_HTTP_SSI_PARAM_LEN);
1224 if (ctx->param->key.data == NULL) {
1225 return NGX_ERROR;
1228 ctx->param->key.data[0] = ch;
1230 ctx->param->value.len = 0;
1232 if (ctx->value_buf == NULL) {
1233 ctx->param->value.data = ngx_pnalloc(r->pool,
1234 ctx->value_len);
1235 if (ctx->param->value.data == NULL) {
1236 return NGX_ERROR;
1239 } else {
1240 ctx->param->value.data = ctx->value_buf;
1243 state = ssi_param_state;
1244 break;
1247 break;
1249 case ssi_param_state:
1250 switch (ch) {
1251 case ' ':
1252 case CR:
1253 case LF:
1254 case '\t':
1255 state = ssi_preequal_state;
1256 break;
1258 case '=':
1259 state = ssi_prevalue_state;
1260 break;
1262 case '-':
1263 state = ssi_error_end0_state;
1265 ctx->param->key.data[ctx->param->key.len++] = ch;
1266 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1267 "invalid \"%V\" parameter in \"%V\" SSI command",
1268 &ctx->param->key, &ctx->command);
1269 break;
1271 default:
1272 if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {
1273 state = ssi_error_state;
1274 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1275 "too long \"%V%c...\" parameter in "
1276 "\"%V\" SSI command",
1277 &ctx->param->key, ch, &ctx->command);
1278 break;
1281 ctx->param->key.data[ctx->param->key.len++] = ch;
1284 break;
1286 case ssi_preequal_state:
1287 switch (ch) {
1288 case ' ':
1289 case CR:
1290 case LF:
1291 case '\t':
1292 break;
1294 case '=':
1295 state = ssi_prevalue_state;
1296 break;
1298 default:
1299 if (ch == '-') {
1300 state = ssi_error_end0_state;
1301 } else {
1302 state = ssi_error_state;
1305 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1306 "unexpected \"%c\" symbol after \"%V\" "
1307 "parameter in \"%V\" SSI command",
1308 ch, &ctx->param->key, &ctx->command);
1309 break;
1312 break;
1314 case ssi_prevalue_state:
1315 switch (ch) {
1316 case ' ':
1317 case CR:
1318 case LF:
1319 case '\t':
1320 break;
1322 case '"':
1323 state = ssi_double_quoted_value_state;
1324 break;
1326 case '\'':
1327 state = ssi_quoted_value_state;
1328 break;
1330 default:
1331 if (ch == '-') {
1332 state = ssi_error_end0_state;
1333 } else {
1334 state = ssi_error_state;
1337 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1338 "unexpected \"%c\" symbol before value of "
1339 "\"%V\" parameter in \"%V\" SSI command",
1340 ch, &ctx->param->key, &ctx->command);
1341 break;
1344 break;
1346 case ssi_double_quoted_value_state:
1347 switch (ch) {
1348 case '"':
1349 state = ssi_postparam_state;
1350 break;
1352 case '\\':
1353 ctx->saved_state = ssi_double_quoted_value_state;
1354 state = ssi_quoted_symbol_state;
1356 /* fall through */
1358 default:
1359 if (ctx->param->value.len == ctx->value_len) {
1360 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1361 "too long \"%V%c...\" value of \"%V\" "
1362 "parameter in \"%V\" SSI command",
1363 &ctx->param->value, ch, &ctx->param->key,
1364 &ctx->command);
1365 state = ssi_error_state;
1366 break;
1369 ctx->param->value.data[ctx->param->value.len++] = ch;
1372 break;
1374 case ssi_quoted_value_state:
1375 switch (ch) {
1376 case '\'':
1377 state = ssi_postparam_state;
1378 break;
1380 case '\\':
1381 ctx->saved_state = ssi_quoted_value_state;
1382 state = ssi_quoted_symbol_state;
1384 /* fall through */
1386 default:
1387 if (ctx->param->value.len == ctx->value_len) {
1388 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1389 "too long \"%V%c...\" value of \"%V\" "
1390 "parameter in \"%V\" SSI command",
1391 &ctx->param->value, ch, &ctx->param->key,
1392 &ctx->command);
1393 state = ssi_error_state;
1394 break;
1397 ctx->param->value.data[ctx->param->value.len++] = ch;
1400 break;
1402 case ssi_quoted_symbol_state:
1403 state = ctx->saved_state;
1405 ctx->param->value.data[ctx->param->value.len++] = ch;
1407 break;
1409 case ssi_postparam_state:
1411 if (ctx->param->value.len + 1 < ctx->value_len / 2) {
1412 value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);
1413 if (value == NULL) {
1414 return NGX_ERROR;
1417 ngx_memcpy(value, ctx->param->value.data,
1418 ctx->param->value.len);
1420 ctx->value_buf = ctx->param->value.data;
1421 ctx->param->value.data = value;
1423 } else {
1424 ctx->value_buf = NULL;
1427 switch (ch) {
1428 case ' ':
1429 case CR:
1430 case LF:
1431 case '\t':
1432 state = ssi_preparam_state;
1433 break;
1435 case '-':
1436 state = ssi_comment_end0_state;
1437 break;
1439 default:
1440 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1441 "unexpected \"%c\" symbol after \"%V\" value "
1442 "of \"%V\" parameter in \"%V\" SSI command",
1443 ch, &ctx->param->value, &ctx->param->key,
1444 &ctx->command);
1445 state = ssi_error_state;
1446 break;
1449 break;
1451 case ssi_comment_end0_state:
1452 switch (ch) {
1453 case '-':
1454 state = ssi_comment_end1_state;
1455 break;
1457 default:
1458 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1459 "unexpected \"%c\" symbol in \"%V\" SSI command",
1460 ch, &ctx->command);
1461 state = ssi_error_state;
1462 break;
1465 break;
1467 case ssi_comment_end1_state:
1468 switch (ch) {
1469 case '>':
1470 ctx->state = ssi_start_state;
1471 ctx->pos = p + 1;
1472 ctx->looked = looked;
1473 ctx->copy_end = copy_end;
1475 if (ctx->copy_start == NULL && copy_end) {
1476 ctx->copy_start = ctx->buf->pos;
1479 return NGX_OK;
1481 default:
1482 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1483 "unexpected \"%c\" symbol in \"%V\" SSI command",
1484 ch, &ctx->command);
1485 state = ssi_error_state;
1486 break;
1489 break;
1491 case ssi_error_state:
1492 switch (ch) {
1493 case '-':
1494 state = ssi_error_end0_state;
1495 break;
1497 default:
1498 break;
1501 break;
1503 case ssi_error_end0_state:
1504 switch (ch) {
1505 case '-':
1506 state = ssi_error_end1_state;
1507 break;
1509 default:
1510 state = ssi_error_state;
1511 break;
1514 break;
1516 case ssi_error_end1_state:
1517 switch (ch) {
1518 case '>':
1519 ctx->state = ssi_start_state;
1520 ctx->pos = p + 1;
1521 ctx->looked = looked;
1522 ctx->copy_end = copy_end;
1524 if (ctx->copy_start == NULL && copy_end) {
1525 ctx->copy_start = ctx->buf->pos;
1528 return NGX_HTTP_SSI_ERROR;
1530 default:
1531 state = ssi_error_state;
1532 break;
1535 break;
1539 ctx->state = state;
1540 ctx->pos = p;
1541 ctx->looked = looked;
1543 ctx->copy_end = (state == ssi_start_state) ? p : copy_end;
1545 if (ctx->copy_start == NULL && ctx->copy_end) {
1546 ctx->copy_start = ctx->buf->pos;
1549 return NGX_AGAIN;
1553 static ngx_str_t *
1554 ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name,
1555 ngx_uint_t key)
1557 ngx_uint_t i;
1558 ngx_list_part_t *part;
1559 ngx_http_ssi_var_t *var;
1560 ngx_http_ssi_ctx_t *ctx;
1562 ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
1564 if (ctx->variables == NULL) {
1565 return NULL;
1568 part = &ctx->variables->part;
1569 var = part->elts;
1571 for (i = 0; /* void */ ; i++) {
1573 if (i >= part->nelts) {
1574 if (part->next == NULL) {
1575 break;
1578 part = part->next;
1579 var = part->elts;
1580 i = 0;
1583 if (name->len != var[i].name.len) {
1584 continue;
1587 if (key != var[i].key) {
1588 continue;
1591 if (ngx_strncmp(name->data, var[i].name.data, name->len) == 0) {
1592 return &var[i].value;
1596 return NULL;
1600 static ngx_int_t
1601 ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1602 ngx_str_t *text, ngx_uint_t flags)
1604 u_char ch, *p, **value, *data, *part_data;
1605 size_t *size, len, prefix, part_len;
1606 ngx_str_t var, *val;
1607 ngx_int_t key;
1608 ngx_uint_t i, n, bracket, quoted;
1609 ngx_array_t lengths, values;
1610 ngx_http_variable_value_t *vv;
1612 n = ngx_http_script_variables_count(text);
1614 if (n == 0) {
1616 data = text->data;
1617 p = data;
1619 if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') {
1621 for (prefix = r->uri.len; prefix; prefix--) {
1622 if (r->uri.data[prefix - 1] == '/') {
1623 break;
1627 if (prefix) {
1628 len = prefix + text->len;
1630 data = ngx_pnalloc(r->pool, len);
1631 if (data == NULL) {
1632 return NGX_ERROR;
1635 p = ngx_copy(data, r->uri.data, prefix);
1639 quoted = 0;
1641 for (i = 0; i < text->len; i++) {
1642 ch = text->data[i];
1644 if (!quoted) {
1646 if (ch == '\\') {
1647 quoted = 1;
1648 continue;
1651 } else {
1652 quoted = 0;
1654 if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
1655 *p++ = '\\';
1659 *p++ = ch;
1662 text->len = p - data;
1663 text->data = data;
1665 return NGX_OK;
1668 if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
1669 return NGX_ERROR;
1672 if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
1673 return NGX_ERROR;
1676 len = 0;
1677 i = 0;
1679 while (i < text->len) {
1681 if (text->data[i] == '$') {
1683 var.len = 0;
1685 if (++i == text->len) {
1686 goto invalid_variable;
1689 if (text->data[i] == '{') {
1690 bracket = 1;
1692 if (++i == text->len) {
1693 goto invalid_variable;
1696 var.data = &text->data[i];
1698 } else {
1699 bracket = 0;
1700 var.data = &text->data[i];
1703 for ( /* void */ ; i < text->len; i++, var.len++) {
1704 ch = text->data[i];
1706 if (ch == '}' && bracket) {
1707 i++;
1708 bracket = 0;
1709 break;
1712 if ((ch >= 'A' && ch <= 'Z')
1713 || (ch >= 'a' && ch <= 'z')
1714 || (ch >= '0' && ch <= '9')
1715 || ch == '_')
1717 continue;
1720 break;
1723 if (bracket) {
1724 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1725 "the closing bracket in \"%V\" "
1726 "variable is missing", &var);
1727 return NGX_HTTP_SSI_ERROR;
1730 if (var.len == 0) {
1731 goto invalid_variable;
1734 key = ngx_hash_strlow(var.data, var.data, var.len);
1736 val = ngx_http_ssi_get_variable(r, &var, key);
1738 if (val == NULL) {
1739 vv = ngx_http_get_variable(r, &var, key,
1740 flags & NGX_HTTP_SSI_EXPR_TEST);
1741 if (vv == NULL) {
1742 return NGX_ERROR;
1745 if (vv->not_found) {
1746 continue;
1749 part_data = vv->data;
1750 part_len = vv->len;
1752 } else {
1753 part_data = val->data;
1754 part_len = val->len;
1757 } else {
1758 part_data = &text->data[i];
1759 quoted = 0;
1761 for (p = part_data; i < text->len; i++) {
1762 ch = text->data[i];
1764 if (!quoted) {
1766 if (ch == '\\') {
1767 quoted = 1;
1768 continue;
1771 if (ch == '$') {
1772 break;
1775 } else {
1776 quoted = 0;
1778 if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
1779 *p++ = '\\';
1783 *p++ = ch;
1786 part_len = p - part_data;
1789 len += part_len;
1791 size = ngx_array_push(&lengths);
1792 if (size == NULL) {
1793 return NGX_ERROR;
1796 *size = part_len;
1798 value = ngx_array_push(&values);
1799 if (value == NULL) {
1800 return NGX_ERROR;
1803 *value = part_data;
1806 prefix = 0;
1808 size = lengths.elts;
1809 value = values.elts;
1811 if (flags & NGX_HTTP_SSI_ADD_PREFIX) {
1812 for (i = 0; i < values.nelts; i++) {
1813 if (size[i] != 0) {
1814 if (*value[i] != '/') {
1815 for (prefix = r->uri.len; prefix; prefix--) {
1816 if (r->uri.data[prefix - 1] == '/') {
1817 len += prefix;
1818 break;
1823 break;
1828 p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
1829 if (p == NULL) {
1830 return NGX_ERROR;
1833 text->len = len;
1834 text->data = p;
1836 p = ngx_copy(p, r->uri.data, prefix);
1838 for (i = 0; i < values.nelts; i++) {
1839 p = ngx_copy(p, value[i], size[i]);
1842 return NGX_OK;
1844 invalid_variable:
1846 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1847 "invalid variable name in \"%V\"", text);
1849 return NGX_HTTP_SSI_ERROR;
1853 static ngx_int_t
1854 ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1855 ngx_str_t **params)
1857 u_char *dst, *src;
1858 size_t len;
1859 ngx_int_t rc, key;
1860 ngx_str_t *uri, *file, *wait, *set, *stub, args;
1861 ngx_buf_t *b;
1862 ngx_uint_t flags, i;
1863 ngx_chain_t *cl, *tl, **ll, *out;
1864 ngx_http_request_t *sr;
1865 ngx_http_ssi_var_t *var;
1866 ngx_http_ssi_ctx_t *mctx;
1867 ngx_http_ssi_block_t *bl;
1868 ngx_http_post_subrequest_t *psr;
1870 uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
1871 file = params[NGX_HTTP_SSI_INCLUDE_FILE];
1872 wait = params[NGX_HTTP_SSI_INCLUDE_WAIT];
1873 set = params[NGX_HTTP_SSI_INCLUDE_SET];
1874 stub = params[NGX_HTTP_SSI_INCLUDE_STUB];
1876 if (uri && file) {
1877 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1878 "inlcusion may be either virtual=\"%V\" or file=\"%V\"",
1879 uri, file);
1880 return NGX_HTTP_SSI_ERROR;
1883 if (uri == NULL && file == NULL) {
1884 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1885 "no parameter in \"include\" SSI command");
1886 return NGX_HTTP_SSI_ERROR;
1889 if (set && stub) {
1890 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1891 "\"set\" and \"stub\" may not be used together "
1892 "in \"include\" SSI command");
1893 return NGX_HTTP_SSI_ERROR;
1896 if (wait) {
1897 if (uri == NULL) {
1898 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1899 "\"wait\" may not be used with file=\"%V\"", file);
1900 return NGX_HTTP_SSI_ERROR;
1903 if (wait->len == 2
1904 && ngx_strncasecmp(wait->data, (u_char *) "no", 2) == 0)
1906 wait = NULL;
1908 } else if (wait->len != 3
1909 || ngx_strncasecmp(wait->data, (u_char *) "yes", 3) != 0)
1911 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1912 "invalid value \"%V\" in the \"wait\" parameter",
1913 wait);
1914 return NGX_HTTP_SSI_ERROR;
1918 if (uri == NULL) {
1919 uri = file;
1922 rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);
1924 if (rc != NGX_OK) {
1925 return rc;
1928 dst = uri->data;
1929 src = uri->data;
1931 ngx_unescape_uri(&dst, &src, uri->len, NGX_UNESCAPE_URI);
1933 len = (uri->data + uri->len) - src;
1934 if (len) {
1935 dst = ngx_copy(dst, src, len);
1938 uri->len = dst - uri->data;
1940 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1941 "ssi include: \"%V\"", uri);
1943 args.len = 0;
1944 args.data = NULL;
1945 flags = 0;
1947 if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
1948 return NGX_HTTP_SSI_ERROR;
1951 psr = NULL;
1953 mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
1955 if (stub) {
1956 if (mctx->blocks) {
1957 bl = mctx->blocks->elts;
1958 for (i = 0; i < mctx->blocks->nelts; i++) {
1959 if (stub->len == bl[i].name.len
1960 && ngx_strncmp(stub->data, bl[i].name.data, stub->len) == 0)
1962 goto found;
1967 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1968 "\"stub\"=\"%V\" for \"include\" not found", stub);
1969 return NGX_HTTP_SSI_ERROR;
1971 found:
1973 psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
1974 if (psr == NULL) {
1975 return NGX_ERROR;
1978 psr->handler = ngx_http_ssi_stub_output;
1980 if (bl[i].count++) {
1982 out = NULL;
1983 ll = &out;
1985 for (tl = bl[i].bufs; tl; tl = tl->next) {
1987 if (ctx->free) {
1988 cl = ctx->free;
1989 ctx->free = ctx->free->next;
1990 b = cl->buf;
1992 } else {
1993 b = ngx_alloc_buf(r->pool);
1994 if (b == NULL) {
1995 return NGX_ERROR;
1998 cl = ngx_alloc_chain_link(r->pool);
1999 if (cl == NULL) {
2000 return NGX_ERROR;
2003 cl->buf = b;
2006 ngx_memcpy(b, tl->buf, sizeof(ngx_buf_t));
2008 b->pos = b->start;
2010 *ll = cl;
2011 cl->next = NULL;
2012 ll = &cl->next;
2015 psr->data = out;
2017 } else {
2018 psr->data = bl[i].bufs;
2022 if (set) {
2023 key = ngx_hash_strlow(set->data, set->data, set->len);
2025 psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
2026 if (psr == NULL) {
2027 return NGX_ERROR;
2030 psr->handler = ngx_http_ssi_set_variable;
2031 psr->data = ngx_http_ssi_get_variable(r, set, key);
2033 if (psr->data == NULL) {
2035 if (mctx->variables == NULL) {
2036 mctx->variables = ngx_list_create(r->pool, 4,
2037 sizeof(ngx_http_ssi_var_t));
2038 if (mctx->variables == NULL) {
2039 return NGX_ERROR;
2043 var = ngx_list_push(mctx->variables);
2044 if (var == NULL) {
2045 return NGX_ERROR;
2048 var->name = *set;
2049 var->key = key;
2050 var->value = ngx_http_ssi_null_string;
2051 psr->data = &var->value;
2054 flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
2057 rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags);
2059 if (rc == NGX_DONE) {
2060 return NGX_DONE;
2063 if (rc == NGX_ERROR) {
2064 return NGX_HTTP_SSI_ERROR;
2067 if (wait == NULL && set == NULL) {
2068 return NGX_OK;
2071 if (rc == NGX_AGAIN) {
2072 if (ctx->wait == NULL) {
2073 ctx->wait = sr;
2075 } else {
2076 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2077 "only one subrequest may be waited at the same time");
2081 return rc;
2085 static ngx_int_t
2086 ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc)
2088 ngx_chain_t *out;
2090 if (rc == NGX_ERROR || r->connection->error || r->request_output) {
2091 return rc;
2094 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2095 "ssi stub output: \"%V?%V\"", &r->uri, &r->args);
2097 out = data;
2099 if (!r->header_sent) {
2100 if (ngx_http_set_content_type(r) == NGX_ERROR) {
2101 return NGX_ERROR;
2104 if (ngx_http_send_header(r) == NGX_ERROR) {
2105 return NGX_ERROR;
2109 return ngx_http_output_filter(r, out);
2113 static ngx_int_t
2114 ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc)
2116 ngx_str_t *value = data;
2118 if (r->upstream) {
2119 value->len = r->upstream->buffer.last - r->upstream->buffer.pos;
2120 value->data = r->upstream->buffer.pos;
2123 return rc;
2127 static ngx_int_t
2128 ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2129 ngx_str_t **params)
2131 u_char *p;
2132 uintptr_t len;
2133 ngx_int_t key;
2134 ngx_buf_t *b;
2135 ngx_str_t *var, *value, *enc, text;
2136 ngx_chain_t *cl;
2137 ngx_http_variable_value_t *vv;
2139 var = params[NGX_HTTP_SSI_ECHO_VAR];
2141 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2142 "ssi echo \"%V\"", var);
2144 key = ngx_hash_strlow(var->data, var->data, var->len);
2146 value = ngx_http_ssi_get_variable(r, var, key);
2148 if (value == NULL) {
2149 vv = ngx_http_get_variable(r, var, key, 1);
2151 if (vv == NULL) {
2152 return NGX_HTTP_SSI_ERROR;
2155 if (!vv->not_found) {
2156 text.data = vv->data;
2157 text.len = vv->len;
2158 value = &text;
2162 if (value == NULL) {
2163 value = params[NGX_HTTP_SSI_ECHO_DEFAULT];
2165 if (value == NULL) {
2166 value = &ngx_http_ssi_none;
2168 } else if (value->len == 0) {
2169 return NGX_OK;
2172 } else {
2173 if (value->len == 0) {
2174 return NGX_OK;
2178 enc = params[NGX_HTTP_SSI_ECHO_ENCODING];
2180 if (enc) {
2181 if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) {
2183 ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;
2185 } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {
2187 ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;
2189 } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {
2191 ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
2193 } else {
2194 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2195 "unknown encoding \"%V\" in the \"echo\" command",
2196 enc);
2200 switch (ctx->encoding) {
2202 case NGX_HTTP_SSI_NO_ENCODING:
2203 break;
2205 case NGX_HTTP_SSI_URL_ENCODING:
2206 len = 2 * ngx_escape_uri(NULL, value->data, value->len,
2207 NGX_ESCAPE_HTML);
2209 if (len) {
2210 p = ngx_pnalloc(r->pool, value->len + len);
2211 if (p == NULL) {
2212 return NGX_HTTP_SSI_ERROR;
2215 (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);
2217 value->len += len;
2218 value->data = p;
2221 break;
2223 case NGX_HTTP_SSI_ENTITY_ENCODING:
2224 len = ngx_escape_html(NULL, value->data, value->len);
2226 if (len) {
2227 p = ngx_pnalloc(r->pool, value->len + len);
2228 if (p == NULL) {
2229 return NGX_HTTP_SSI_ERROR;
2232 (void) ngx_escape_html(p, value->data, value->len);
2234 value->len += len;
2235 value->data = p;
2238 break;
2241 b = ngx_calloc_buf(r->pool);
2242 if (b == NULL) {
2243 return NGX_HTTP_SSI_ERROR;
2246 cl = ngx_alloc_chain_link(r->pool);
2247 if (cl == NULL) {
2248 return NGX_HTTP_SSI_ERROR;
2251 b->memory = 1;
2252 b->pos = value->data;
2253 b->last = value->data + value->len;
2255 cl->buf = b;
2256 cl->next = NULL;
2257 *ctx->last_out = cl;
2258 ctx->last_out = &cl->next;
2260 return NGX_OK;
2264 static ngx_int_t
2265 ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2266 ngx_str_t **params)
2268 ngx_str_t *value;
2270 value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];
2272 if (value) {
2273 ctx->timefmt.len = value->len;
2274 ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);
2275 if (ctx->timefmt.data == NULL) {
2276 return NGX_HTTP_SSI_ERROR;
2279 ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1);
2282 value = params[NGX_HTTP_SSI_CONFIG_ERRMSG];
2284 if (value) {
2285 ctx->errmsg = *value;
2288 return NGX_OK;
2292 static ngx_int_t
2293 ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2294 ngx_str_t **params)
2296 ngx_int_t key, rc;
2297 ngx_str_t *name, *value, *vv;
2298 ngx_http_ssi_var_t *var;
2299 ngx_http_ssi_ctx_t *mctx;
2301 mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
2303 if (mctx->variables == NULL) {
2304 mctx->variables = ngx_list_create(r->pool, 4,
2305 sizeof(ngx_http_ssi_var_t));
2306 if (mctx->variables == NULL) {
2307 return NGX_ERROR;
2311 name = params[NGX_HTTP_SSI_SET_VAR];
2312 value = params[NGX_HTTP_SSI_SET_VALUE];
2314 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2315 "ssi set \"%V\" \"%V\"", name, value);
2317 rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);
2319 if (rc != NGX_OK) {
2320 return rc;
2323 key = ngx_hash_strlow(name->data, name->data, name->len);
2325 vv = ngx_http_ssi_get_variable(r, name, key);
2327 if (vv) {
2328 *vv = *value;
2329 return NGX_OK;
2332 var = ngx_list_push(mctx->variables);
2333 if (var == NULL) {
2334 return NGX_ERROR;
2337 var->name = *name;
2338 var->key = key;
2339 var->value = *value;
2341 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2342 "set: \"%V\"=\"%V\"", name, value);
2344 return NGX_OK;
2348 static ngx_int_t
2349 ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2350 ngx_str_t **params)
2352 u_char *p, *last;
2353 ngx_str_t *expr, left, right;
2354 ngx_int_t rc;
2355 ngx_uint_t negative, noregex, flags;
2357 if (ctx->command.len == 2) {
2358 if (ctx->conditional) {
2359 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2360 "the \"if\" command inside the \"if\" command");
2361 return NGX_HTTP_SSI_ERROR;
2365 if (ctx->output_chosen) {
2366 ctx->output = 0;
2367 return NGX_OK;
2370 expr = params[NGX_HTTP_SSI_IF_EXPR];
2372 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2373 "ssi if expr=\"%V\"", expr);
2375 left.data = expr->data;
2376 last = expr->data + expr->len;
2378 for (p = left.data; p < last; p++) {
2379 if (*p >= 'A' && *p <= 'Z') {
2380 *p |= 0x20;
2381 continue;
2384 if ((*p >= 'a' && *p <= 'z')
2385 || (*p >= '0' && *p <= '9')
2386 || *p == '$' || *p == '{' || *p == '}' || *p == '_'
2387 || *p == '"' || *p == '\'')
2389 continue;
2392 break;
2395 left.len = p - left.data;
2397 while (p < last && *p == ' ') {
2398 p++;
2401 flags = (p == last) ? NGX_HTTP_SSI_EXPR_TEST : 0;
2403 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2404 "left: \"%V\"", &left);
2406 rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);
2408 if (rc != NGX_OK) {
2409 return rc;
2412 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2413 "evaluted left: \"%V\"", &left);
2415 if (p == last) {
2416 if (left.len) {
2417 ctx->output = 1;
2418 ctx->output_chosen = 1;
2420 } else {
2421 ctx->output = 0;
2424 ctx->conditional = NGX_HTTP_SSI_COND_IF;
2426 return NGX_OK;
2429 if (p < last && *p == '=') {
2430 negative = 0;
2431 p++;
2433 } else if (p + 1 < last && *p == '!' && *(p + 1) == '=') {
2434 negative = 1;
2435 p += 2;
2437 } else {
2438 goto invalid_expression;
2441 while (p < last && *p == ' ') {
2442 p++;
2445 if (p < last - 1 && *p == '/') {
2446 if (*(last - 1) != '/') {
2447 goto invalid_expression;
2450 noregex = 0;
2451 flags = NGX_HTTP_SSI_ADD_ZERO;
2452 last--;
2453 p++;
2455 } else {
2456 noregex = 1;
2457 flags = 0;
2459 if (p < last - 1 && p[0] == '\\' && p[1] == '/') {
2460 p++;
2464 right.len = last - p;
2465 right.data = p;
2467 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2468 "right: \"%V\"", &right);
2470 rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);
2472 if (rc != NGX_OK) {
2473 return rc;
2476 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2477 "evaluted right: \"%V\"", &right);
2479 if (noregex) {
2480 if (left.len != right.len) {
2481 rc = -1;
2483 } else {
2484 rc = ngx_strncmp(left.data, right.data, right.len);
2487 } else {
2488 #if (NGX_PCRE)
2489 ngx_str_t err;
2490 ngx_regex_t *regex;
2491 u_char errstr[NGX_MAX_CONF_ERRSTR];
2493 err.len = NGX_MAX_CONF_ERRSTR;
2494 err.data = errstr;
2496 right.data[right.len] = '\0';
2498 regex = ngx_regex_compile(&right, 0, r->pool, &err);
2500 if (regex == NULL) {
2501 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s", err.data);
2502 return NGX_HTTP_SSI_ERROR;
2505 rc = ngx_regex_exec(regex, &left, NULL, 0);
2507 if (rc != NGX_REGEX_NO_MATCHED && rc < 0) {
2508 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
2509 ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"",
2510 rc, &left, &right);
2511 return NGX_HTTP_SSI_ERROR;
2513 #else
2514 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
2515 "the using of the regex \"%V\" in SSI "
2516 "requires PCRE library", &right);
2518 return NGX_HTTP_SSI_ERROR;
2519 #endif
2522 if ((rc == 0 && !negative) || (rc != 0 && negative)) {
2523 ctx->output = 1;
2524 ctx->output_chosen = 1;
2526 } else {
2527 ctx->output = 0;
2530 ctx->conditional = NGX_HTTP_SSI_COND_IF;
2532 return NGX_OK;
2534 invalid_expression:
2536 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2537 "invalid expression in \"%V\"", expr);
2539 return NGX_HTTP_SSI_ERROR;
2543 static ngx_int_t
2544 ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2545 ngx_str_t **params)
2547 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2548 "ssi else");
2550 if (ctx->output_chosen) {
2551 ctx->output = 0;
2552 } else {
2553 ctx->output = 1;
2556 ctx->conditional = NGX_HTTP_SSI_COND_ELSE;
2558 return NGX_OK;
2562 static ngx_int_t
2563 ngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2564 ngx_str_t **params)
2566 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2567 "ssi endif");
2569 ctx->output = 1;
2570 ctx->output_chosen = 0;
2571 ctx->conditional = 0;
2573 return NGX_OK;
2577 static ngx_int_t
2578 ngx_http_ssi_block(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2579 ngx_str_t **params)
2581 ngx_http_ssi_ctx_t *mctx;
2582 ngx_http_ssi_block_t *bl;
2584 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2585 "ssi block");
2587 mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
2589 if (mctx->blocks == NULL) {
2590 mctx->blocks = ngx_array_create(r->pool, 4,
2591 sizeof(ngx_http_ssi_block_t));
2592 if (mctx->blocks == NULL) {
2593 return NGX_HTTP_SSI_ERROR;
2597 bl = ngx_array_push(mctx->blocks);
2598 if (bl == NULL) {
2599 return NGX_HTTP_SSI_ERROR;
2602 bl->name = *params[NGX_HTTP_SSI_BLOCK_NAME];
2603 bl->bufs = NULL;
2604 bl->count = 0;
2606 ctx->output = 0;
2607 ctx->block = 1;
2609 return NGX_OK;
2613 static ngx_int_t
2614 ngx_http_ssi_endblock(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
2615 ngx_str_t **params)
2617 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2618 "ssi endblock");
2620 ctx->output = 1;
2621 ctx->block = 0;
2623 return NGX_OK;
2627 static ngx_int_t
2628 ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
2629 ngx_http_variable_value_t *v, uintptr_t gmt)
2631 ngx_http_ssi_ctx_t *ctx;
2632 ngx_time_t *tp;
2633 struct tm tm;
2634 char buf[NGX_HTTP_SSI_DATE_LEN];
2636 v->valid = 1;
2637 v->no_cacheable = 0;
2638 v->not_found = 0;
2640 tp = ngx_timeofday();
2642 ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
2644 if (ctx == NULL
2645 || (ctx->timefmt.len == sizeof("%s") - 1
2646 && ctx->timefmt.data[0] == '%' && ctx->timefmt.data[1] == 's'))
2648 v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);
2649 if (v->data == NULL) {
2650 return NGX_ERROR;
2653 v->len = ngx_sprintf(v->data, "%T", tp->sec + (gmt ? 0 : tp->gmtoff))
2654 - v->data;
2656 return NGX_OK;
2659 if (gmt) {
2660 ngx_libc_gmtime(tp->sec, &tm);
2661 } else {
2662 ngx_libc_localtime(tp->sec, &tm);
2665 v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,
2666 (char *) ctx->timefmt.data, &tm);
2667 if (v->len == 0) {
2668 return NGX_ERROR;
2671 v->data = ngx_pnalloc(r->pool, v->len);
2672 if (v->data == NULL) {
2673 return NGX_ERROR;
2676 ngx_memcpy(v->data, buf, v->len);
2678 return NGX_OK;
2682 static char *
2683 ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2685 ngx_http_ssi_loc_conf_t *slcf = conf;
2687 ngx_str_t *value, *type;
2688 ngx_uint_t i;
2690 if (slcf->types == NULL) {
2691 slcf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
2692 if (slcf->types == NULL) {
2693 return NGX_CONF_ERROR;
2696 type = ngx_array_push(slcf->types);
2697 if (type == NULL) {
2698 return NGX_CONF_ERROR;
2701 type->len = sizeof("text/html") - 1;
2702 type->data = (u_char *) "text/html";
2705 value = cf->args->elts;
2707 for (i = 1; i < cf->args->nelts; i++) {
2709 if (ngx_strcmp(value[i].data, "text/html") == 0) {
2710 continue;
2713 type = ngx_array_push(slcf->types);
2714 if (type == NULL) {
2715 return NGX_CONF_ERROR;
2718 type->len = value[i].len;
2720 type->data = ngx_pnalloc(cf->pool, type->len + 1);
2721 if (type->data == NULL) {
2722 return NGX_CONF_ERROR;
2725 ngx_cpystrn(type->data, value[i].data, type->len + 1);
2728 return NGX_CONF_OK;
2732 static ngx_int_t
2733 ngx_http_ssi_preconfiguration(ngx_conf_t *cf)
2735 ngx_int_t rc;
2736 ngx_http_variable_t *var, *v;
2737 ngx_http_ssi_command_t *cmd;
2738 ngx_http_ssi_main_conf_t *smcf;
2740 for (v = ngx_http_ssi_vars; v->name.len; v++) {
2741 var = ngx_http_add_variable(cf, &v->name, v->flags);
2742 if (var == NULL) {
2743 return NGX_ERROR;
2746 var->get_handler = v->get_handler;
2747 var->data = v->data;
2750 smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);
2752 for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {
2753 rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,
2754 NGX_HASH_READONLY_KEY);
2756 if (rc == NGX_OK) {
2757 continue;
2760 if (rc == NGX_BUSY) {
2761 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2762 "conflicting SSI command \"%V\"", &cmd->name);
2765 return NGX_ERROR;
2768 return NGX_OK;
2772 static void *
2773 ngx_http_ssi_create_main_conf(ngx_conf_t *cf)
2775 ngx_http_ssi_main_conf_t *smcf;
2777 smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));
2778 if (smcf == NULL) {
2779 return NGX_CONF_ERROR;
2782 smcf->commands.pool = cf->pool;
2783 smcf->commands.temp_pool = cf->temp_pool;
2785 if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {
2786 return NGX_CONF_ERROR;
2789 return smcf;
2793 static char *
2794 ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)
2796 ngx_http_ssi_main_conf_t *smcf = conf;
2798 ngx_hash_init_t hash;
2800 hash.hash = &smcf->hash;
2801 hash.key = ngx_hash_key;
2802 hash.max_size = 1024;
2803 hash.bucket_size = ngx_cacheline_size;
2804 hash.name = "ssi_command_hash";
2805 hash.pool = cf->pool;
2806 hash.temp_pool = NULL;
2808 if (ngx_hash_init(&hash, smcf->commands.keys.elts,
2809 smcf->commands.keys.nelts)
2810 != NGX_OK)
2812 return NGX_CONF_ERROR;
2815 return NGX_CONF_OK;
2819 static void *
2820 ngx_http_ssi_create_loc_conf(ngx_conf_t *cf)
2822 ngx_http_ssi_loc_conf_t *slcf;
2824 slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
2825 if (slcf == NULL) {
2826 return NGX_CONF_ERROR;
2830 * set by ngx_pcalloc():
2832 * conf->types = NULL;
2835 slcf->enable = NGX_CONF_UNSET;
2836 slcf->silent_errors = NGX_CONF_UNSET;
2837 slcf->ignore_recycled_buffers = NGX_CONF_UNSET;
2839 slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;
2840 slcf->value_len = NGX_CONF_UNSET_SIZE;
2842 return slcf;
2846 static char *
2847 ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
2849 ngx_http_ssi_loc_conf_t *prev = parent;
2850 ngx_http_ssi_loc_conf_t *conf = child;
2852 ngx_str_t *type;
2854 ngx_conf_merge_value(conf->enable, prev->enable, 0);
2855 ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
2856 ngx_conf_merge_value(conf->ignore_recycled_buffers,
2857 prev->ignore_recycled_buffers, 0);
2859 ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
2860 ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256);
2862 if (conf->types == NULL) {
2863 if (prev->types == NULL) {
2864 conf->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t));
2865 if (conf->types == NULL) {
2866 return NGX_CONF_ERROR;
2869 type = ngx_array_push(conf->types);
2870 if (type == NULL) {
2871 return NGX_CONF_ERROR;
2874 type->len = sizeof("text/html") - 1;
2875 type->data = (u_char *) "text/html";
2877 } else {
2878 conf->types = prev->types;
2882 return NGX_CONF_OK;
2886 static ngx_int_t
2887 ngx_http_ssi_filter_init(ngx_conf_t *cf)
2889 ngx_http_next_header_filter = ngx_http_top_header_filter;
2890 ngx_http_top_header_filter = ngx_http_ssi_header_filter;
2892 ngx_http_next_body_filter = ngx_http_top_body_filter;
2893 ngx_http_top_body_filter = ngx_http_ssi_body_filter;
2895 return NGX_OK;