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