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