Update and clean Tomato RAF files
[tomato.git] / release / src / router / nginx / src / http / modules / ngx_http_xslt_filter_module.c
bloba6ae1ce02b8ba21921c1098265abf4eba961f3b9
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
12 #include <libxml/parser.h>
13 #include <libxml/tree.h>
14 #include <libxslt/xslt.h>
15 #include <libxslt/xsltInternals.h>
16 #include <libxslt/transform.h>
17 #include <libxslt/variables.h>
18 #include <libxslt/xsltutils.h>
20 #if (NGX_HAVE_EXSLT)
21 #include <libexslt/exslt.h>
22 #endif
25 #ifndef NGX_HTTP_XSLT_REUSE_DTD
26 #define NGX_HTTP_XSLT_REUSE_DTD 1
27 #endif
30 typedef struct {
31 u_char *name;
32 void *data;
33 } ngx_http_xslt_file_t;
36 typedef struct {
37 ngx_array_t dtd_files; /* ngx_http_xslt_file_t */
38 ngx_array_t sheet_files; /* ngx_http_xslt_file_t */
39 } ngx_http_xslt_filter_main_conf_t;
42 typedef struct {
43 u_char *name;
44 ngx_http_complex_value_t value;
45 ngx_uint_t quote; /* unsigned quote:1; */
46 } ngx_http_xslt_param_t;
49 typedef struct {
50 xsltStylesheetPtr stylesheet;
51 ngx_array_t params; /* ngx_http_xslt_param_t */
52 } ngx_http_xslt_sheet_t;
55 typedef struct {
56 xmlDtdPtr dtd;
57 ngx_array_t sheets; /* ngx_http_xslt_sheet_t */
58 ngx_hash_t types;
59 ngx_array_t *types_keys;
60 ngx_array_t *params; /* ngx_http_xslt_param_t */
61 } ngx_http_xslt_filter_loc_conf_t;
64 typedef struct {
65 xmlDocPtr doc;
66 xmlParserCtxtPtr ctxt;
67 xsltTransformContextPtr transform;
68 ngx_http_request_t *request;
69 ngx_array_t params;
71 ngx_uint_t done; /* unsigned done:1; */
72 } ngx_http_xslt_filter_ctx_t;
75 static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
76 ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
77 static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
78 ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
81 static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
82 const xmlChar *externalId, const xmlChar *systemId);
83 static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);
86 static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
87 ngx_http_xslt_filter_ctx_t *ctx);
88 static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,
89 ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params, ngx_uint_t final);
90 static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s);
91 static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s);
92 static void ngx_http_xslt_cleanup(void *data);
94 static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,
95 void *conf);
96 static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,
97 void *conf);
98 static char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd,
99 void *conf);
100 static void ngx_http_xslt_cleanup_dtd(void *data);
101 static void ngx_http_xslt_cleanup_stylesheet(void *data);
102 static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf);
103 static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
104 static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
105 void *child);
106 static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
107 static void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle);
110 ngx_str_t ngx_http_xslt_default_types[] = {
111 ngx_string("text/xml"),
112 ngx_null_string
116 static ngx_command_t ngx_http_xslt_filter_commands[] = {
118 { ngx_string("xml_entities"),
119 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
120 ngx_http_xslt_entities,
121 NGX_HTTP_LOC_CONF_OFFSET,
123 NULL },
125 { ngx_string("xslt_stylesheet"),
126 NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
127 ngx_http_xslt_stylesheet,
128 NGX_HTTP_LOC_CONF_OFFSET,
130 NULL },
132 { ngx_string("xslt_param"),
133 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
134 ngx_http_xslt_param,
135 NGX_HTTP_LOC_CONF_OFFSET,
137 NULL },
139 { ngx_string("xslt_string_param"),
140 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
141 ngx_http_xslt_param,
142 NGX_HTTP_LOC_CONF_OFFSET,
144 (void *) 1 },
146 { ngx_string("xslt_types"),
147 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
148 ngx_http_types_slot,
149 NGX_HTTP_LOC_CONF_OFFSET,
150 offsetof(ngx_http_xslt_filter_loc_conf_t, types_keys),
151 &ngx_http_xslt_default_types[0] },
153 ngx_null_command
157 static ngx_http_module_t ngx_http_xslt_filter_module_ctx = {
158 NULL, /* preconfiguration */
159 ngx_http_xslt_filter_init, /* postconfiguration */
161 ngx_http_xslt_filter_create_main_conf, /* create main configuration */
162 NULL, /* init main configuration */
164 NULL, /* create server configuration */
165 NULL, /* merge server configuration */
167 ngx_http_xslt_filter_create_conf, /* create location configuration */
168 ngx_http_xslt_filter_merge_conf /* merge location configuration */
172 ngx_module_t ngx_http_xslt_filter_module = {
173 NGX_MODULE_V1,
174 &ngx_http_xslt_filter_module_ctx, /* module context */
175 ngx_http_xslt_filter_commands, /* module directives */
176 NGX_HTTP_MODULE, /* module type */
177 NULL, /* init master */
178 NULL, /* init module */
179 NULL, /* init process */
180 NULL, /* init thread */
181 NULL, /* exit thread */
182 ngx_http_xslt_filter_exit, /* exit process */
183 ngx_http_xslt_filter_exit, /* exit master */
184 NGX_MODULE_V1_PADDING
188 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
189 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
192 static ngx_int_t
193 ngx_http_xslt_header_filter(ngx_http_request_t *r)
195 ngx_http_xslt_filter_ctx_t *ctx;
196 ngx_http_xslt_filter_loc_conf_t *conf;
198 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
199 "xslt filter header");
201 if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
202 return ngx_http_next_header_filter(r);
205 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
207 if (conf->sheets.nelts == 0
208 || ngx_http_test_content_type(r, &conf->types) == NULL)
210 return ngx_http_next_header_filter(r);
213 ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
215 if (ctx) {
216 return ngx_http_next_header_filter(r);
219 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
220 if (ctx == NULL) {
221 return NGX_ERROR;
224 ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
226 r->main_filter_need_in_memory = 1;
228 return NGX_OK;
232 static ngx_int_t
233 ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
235 int wellFormed;
236 ngx_chain_t *cl;
237 ngx_http_xslt_filter_ctx_t *ctx;
239 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
240 "xslt filter body");
242 if (in == NULL) {
243 return ngx_http_next_body_filter(r, in);
246 ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
248 if (ctx == NULL || ctx->done) {
249 return ngx_http_next_body_filter(r, in);
252 for (cl = in; cl; cl = cl->next) {
254 if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
256 if (ctx->ctxt->myDoc) {
258 #if (NGX_HTTP_XSLT_REUSE_DTD)
259 ctx->ctxt->myDoc->extSubset = NULL;
260 #endif
261 xmlFreeDoc(ctx->ctxt->myDoc);
264 xmlFreeParserCtxt(ctx->ctxt);
266 return ngx_http_xslt_send(r, ctx, NULL);
269 if (cl->buf->last_buf || cl->buf->last_in_chain) {
271 ctx->doc = ctx->ctxt->myDoc;
273 #if (NGX_HTTP_XSLT_REUSE_DTD)
274 ctx->doc->extSubset = NULL;
275 #endif
277 wellFormed = ctx->ctxt->wellFormed;
279 xmlFreeParserCtxt(ctx->ctxt);
281 if (wellFormed) {
282 return ngx_http_xslt_send(r, ctx,
283 ngx_http_xslt_apply_stylesheet(r, ctx));
286 xmlFreeDoc(ctx->doc);
288 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
289 "not well formed XML document");
291 return ngx_http_xslt_send(r, ctx, NULL);
295 return NGX_OK;
299 static ngx_int_t
300 ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
301 ngx_buf_t *b)
303 ngx_int_t rc;
304 ngx_chain_t out;
305 ngx_pool_cleanup_t *cln;
307 ctx->done = 1;
309 if (b == NULL) {
310 return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
311 NGX_HTTP_INTERNAL_SERVER_ERROR);
314 cln = ngx_pool_cleanup_add(r->pool, 0);
316 if (cln == NULL) {
317 ngx_free(b->pos);
318 return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
319 NGX_HTTP_INTERNAL_SERVER_ERROR);
322 if (r == r->main) {
323 r->headers_out.content_length_n = b->last - b->pos;
325 if (r->headers_out.content_length) {
326 r->headers_out.content_length->hash = 0;
327 r->headers_out.content_length = NULL;
330 ngx_http_clear_last_modified(r);
331 ngx_http_clear_etag(r);
334 rc = ngx_http_next_header_filter(r);
336 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
337 ngx_free(b->pos);
338 return rc;
341 cln->handler = ngx_http_xslt_cleanup;
342 cln->data = b->pos;
344 out.buf = b;
345 out.next = NULL;
347 return ngx_http_next_body_filter(r, &out);
351 static ngx_int_t
352 ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
353 ngx_buf_t *b)
355 int err;
356 xmlParserCtxtPtr ctxt;
358 if (ctx->ctxt == NULL) {
360 ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
361 if (ctxt == NULL) {
362 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
363 "xmlCreatePushParserCtxt() failed");
364 return NGX_ERROR;
366 xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD
367 |XML_PARSE_NOWARNING);
369 ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset;
370 ctxt->sax->setDocumentLocator = NULL;
371 ctxt->sax->error = ngx_http_xslt_sax_error;
372 ctxt->sax->fatalError = ngx_http_xslt_sax_error;
373 ctxt->sax->_private = ctx;
375 ctx->ctxt = ctxt;
376 ctx->request = r;
379 err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),
380 (b->last_buf) || (b->last_in_chain));
382 if (err == 0) {
383 b->pos = b->last;
384 return NGX_OK;
387 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
388 "xmlParseChunk() failed, error:%d", err);
390 return NGX_ERROR;
394 static void
395 ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
396 const xmlChar *externalId, const xmlChar *systemId)
398 xmlParserCtxtPtr ctxt = data;
400 xmlDocPtr doc;
401 xmlDtdPtr dtd;
402 ngx_http_request_t *r;
403 ngx_http_xslt_filter_ctx_t *ctx;
404 ngx_http_xslt_filter_loc_conf_t *conf;
406 ctx = ctxt->sax->_private;
407 r = ctx->request;
409 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
411 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
412 "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
413 name ? name : (xmlChar *) "",
414 externalId ? externalId : (xmlChar *) "",
415 systemId ? systemId : (xmlChar *) "");
417 doc = ctxt->myDoc;
419 #if (NGX_HTTP_XSLT_REUSE_DTD)
421 dtd = conf->dtd;
423 #else
425 dtd = xmlCopyDtd(conf->dtd);
426 if (dtd == NULL) {
427 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
428 "xmlCopyDtd() failed");
429 return;
432 if (doc->children == NULL) {
433 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
435 } else {
436 xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
439 #endif
441 doc->extSubset = dtd;
445 static void ngx_cdecl
446 ngx_http_xslt_sax_error(void *data, const char *msg, ...)
448 xmlParserCtxtPtr ctxt = data;
450 size_t n;
451 va_list args;
452 ngx_http_xslt_filter_ctx_t *ctx;
453 u_char buf[NGX_MAX_ERROR_STR];
455 ctx = ctxt->sax->_private;
457 buf[0] = '\0';
459 va_start(args, msg);
460 n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
461 va_end(args);
463 while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
465 ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
466 "libxml2 error: \"%*s\"", n + 1, buf);
470 static ngx_buf_t *
471 ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
472 ngx_http_xslt_filter_ctx_t *ctx)
474 int len, rc, doc_type;
475 u_char *type, *encoding;
476 ngx_buf_t *b;
477 ngx_uint_t i;
478 xmlChar *buf;
479 xmlDocPtr doc, res;
480 ngx_http_xslt_sheet_t *sheet;
481 ngx_http_xslt_filter_loc_conf_t *conf;
483 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
484 sheet = conf->sheets.elts;
485 doc = ctx->doc;
487 /* preallocate array for 4 params */
489 if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
490 != NGX_OK)
492 xmlFreeDoc(doc);
493 return NULL;
496 for (i = 0; i < conf->sheets.nelts; i++) {
498 ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);
499 if (ctx->transform == NULL) {
500 xmlFreeDoc(doc);
501 return NULL;
504 if (conf->params
505 && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)
507 xsltFreeTransformContext(ctx->transform);
508 xmlFreeDoc(doc);
509 return NULL;
512 if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {
513 xsltFreeTransformContext(ctx->transform);
514 xmlFreeDoc(doc);
515 return NULL;
518 res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,
519 ctx->params.elts, NULL, NULL,
520 ctx->transform);
522 xsltFreeTransformContext(ctx->transform);
523 xmlFreeDoc(doc);
525 if (res == NULL) {
526 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
527 "xsltApplyStylesheet() failed");
528 return NULL;
531 doc = res;
533 /* reset array elements */
534 ctx->params.nelts = 0;
537 /* there must be at least one stylesheet */
539 if (r == r->main) {
540 type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);
542 } else {
543 type = NULL;
546 encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);
547 doc_type = doc->type;
549 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
550 "xslt filter type: %d t:%s e:%s",
551 doc_type, type ? type : (u_char *) "(null)",
552 encoding ? encoding : (u_char *) "(null)");
554 rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
556 xmlFreeDoc(doc);
558 if (rc != 0) {
559 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
560 "xsltSaveResultToString() failed");
561 return NULL;
564 if (len == 0) {
565 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
566 "xsltSaveResultToString() returned zero-length result");
567 return NULL;
570 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
571 if (b == NULL) {
572 ngx_free(buf);
573 return NULL;
576 b->pos = buf;
577 b->last = buf + len;
578 b->memory = 1;
580 if (encoding) {
581 r->headers_out.charset.len = ngx_strlen(encoding);
582 r->headers_out.charset.data = encoding;
585 if (r != r->main) {
586 return b;
589 b->last_buf = 1;
591 if (type) {
592 len = ngx_strlen(type);
594 r->headers_out.content_type_len = len;
595 r->headers_out.content_type.len = len;
596 r->headers_out.content_type.data = type;
598 } else if (doc_type == XML_HTML_DOCUMENT_NODE) {
600 r->headers_out.content_type_len = sizeof("text/html") - 1;
601 ngx_str_set(&r->headers_out.content_type, "text/html");
604 r->headers_out.content_type_lowcase = NULL;
606 return b;
610 static ngx_int_t
611 ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
612 ngx_array_t *params, ngx_uint_t final)
614 u_char *p, *last, *value, *dst, *src, **s;
615 size_t len;
616 ngx_uint_t i;
617 ngx_str_t string;
618 ngx_http_xslt_param_t *param;
620 param = params->elts;
622 for (i = 0; i < params->nelts; i++) {
624 if (ngx_http_complex_value(r, &param[i].value, &string) != NGX_OK) {
625 return NGX_ERROR;
628 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
629 "xslt filter param: \"%s\"", string.data);
631 if (param[i].name) {
633 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
634 "xslt filter param name: \"%s\"", param[i].name);
636 if (param[i].quote) {
637 if (xsltQuoteOneUserParam(ctx->transform, param[i].name,
638 string.data)
639 != 0)
641 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
642 "xsltQuoteOneUserParam(\"%s\", \"%s\") failed",
643 param[i].name, string.data);
644 return NGX_ERROR;
647 continue;
650 s = ngx_array_push(&ctx->params);
651 if (s == NULL) {
652 return NGX_ERROR;
655 *s = param[i].name;
657 s = ngx_array_push(&ctx->params);
658 if (s == NULL) {
659 return NGX_ERROR;
662 *s = string.data;
664 continue;
668 * parse param1=value1:param2=value2 syntax as used by parameters
669 * specified in xslt_stylesheet directives
672 p = string.data;
673 last = string.data + string.len;
675 while (p && *p) {
677 value = p;
678 p = (u_char *) ngx_strchr(p, '=');
679 if (p == NULL) {
680 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
681 "invalid libxslt parameter \"%s\"", value);
682 return NGX_ERROR;
684 *p++ = '\0';
686 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
687 "xslt filter param name: \"%s\"", value);
689 s = ngx_array_push(&ctx->params);
690 if (s == NULL) {
691 return NGX_ERROR;
694 *s = value;
696 value = p;
697 p = (u_char *) ngx_strchr(p, ':');
699 if (p) {
700 len = p - value;
701 *p++ = '\0';
703 } else {
704 len = last - value;
707 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
708 "xslt filter param value: \"%s\"", value);
710 dst = value;
711 src = value;
713 ngx_unescape_uri(&dst, &src, len, 0);
715 *dst = '\0';
717 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
718 "xslt filter param unescaped: \"%s\"", value);
720 s = ngx_array_push(&ctx->params);
721 if (s == NULL) {
722 return NGX_ERROR;
725 *s = value;
729 if (final) {
730 s = ngx_array_push(&ctx->params);
731 if (s == NULL) {
732 return NGX_ERROR;
735 *s = NULL;
738 return NGX_OK;
742 static u_char *
743 ngx_http_xslt_content_type(xsltStylesheetPtr s)
745 u_char *type;
747 if (s->mediaType) {
748 return s->mediaType;
751 for (s = s->imports; s; s = s->next) {
753 type = ngx_http_xslt_content_type(s);
755 if (type) {
756 return type;
760 return NULL;
764 static u_char *
765 ngx_http_xslt_encoding(xsltStylesheetPtr s)
767 u_char *encoding;
769 if (s->encoding) {
770 return s->encoding;
773 for (s = s->imports; s; s = s->next) {
775 encoding = ngx_http_xslt_encoding(s);
777 if (encoding) {
778 return encoding;
782 return NULL;
786 static void
787 ngx_http_xslt_cleanup(void *data)
789 ngx_free(data);
793 static char *
794 ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
796 ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
798 ngx_str_t *value;
799 ngx_uint_t i;
800 ngx_pool_cleanup_t *cln;
801 ngx_http_xslt_file_t *file;
802 ngx_http_xslt_filter_main_conf_t *xmcf;
804 if (xlcf->dtd) {
805 return "is duplicate";
808 value = cf->args->elts;
810 xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
812 file = xmcf->dtd_files.elts;
813 for (i = 0; i < xmcf->dtd_files.nelts; i++) {
814 if (ngx_strcmp(file[i].name, value[1].data) == 0) {
815 xlcf->dtd = file[i].data;
816 return NGX_CONF_OK;
820 cln = ngx_pool_cleanup_add(cf->pool, 0);
821 if (cln == NULL) {
822 return NGX_CONF_ERROR;
825 xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
827 if (xlcf->dtd == NULL) {
828 ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
829 return NGX_CONF_ERROR;
832 cln->handler = ngx_http_xslt_cleanup_dtd;
833 cln->data = xlcf->dtd;
835 file = ngx_array_push(&xmcf->dtd_files);
836 if (file == NULL) {
837 return NGX_CONF_ERROR;
840 file->name = value[1].data;
841 file->data = xlcf->dtd;
843 return NGX_CONF_OK;
848 static char *
849 ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
851 ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
853 ngx_str_t *value;
854 ngx_uint_t i, n;
855 ngx_pool_cleanup_t *cln;
856 ngx_http_xslt_file_t *file;
857 ngx_http_xslt_sheet_t *sheet;
858 ngx_http_xslt_param_t *param;
859 ngx_http_compile_complex_value_t ccv;
860 ngx_http_xslt_filter_main_conf_t *xmcf;
862 value = cf->args->elts;
864 if (xlcf->sheets.elts == NULL) {
865 if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
866 sizeof(ngx_http_xslt_sheet_t))
867 != NGX_OK)
869 return NGX_CONF_ERROR;
873 sheet = ngx_array_push(&xlcf->sheets);
874 if (sheet == NULL) {
875 return NGX_CONF_ERROR;
878 ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
880 if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
881 return NGX_CONF_ERROR;
884 xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
886 file = xmcf->sheet_files.elts;
887 for (i = 0; i < xmcf->sheet_files.nelts; i++) {
888 if (ngx_strcmp(file[i].name, value[1].data) == 0) {
889 sheet->stylesheet = file[i].data;
890 goto found;
894 cln = ngx_pool_cleanup_add(cf->pool, 0);
895 if (cln == NULL) {
896 return NGX_CONF_ERROR;
899 sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
900 if (sheet->stylesheet == NULL) {
901 ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
902 "xsltParseStylesheetFile(\"%s\") failed",
903 value[1].data);
904 return NGX_CONF_ERROR;
907 cln->handler = ngx_http_xslt_cleanup_stylesheet;
908 cln->data = sheet->stylesheet;
910 file = ngx_array_push(&xmcf->sheet_files);
911 if (file == NULL) {
912 return NGX_CONF_ERROR;
915 file->name = value[1].data;
916 file->data = sheet->stylesheet;
918 found:
920 n = cf->args->nelts;
922 if (n == 2) {
923 return NGX_CONF_OK;
926 if (ngx_array_init(&sheet->params, cf->pool, n - 2,
927 sizeof(ngx_http_xslt_param_t))
928 != NGX_OK)
930 return NGX_CONF_ERROR;
933 for (i = 2; i < n; i++) {
935 param = ngx_array_push(&sheet->params);
936 if (param == NULL) {
937 return NGX_CONF_ERROR;
940 ngx_memzero(param, sizeof(ngx_http_xslt_param_t));
941 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
943 ccv.cf = cf;
944 ccv.value = &value[i];
945 ccv.complex_value = &param->value;
946 ccv.zero = 1;
948 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
949 return NGX_CONF_ERROR;
953 return NGX_CONF_OK;
957 static char *
958 ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
960 ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
962 ngx_http_xslt_param_t *param;
963 ngx_http_compile_complex_value_t ccv;
964 ngx_str_t *value;
966 value = cf->args->elts;
968 if (xlcf->params == NULL) {
969 xlcf->params = ngx_array_create(cf->pool, 2,
970 sizeof(ngx_http_xslt_param_t));
971 if (xlcf->params == NULL) {
972 return NGX_CONF_ERROR;
976 param = ngx_array_push(xlcf->params);
977 if (param == NULL) {
978 return NGX_CONF_ERROR;
981 param->name = value[1].data;
982 param->quote = (cmd->post == NULL) ? 0 : 1;
984 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
986 ccv.cf = cf;
987 ccv.value = &value[2];
988 ccv.complex_value = &param->value;
989 ccv.zero = 1;
991 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
992 return NGX_CONF_ERROR;
995 return NGX_CONF_OK;
999 static void
1000 ngx_http_xslt_cleanup_dtd(void *data)
1002 xmlFreeDtd(data);
1006 static void
1007 ngx_http_xslt_cleanup_stylesheet(void *data)
1009 xsltFreeStylesheet(data);
1013 static void *
1014 ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)
1016 ngx_http_xslt_filter_main_conf_t *conf;
1018 conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
1019 if (conf == NULL) {
1020 return NULL;
1023 if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
1024 sizeof(ngx_http_xslt_file_t))
1025 != NGX_OK)
1027 return NULL;
1030 if (ngx_array_init(&conf->sheet_files, cf->pool, 1,
1031 sizeof(ngx_http_xslt_file_t))
1032 != NGX_OK)
1034 return NULL;
1037 return conf;
1041 static void *
1042 ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
1044 ngx_http_xslt_filter_loc_conf_t *conf;
1046 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
1047 if (conf == NULL) {
1048 return NULL;
1052 * set by ngx_pcalloc():
1054 * conf->dtd = NULL;
1055 * conf->sheets = { NULL };
1056 * conf->types = { NULL };
1057 * conf->types_keys = NULL;
1058 * conf->params = NULL;
1061 return conf;
1065 static char *
1066 ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1068 ngx_http_xslt_filter_loc_conf_t *prev = parent;
1069 ngx_http_xslt_filter_loc_conf_t *conf = child;
1071 if (conf->dtd == NULL) {
1072 conf->dtd = prev->dtd;
1075 if (conf->sheets.nelts == 0) {
1076 conf->sheets = prev->sheets;
1079 if (conf->params == NULL) {
1080 conf->params = prev->params;
1083 if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
1084 &prev->types_keys, &prev->types,
1085 ngx_http_xslt_default_types)
1086 != NGX_OK)
1088 return NGX_CONF_ERROR;
1091 return NGX_CONF_OK;
1095 static ngx_int_t
1096 ngx_http_xslt_filter_init(ngx_conf_t *cf)
1098 xmlInitParser();
1100 #if (NGX_HAVE_EXSLT)
1101 exsltRegisterAll();
1102 #endif
1104 ngx_http_next_header_filter = ngx_http_top_header_filter;
1105 ngx_http_top_header_filter = ngx_http_xslt_header_filter;
1107 ngx_http_next_body_filter = ngx_http_top_body_filter;
1108 ngx_http_top_body_filter = ngx_http_xslt_body_filter;
1110 return NGX_OK;
1114 static void
1115 ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
1117 xsltCleanupGlobals();
1118 xmlCleanupParser();