Annotation of embedaddon/nginx/src/http/modules/ngx_http_xslt_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: #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>
19:
20: #if (NGX_HAVE_EXSLT)
21: #include <libexslt/exslt.h>
22: #endif
23:
24:
25: #ifndef NGX_HTTP_XSLT_REUSE_DTD
26: #define NGX_HTTP_XSLT_REUSE_DTD 1
27: #endif
28:
29:
30: typedef struct {
31: u_char *name;
32: void *data;
33: } ngx_http_xslt_file_t;
34:
35:
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;
40:
41:
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;
47:
48:
49: typedef struct {
50: xsltStylesheetPtr stylesheet;
51: ngx_array_t params; /* ngx_http_xslt_param_t */
52: } ngx_http_xslt_sheet_t;
53:
54:
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;
62:
63:
64: typedef struct {
65: xmlDocPtr doc;
66: xmlParserCtxtPtr ctxt;
67: xsltTransformContextPtr transform;
68: ngx_http_request_t *request;
69: ngx_array_t params;
70:
71: ngx_uint_t done; /* unsigned done:1; */
72: } ngx_http_xslt_filter_ctx_t;
73:
74:
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);
79:
80:
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, ...);
84:
85:
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);
93:
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);
108:
109:
110: ngx_str_t ngx_http_xslt_default_types[] = {
111: ngx_string("text/xml"),
112: ngx_null_string
113: };
114:
115:
116: static ngx_command_t ngx_http_xslt_filter_commands[] = {
117:
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,
122: 0,
123: NULL },
124:
125: { ngx_string("xslt_stylesheet"),
126: NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
127: ngx_http_xslt_stylesheet,
128: NGX_HTTP_LOC_CONF_OFFSET,
129: 0,
130: NULL },
131:
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,
136: 0,
137: NULL },
138:
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,
143: 0,
144: (void *) 1 },
145:
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] },
152:
153: ngx_null_command
154: };
155:
156:
157: static ngx_http_module_t ngx_http_xslt_filter_module_ctx = {
158: NULL, /* preconfiguration */
159: ngx_http_xslt_filter_init, /* postconfiguration */
160:
161: ngx_http_xslt_filter_create_main_conf, /* create main configuration */
162: NULL, /* init main configuration */
163:
164: NULL, /* create server configuration */
165: NULL, /* merge server configuration */
166:
167: ngx_http_xslt_filter_create_conf, /* create location configuration */
168: ngx_http_xslt_filter_merge_conf /* merge location configuration */
169: };
170:
171:
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
185: };
186:
187:
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;
190:
191:
192: static ngx_int_t
193: ngx_http_xslt_header_filter(ngx_http_request_t *r)
194: {
195: ngx_http_xslt_filter_ctx_t *ctx;
196: ngx_http_xslt_filter_loc_conf_t *conf;
197:
198: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
199: "xslt filter header");
200:
201: if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
202: return ngx_http_next_header_filter(r);
203: }
204:
205: conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
206:
207: if (conf->sheets.nelts == 0
208: || ngx_http_test_content_type(r, &conf->types) == NULL)
209: {
210: return ngx_http_next_header_filter(r);
211: }
212:
213: ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
214:
215: if (ctx) {
216: return ngx_http_next_header_filter(r);
217: }
218:
219: ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
220: if (ctx == NULL) {
221: return NGX_ERROR;
222: }
223:
224: ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
225:
226: r->main_filter_need_in_memory = 1;
227:
228: return NGX_OK;
229: }
230:
231:
232: static ngx_int_t
233: ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
234: {
235: int wellFormed;
236: ngx_chain_t *cl;
237: ngx_http_xslt_filter_ctx_t *ctx;
238:
239: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
240: "xslt filter body");
241:
242: if (in == NULL) {
243: return ngx_http_next_body_filter(r, in);
244: }
245:
246: ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
247:
248: if (ctx == NULL || ctx->done) {
249: return ngx_http_next_body_filter(r, in);
250: }
251:
252: for (cl = in; cl; cl = cl->next) {
253:
254: if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
255:
256: if (ctx->ctxt->myDoc) {
257:
258: #if (NGX_HTTP_XSLT_REUSE_DTD)
259: ctx->ctxt->myDoc->extSubset = NULL;
260: #endif
261: xmlFreeDoc(ctx->ctxt->myDoc);
262: }
263:
264: xmlFreeParserCtxt(ctx->ctxt);
265:
266: return ngx_http_xslt_send(r, ctx, NULL);
267: }
268:
269: if (cl->buf->last_buf || cl->buf->last_in_chain) {
270:
271: ctx->doc = ctx->ctxt->myDoc;
272:
273: #if (NGX_HTTP_XSLT_REUSE_DTD)
274: ctx->doc->extSubset = NULL;
275: #endif
276:
277: wellFormed = ctx->ctxt->wellFormed;
278:
279: xmlFreeParserCtxt(ctx->ctxt);
280:
281: if (wellFormed) {
282: return ngx_http_xslt_send(r, ctx,
283: ngx_http_xslt_apply_stylesheet(r, ctx));
284: }
285:
286: xmlFreeDoc(ctx->doc);
287:
288: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
289: "not well formed XML document");
290:
291: return ngx_http_xslt_send(r, ctx, NULL);
292: }
293: }
294:
295: return NGX_OK;
296: }
297:
298:
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)
302: {
303: ngx_int_t rc;
304: ngx_chain_t out;
305: ngx_pool_cleanup_t *cln;
306:
307: ctx->done = 1;
308:
309: if (b == NULL) {
310: return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
311: NGX_HTTP_INTERNAL_SERVER_ERROR);
312: }
313:
314: cln = ngx_pool_cleanup_add(r->pool, 0);
315:
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);
320: }
321:
322: if (r == r->main) {
323: r->headers_out.content_length_n = b->last - b->pos;
324:
325: if (r->headers_out.content_length) {
326: r->headers_out.content_length->hash = 0;
327: r->headers_out.content_length = NULL;
328: }
329:
330: ngx_http_clear_last_modified(r);
331: ngx_http_clear_etag(r);
332: }
333:
334: rc = ngx_http_next_header_filter(r);
335:
336: if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
337: ngx_free(b->pos);
338: return rc;
339: }
340:
341: cln->handler = ngx_http_xslt_cleanup;
342: cln->data = b->pos;
343:
344: out.buf = b;
345: out.next = NULL;
346:
347: return ngx_http_next_body_filter(r, &out);
348: }
349:
350:
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)
354: {
355: int err;
356: xmlParserCtxtPtr ctxt;
357:
358: if (ctx->ctxt == NULL) {
359:
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;
365: }
366: xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD
367: |XML_PARSE_NOWARNING);
368:
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;
374:
375: ctx->ctxt = ctxt;
376: ctx->request = r;
377: }
378:
379: err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),
380: (b->last_buf) || (b->last_in_chain));
381:
382: if (err == 0) {
383: b->pos = b->last;
384: return NGX_OK;
385: }
386:
387: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
388: "xmlParseChunk() failed, error:%d", err);
389:
390: return NGX_ERROR;
391: }
392:
393:
394: static void
395: ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
396: const xmlChar *externalId, const xmlChar *systemId)
397: {
398: xmlParserCtxtPtr ctxt = data;
399:
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;
405:
406: ctx = ctxt->sax->_private;
407: r = ctx->request;
408:
409: conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
410:
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 *) "");
416:
417: doc = ctxt->myDoc;
418:
419: #if (NGX_HTTP_XSLT_REUSE_DTD)
420:
421: dtd = conf->dtd;
422:
423: #else
424:
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;
430: }
431:
432: if (doc->children == NULL) {
433: xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
434:
435: } else {
436: xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
437: }
438:
439: #endif
440:
441: doc->extSubset = dtd;
442: }
443:
444:
445: static void ngx_cdecl
446: ngx_http_xslt_sax_error(void *data, const char *msg, ...)
447: {
448: xmlParserCtxtPtr ctxt = data;
449:
450: size_t n;
451: va_list args;
452: ngx_http_xslt_filter_ctx_t *ctx;
453: u_char buf[NGX_MAX_ERROR_STR];
454:
455: ctx = ctxt->sax->_private;
456:
457: buf[0] = '\0';
458:
459: va_start(args, msg);
460: n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
461: va_end(args);
462:
463: while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
464:
465: ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
466: "libxml2 error: \"%*s\"", n + 1, buf);
467: }
468:
469:
470: static ngx_buf_t *
471: ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
472: ngx_http_xslt_filter_ctx_t *ctx)
473: {
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;
482:
483: conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
484: sheet = conf->sheets.elts;
485: doc = ctx->doc;
486:
487: /* preallocate array for 4 params */
488:
489: if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
490: != NGX_OK)
491: {
492: xmlFreeDoc(doc);
493: return NULL;
494: }
495:
496: for (i = 0; i < conf->sheets.nelts; i++) {
497:
498: ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);
499: if (ctx->transform == NULL) {
500: xmlFreeDoc(doc);
501: return NULL;
502: }
503:
504: if (conf->params
505: && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)
506: {
507: xsltFreeTransformContext(ctx->transform);
508: xmlFreeDoc(doc);
509: return NULL;
510: }
511:
512: if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {
513: xsltFreeTransformContext(ctx->transform);
514: xmlFreeDoc(doc);
515: return NULL;
516: }
517:
518: res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,
519: ctx->params.elts, NULL, NULL,
520: ctx->transform);
521:
522: xsltFreeTransformContext(ctx->transform);
523: xmlFreeDoc(doc);
524:
525: if (res == NULL) {
526: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
527: "xsltApplyStylesheet() failed");
528: return NULL;
529: }
530:
531: doc = res;
532:
533: /* reset array elements */
534: ctx->params.nelts = 0;
535: }
536:
537: /* there must be at least one stylesheet */
538:
539: if (r == r->main) {
540: type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);
541:
542: } else {
543: type = NULL;
544: }
545:
546: encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);
547: doc_type = doc->type;
548:
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)");
553:
554: rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
555:
556: xmlFreeDoc(doc);
557:
558: if (rc != 0) {
559: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
560: "xsltSaveResultToString() failed");
561: return NULL;
562: }
563:
564: if (len == 0) {
565: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
566: "xsltSaveResultToString() returned zero-length result");
567: return NULL;
568: }
569:
570: b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
571: if (b == NULL) {
572: ngx_free(buf);
573: return NULL;
574: }
575:
576: b->pos = buf;
577: b->last = buf + len;
578: b->memory = 1;
579:
580: if (encoding) {
581: r->headers_out.charset.len = ngx_strlen(encoding);
582: r->headers_out.charset.data = encoding;
583: }
584:
585: if (r != r->main) {
586: return b;
587: }
588:
589: b->last_buf = 1;
590:
591: if (type) {
592: len = ngx_strlen(type);
593:
594: r->headers_out.content_type_len = len;
595: r->headers_out.content_type.len = len;
596: r->headers_out.content_type.data = type;
597:
598: } else if (doc_type == XML_HTML_DOCUMENT_NODE) {
599:
600: r->headers_out.content_type_len = sizeof("text/html") - 1;
601: ngx_str_set(&r->headers_out.content_type, "text/html");
602: }
603:
604: r->headers_out.content_type_lowcase = NULL;
605:
606: return b;
607: }
608:
609:
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)
613: {
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;
619:
620: param = params->elts;
621:
622: for (i = 0; i < params->nelts; i++) {
623:
624: if (ngx_http_complex_value(r, ¶m[i].value, &string) != NGX_OK) {
625: return NGX_ERROR;
626: }
627:
628: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
629: "xslt filter param: \"%s\"", string.data);
630:
631: if (param[i].name) {
632:
633: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
634: "xslt filter param name: \"%s\"", param[i].name);
635:
636: if (param[i].quote) {
637: if (xsltQuoteOneUserParam(ctx->transform, param[i].name,
638: string.data)
639: != 0)
640: {
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;
645: }
646:
647: continue;
648: }
649:
650: s = ngx_array_push(&ctx->params);
651: if (s == NULL) {
652: return NGX_ERROR;
653: }
654:
655: *s = param[i].name;
656:
657: s = ngx_array_push(&ctx->params);
658: if (s == NULL) {
659: return NGX_ERROR;
660: }
661:
662: *s = string.data;
663:
664: continue;
665: }
666:
667: /*
668: * parse param1=value1:param2=value2 syntax as used by parameters
669: * specified in xslt_stylesheet directives
670: */
671:
672: p = string.data;
673: last = string.data + string.len;
674:
675: while (p && *p) {
676:
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;
683: }
684: *p++ = '\0';
685:
686: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
687: "xslt filter param name: \"%s\"", value);
688:
689: s = ngx_array_push(&ctx->params);
690: if (s == NULL) {
691: return NGX_ERROR;
692: }
693:
694: *s = value;
695:
696: value = p;
697: p = (u_char *) ngx_strchr(p, ':');
698:
699: if (p) {
700: len = p - value;
701: *p++ = '\0';
702:
703: } else {
704: len = last - value;
705: }
706:
707: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
708: "xslt filter param value: \"%s\"", value);
709:
710: dst = value;
711: src = value;
712:
713: ngx_unescape_uri(&dst, &src, len, 0);
714:
715: *dst = '\0';
716:
717: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
718: "xslt filter param unescaped: \"%s\"", value);
719:
720: s = ngx_array_push(&ctx->params);
721: if (s == NULL) {
722: return NGX_ERROR;
723: }
724:
725: *s = value;
726: }
727: }
728:
729: if (final) {
730: s = ngx_array_push(&ctx->params);
731: if (s == NULL) {
732: return NGX_ERROR;
733: }
734:
735: *s = NULL;
736: }
737:
738: return NGX_OK;
739: }
740:
741:
742: static u_char *
743: ngx_http_xslt_content_type(xsltStylesheetPtr s)
744: {
745: u_char *type;
746:
747: if (s->mediaType) {
748: return s->mediaType;
749: }
750:
751: for (s = s->imports; s; s = s->next) {
752:
753: type = ngx_http_xslt_content_type(s);
754:
755: if (type) {
756: return type;
757: }
758: }
759:
760: return NULL;
761: }
762:
763:
764: static u_char *
765: ngx_http_xslt_encoding(xsltStylesheetPtr s)
766: {
767: u_char *encoding;
768:
769: if (s->encoding) {
770: return s->encoding;
771: }
772:
773: for (s = s->imports; s; s = s->next) {
774:
775: encoding = ngx_http_xslt_encoding(s);
776:
777: if (encoding) {
778: return encoding;
779: }
780: }
781:
782: return NULL;
783: }
784:
785:
786: static void
787: ngx_http_xslt_cleanup(void *data)
788: {
789: ngx_free(data);
790: }
791:
792:
793: static char *
794: ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
795: {
796: ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
797:
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;
803:
804: if (xlcf->dtd) {
805: return "is duplicate";
806: }
807:
808: value = cf->args->elts;
809:
810: xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
811:
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;
817: }
818: }
819:
820: cln = ngx_pool_cleanup_add(cf->pool, 0);
821: if (cln == NULL) {
822: return NGX_CONF_ERROR;
823: }
824:
825: xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
826:
827: if (xlcf->dtd == NULL) {
828: ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
829: return NGX_CONF_ERROR;
830: }
831:
832: cln->handler = ngx_http_xslt_cleanup_dtd;
833: cln->data = xlcf->dtd;
834:
835: file = ngx_array_push(&xmcf->dtd_files);
836: if (file == NULL) {
837: return NGX_CONF_ERROR;
838: }
839:
840: file->name = value[1].data;
841: file->data = xlcf->dtd;
842:
843: return NGX_CONF_OK;
844: }
845:
846:
847:
848: static char *
849: ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
850: {
851: ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
852:
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;
861:
862: value = cf->args->elts;
863:
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)
868: {
869: return NGX_CONF_ERROR;
870: }
871: }
872:
873: sheet = ngx_array_push(&xlcf->sheets);
874: if (sheet == NULL) {
875: return NGX_CONF_ERROR;
876: }
877:
878: ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
879:
880: if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
881: return NGX_CONF_ERROR;
882: }
883:
884: xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
885:
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;
891: }
892: }
893:
894: cln = ngx_pool_cleanup_add(cf->pool, 0);
895: if (cln == NULL) {
896: return NGX_CONF_ERROR;
897: }
898:
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;
905: }
906:
907: cln->handler = ngx_http_xslt_cleanup_stylesheet;
908: cln->data = sheet->stylesheet;
909:
910: file = ngx_array_push(&xmcf->sheet_files);
911: if (file == NULL) {
912: return NGX_CONF_ERROR;
913: }
914:
915: file->name = value[1].data;
916: file->data = sheet->stylesheet;
917:
918: found:
919:
920: n = cf->args->nelts;
921:
922: if (n == 2) {
923: return NGX_CONF_OK;
924: }
925:
926: if (ngx_array_init(&sheet->params, cf->pool, n - 2,
927: sizeof(ngx_http_xslt_param_t))
928: != NGX_OK)
929: {
930: return NGX_CONF_ERROR;
931: }
932:
933: for (i = 2; i < n; i++) {
934:
935: param = ngx_array_push(&sheet->params);
936: if (param == NULL) {
937: return NGX_CONF_ERROR;
938: }
939:
940: ngx_memzero(param, sizeof(ngx_http_xslt_param_t));
941: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
942:
943: ccv.cf = cf;
944: ccv.value = &value[i];
945: ccv.complex_value = ¶m->value;
946: ccv.zero = 1;
947:
948: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
949: return NGX_CONF_ERROR;
950: }
951: }
952:
953: return NGX_CONF_OK;
954: }
955:
956:
957: static char *
958: ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
959: {
960: ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
961:
962: ngx_http_xslt_param_t *param;
963: ngx_http_compile_complex_value_t ccv;
964: ngx_str_t *value;
965:
966: value = cf->args->elts;
967:
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;
973: }
974: }
975:
976: param = ngx_array_push(xlcf->params);
977: if (param == NULL) {
978: return NGX_CONF_ERROR;
979: }
980:
981: param->name = value[1].data;
982: param->quote = (cmd->post == NULL) ? 0 : 1;
983:
984: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
985:
986: ccv.cf = cf;
987: ccv.value = &value[2];
988: ccv.complex_value = ¶m->value;
989: ccv.zero = 1;
990:
991: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
992: return NGX_CONF_ERROR;
993: }
994:
995: return NGX_CONF_OK;
996: }
997:
998:
999: static void
1000: ngx_http_xslt_cleanup_dtd(void *data)
1001: {
1002: xmlFreeDtd(data);
1003: }
1004:
1005:
1006: static void
1007: ngx_http_xslt_cleanup_stylesheet(void *data)
1008: {
1009: xsltFreeStylesheet(data);
1010: }
1011:
1012:
1013: static void *
1014: ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)
1015: {
1016: ngx_http_xslt_filter_main_conf_t *conf;
1017:
1018: conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
1019: if (conf == NULL) {
1020: return NULL;
1021: }
1022:
1023: if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
1024: sizeof(ngx_http_xslt_file_t))
1025: != NGX_OK)
1026: {
1027: return NULL;
1028: }
1029:
1030: if (ngx_array_init(&conf->sheet_files, cf->pool, 1,
1031: sizeof(ngx_http_xslt_file_t))
1032: != NGX_OK)
1033: {
1034: return NULL;
1035: }
1036:
1037: return conf;
1038: }
1039:
1040:
1041: static void *
1042: ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
1043: {
1044: ngx_http_xslt_filter_loc_conf_t *conf;
1045:
1046: conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
1047: if (conf == NULL) {
1048: return NULL;
1049: }
1050:
1051: /*
1052: * set by ngx_pcalloc():
1053: *
1054: * conf->dtd = NULL;
1055: * conf->sheets = { NULL };
1056: * conf->types = { NULL };
1057: * conf->types_keys = NULL;
1058: * conf->params = NULL;
1059: */
1060:
1061: return conf;
1062: }
1063:
1064:
1065: static char *
1066: ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1067: {
1068: ngx_http_xslt_filter_loc_conf_t *prev = parent;
1069: ngx_http_xslt_filter_loc_conf_t *conf = child;
1070:
1071: if (conf->dtd == NULL) {
1072: conf->dtd = prev->dtd;
1073: }
1074:
1075: if (conf->sheets.nelts == 0) {
1076: conf->sheets = prev->sheets;
1077: }
1078:
1079: if (conf->params == NULL) {
1080: conf->params = prev->params;
1081: }
1082:
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)
1087: {
1088: return NGX_CONF_ERROR;
1089: }
1090:
1091: return NGX_CONF_OK;
1092: }
1093:
1094:
1095: static ngx_int_t
1096: ngx_http_xslt_filter_init(ngx_conf_t *cf)
1097: {
1098: xmlInitParser();
1099:
1100: #if (NGX_HAVE_EXSLT)
1101: exsltRegisterAll();
1102: #endif
1103:
1104: ngx_http_next_header_filter = ngx_http_top_header_filter;
1105: ngx_http_top_header_filter = ngx_http_xslt_header_filter;
1106:
1107: ngx_http_next_body_filter = ngx_http_top_body_filter;
1108: ngx_http_top_body_filter = ngx_http_xslt_body_filter;
1109:
1110: return NGX_OK;
1111: }
1112:
1113:
1114: static void
1115: ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
1116: {
1117: xsltCleanupGlobals();
1118: xmlCleanupParser();
1119: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>