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