nginx 0.7.8
[nginx-catap.git] / src / http / modules / ngx_http_xslt_filter_module.c
blob364fed642dac5ba08d8c519d1f8c2f1a86fef7d5
2 /*
3 * Copyright (C) Igor Sysoev
4 */
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
11 #include <libxml/parser.h>
12 #include <libxml/tree.h>
13 #include <libxslt/xslt.h>
14 #include <libxslt/xsltInternals.h>
15 #include <libxslt/transform.h>
16 #include <libxslt/xsltutils.h>
19 #ifndef NGX_HTTP_XSLT_REUSE_DTD
20 #define NGX_HTTP_XSLT_REUSE_DTD 1
21 #endif
24 typedef struct {
25 ngx_array_t *lengths;
26 ngx_array_t *values;
27 } ngx_http_xslt_param_t;
30 typedef struct {
31 xsltStylesheetPtr stylesheet;
32 ngx_array_t params; /* ngx_http_xslt_param_t */
33 } ngx_http_xslt_sheet_t;
36 typedef struct {
37 xmlDtdPtr dtd;
38 ngx_array_t sheets; /* ngx_http_xslt_sheet_t */
39 ngx_hash_t types_hash;
40 ngx_array_t *keys;
41 } ngx_http_xslt_filter_conf_t;
44 typedef struct {
45 xmlDocPtr doc;
46 xmlParserCtxtPtr ctxt;
47 xmlSAXHandler *sax;
48 ngx_http_request_t *request;
49 ngx_array_t params;
50 unsigned done:1;
51 unsigned html:1;
52 } ngx_http_xslt_filter_ctx_t;
55 static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
56 ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
57 static ngx_int_t ngx_http_xslt_filter_internal_error(ngx_http_request_t *r);
58 static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
59 ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
62 static void ngx_http_xslt_sax_start_document(void *data);
63 static void ngx_http_xslt_sax_end_document(void *data);
64 static void ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name,
65 const xmlChar *externalId, const xmlChar *systemId);
66 static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
67 const xmlChar *externalId, const xmlChar *systemId);
68 static void ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name,
69 int type, const xmlChar *publicId, const xmlChar *systemId,
70 xmlChar *content);
71 static void ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem,
72 const xmlChar *fullname, int type, int def, const xmlChar *defaultValue,
73 xmlEnumerationPtr tree);
74 static void ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name,
75 int type, xmlElementContentPtr content);
76 static void ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name,
77 const xmlChar *publicId, const xmlChar *systemId);
78 static void ngx_http_xslt_sax_unparsed_entity_decl(void *data,
79 const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId,
80 const xmlChar *notationName);
81 static void ngx_http_xslt_sax_start_element(void *data,
82 const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI,
83 int nb_namespaces, const xmlChar **namespaces, int nb_attributes,
84 int nb_defaulted, const xmlChar **attributes);
85 static void ngx_http_xslt_sax_end_element(void *data,
86 const xmlChar * localname ATTRIBUTE_UNUSED,
87 const xmlChar * prefix ATTRIBUTE_UNUSED,
88 const xmlChar * URI ATTRIBUTE_UNUSED);
89 static void ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len);
90 static void ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p,
91 int len);
92 static xmlEntityPtr ngx_http_xslt_sax_get_entity(void *data,
93 const xmlChar *name);
94 static xmlEntityPtr ngx_http_xslt_sax_get_parameter_entity(void *data,
95 const xmlChar *name);
96 static xmlParserInputPtr ngx_http_xslt_sax_resolve_entity(void *data,
97 const xmlChar *publicId, const xmlChar *systemId);
98 static void ngx_http_xslt_sax_reference(void *data, const xmlChar *name);
99 static void ngx_http_xslt_sax_comment(void *data, const xmlChar *value);
100 static void ngx_http_xslt_sax_processing_instruction(void *data,
101 const xmlChar *target, const xmlChar *pidata);
102 static int ngx_http_xslt_sax_is_standalone(void *data);
103 static int ngx_http_xslt_sax_has_internal_subset(void *data);
104 static int ngx_http_xslt_sax_has_external_subset(void *data);
105 static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);
108 static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
109 ngx_http_xslt_filter_ctx_t *ctx);
110 static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,
111 ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params);
112 static void ngx_http_xslt_cleanup(void *data);
114 static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,
115 void *conf);
116 static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,
117 void *conf);
118 static void ngx_http_xslt_cleanup_stylesheet(void *data);
119 static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
120 static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
121 void *child);
122 static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
125 ngx_str_t ngx_http_xslt_default_types[] = {
126 ngx_string("text/xml"),
127 ngx_null_string
131 static ngx_command_t ngx_http_xslt_filter_commands[] = {
133 { ngx_string("xml_entities"),
134 NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
135 ngx_http_xslt_entities,
136 NGX_HTTP_LOC_CONF_OFFSET,
138 NULL },
140 { ngx_string("xslt_stylesheet"),
141 NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE,
142 ngx_http_xslt_stylesheet,
143 NGX_HTTP_LOC_CONF_OFFSET,
145 NULL },
147 { ngx_string("xslt_types"),
148 NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE,
149 ngx_http_types_slot,
150 NGX_HTTP_LOC_CONF_OFFSET,
151 offsetof(ngx_http_xslt_filter_conf_t, keys),
152 &ngx_http_xslt_default_types[0] },
154 ngx_null_command
158 static ngx_http_module_t ngx_http_xslt_filter_module_ctx = {
159 NULL, /* preconfiguration */
160 ngx_http_xslt_filter_init, /* postconfiguration */
162 NULL, /* create main configuration */
163 NULL, /* init main configuration */
165 NULL, /* create server configuration */
166 NULL, /* merge server configuration */
168 ngx_http_xslt_filter_create_conf, /* create location configuration */
169 ngx_http_xslt_filter_merge_conf /* merge location configuration */
173 ngx_module_t ngx_http_xslt_filter_module = {
174 NGX_MODULE_V1,
175 &ngx_http_xslt_filter_module_ctx, /* module context */
176 ngx_http_xslt_filter_commands, /* module directives */
177 NGX_HTTP_MODULE, /* module type */
178 NULL, /* init master */
179 NULL, /* init module */
180 NULL, /* init process */
181 NULL, /* init thread */
182 NULL, /* exit thread */
183 NULL, /* exit process */
184 NULL, /* exit master */
185 NGX_MODULE_V1_PADDING
189 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
190 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
193 static ngx_int_t
194 ngx_http_xslt_header_filter(ngx_http_request_t *r)
196 ngx_http_xslt_filter_ctx_t *ctx;
197 ngx_http_xslt_filter_conf_t *conf;
199 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
200 "xslt filter header");
202 if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
203 return ngx_http_next_header_filter(r);
206 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
208 if (conf->sheets.nelts == 0
209 || ngx_http_test_content_type(r, &conf->types_hash) == NULL)
211 return ngx_http_next_header_filter(r);
214 ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
216 if (ctx) {
217 return ngx_http_next_header_filter(r);
220 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
221 if (ctx == NULL) {
222 return NGX_ERROR;
225 ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
227 r->main_filter_need_in_memory = 1;
229 return NGX_OK;
233 static ngx_int_t
234 ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
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) {
271 ctx->doc = ctx->ctxt->myDoc;
273 #if (NGX_HTTP_XSLT_REUSE_DTD)
274 ctx->doc->extSubset = NULL;
275 #endif
277 xmlFreeParserCtxt(ctx->ctxt);
279 if (ctx->ctxt->wellFormed) {
280 return ngx_http_xslt_send(r, ctx,
281 ngx_http_xslt_apply_stylesheet(r, ctx));
284 xmlFreeDoc(ctx->doc);
286 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
287 "not well formed XML document");
289 return ngx_http_xslt_send(r, ctx, NULL);
293 return NGX_OK;
297 static ngx_int_t
298 ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
299 ngx_buf_t *b)
301 ngx_int_t rc;
302 ngx_chain_t out;
303 ngx_pool_cleanup_t *cln;
305 ctx->done = 1;
307 if (b == NULL) {
308 return ngx_http_xslt_filter_internal_error(r);
311 cln = ngx_pool_cleanup_add(r->pool, 0);
313 if (cln == NULL) {
314 ngx_free(b->pos);
315 return ngx_http_special_response_handler(r,
316 NGX_HTTP_INTERNAL_SERVER_ERROR);
319 if (ctx->html) {
320 r->headers_out.content_type_len = sizeof("text/html") - 1;
321 r->headers_out.content_type.len = sizeof("text/html") - 1;
322 r->headers_out.content_type.data = (u_char *) "text/html";
325 r->headers_out.content_length_n = b->last - b->pos;
327 if (r->headers_out.content_length) {
328 r->headers_out.content_length->hash = 0;
329 r->headers_out.content_length = NULL;
332 r->allow_ranges = 1;
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_filter_internal_error(ngx_http_request_t *r)
354 ngx_int_t rc;
356 /* clear the modules contexts */
357 ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
359 rc = ngx_http_special_response_handler(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
361 /* NGX_ERROR resets any pending data */
363 return (rc == NGX_OK) ? NGX_ERROR : rc;
367 static ngx_int_t
368 ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
369 ngx_buf_t *b)
371 int err;
372 xmlSAXHandler *sax;
373 xmlParserCtxtPtr ctxt;
375 if (ctx->ctxt == NULL) {
377 ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
378 if (ctxt == NULL) {
379 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
380 "xmlCreatePushParserCtxt() failed");
381 return NGX_ERROR;
384 ctx->sax = ngx_palloc(r->pool, sizeof(xmlSAXHandler));
385 if (ctx->sax == NULL) {
386 return NGX_ERROR;
389 sax = ctxt->sax;
391 ngx_memcpy(ctx->sax, sax, sizeof(xmlSAXHandler));
393 sax->startDocument = ngx_http_xslt_sax_start_document;
394 sax->endDocument = ngx_http_xslt_sax_end_document;
396 sax->internalSubset = ngx_http_xslt_sax_internal_subset;
397 sax->externalSubset = ngx_http_xslt_sax_external_subset;
398 sax->entityDecl = ngx_http_xslt_sax_entity_decl;
399 sax->attributeDecl = ngx_http_xslt_sax_attribute_decl;
400 sax->elementDecl = ngx_http_xslt_sax_element_decl;
401 sax->notationDecl = ngx_http_xslt_sax_notation_decl;
402 sax->unparsedEntityDecl = ngx_http_xslt_sax_unparsed_entity_decl;
403 sax->setDocumentLocator = NULL;
405 sax->startElementNs = ngx_http_xslt_sax_start_element;
406 sax->endElementNs = ngx_http_xslt_sax_end_element;
408 sax->characters = ngx_http_xslt_sax_characters;
409 sax->ignorableWhitespace = ngx_http_xslt_sax_characters;
410 sax->cdataBlock = ngx_http_xslt_sax_cdata_block;
411 sax->getEntity = ngx_http_xslt_sax_get_entity;
412 sax->resolveEntity = ngx_http_xslt_sax_resolve_entity;
413 sax->getParameterEntity = ngx_http_xslt_sax_get_parameter_entity;
414 sax->reference = ngx_http_xslt_sax_reference;
415 sax->comment = ngx_http_xslt_sax_comment;
416 sax->processingInstruction = ngx_http_xslt_sax_processing_instruction;
418 sax->isStandalone = ngx_http_xslt_sax_is_standalone;
419 sax->hasInternalSubset = ngx_http_xslt_sax_has_internal_subset;
420 sax->hasExternalSubset = ngx_http_xslt_sax_has_external_subset;
422 sax->warning = NULL;
423 sax->error = ngx_http_xslt_sax_error;
424 sax->fatalError = ngx_http_xslt_sax_error;
426 ctxt->userData = ctx;
428 ctxt->replaceEntities = 1;
429 ctxt->loadsubset = 1;
431 ctx->ctxt = ctxt;
432 ctx->request = r;
435 err = xmlParseChunk(ctx->ctxt, (char *) b->pos,
436 (int) (b->last - b->pos), b->last_buf);
438 if (err == 0) {
439 b->pos = b->last;
440 return NGX_OK;
443 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
444 "xmlParseChunk() failed, error:%d", err);
446 return NGX_ERROR;
450 static void
451 ngx_http_xslt_sax_start_document(void *data)
453 ngx_http_xslt_filter_ctx_t *ctx = data;
455 ctx->sax->startDocument(ctx->ctxt);
459 static void
460 ngx_http_xslt_sax_end_document(void *data)
462 ngx_http_xslt_filter_ctx_t *ctx = data;
464 ctx->sax->endDocument(ctx->ctxt);
468 static void
469 ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name,
470 const xmlChar *externalId, const xmlChar *systemId)
472 ngx_http_xslt_filter_ctx_t *ctx = data;
474 ctx->sax->internalSubset(ctx->ctxt, name, externalId, systemId);
478 static void
479 ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
480 const xmlChar *externalId, const xmlChar *systemId)
482 ngx_http_xslt_filter_ctx_t *ctx = data;
484 xmlDocPtr doc;
485 xmlDtdPtr dtd;
486 ngx_http_request_t *r;
487 ngx_http_xslt_filter_conf_t *conf;
489 r = ctx->request;
491 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
493 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
494 "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
495 name ? name : (xmlChar *) "",
496 externalId ? externalId : (xmlChar *) "",
497 systemId ? systemId : (xmlChar *) "");
499 doc = ctx->ctxt->myDoc;
501 #if (NGX_HTTP_XSLT_REUSE_DTD)
503 dtd = conf->dtd;
505 #else
507 dtd = xmlCopyDtd(conf->dtd);
508 if (dtd == NULL) {
509 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
510 "xmlCopyDtd() failed");
511 return;
514 dtd->name = xmlStrdup(name);
516 if (doc->children == NULL) {
517 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
519 } else {
520 xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
523 #endif
525 doc->extSubset = dtd;
529 static void
530 ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name, int type,
531 const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
533 ngx_http_xslt_filter_ctx_t *ctx = data;
535 ctx->sax->entityDecl(ctx->ctxt, name, type, publicId, systemId, content);
539 static void
540 ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem,
541 const xmlChar *fullname, int type, int def, const xmlChar *defaultValue,
542 xmlEnumerationPtr tree)
544 ngx_http_xslt_filter_ctx_t *ctx = data;
546 ctx->sax->attributeDecl(ctx->ctxt, elem, fullname, type, def, defaultValue,
547 tree);
551 static void
552 ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name, int type,
553 xmlElementContentPtr content)
555 ngx_http_xslt_filter_ctx_t *ctx = data;
557 ctx->sax->elementDecl(ctx->ctxt, name, type, content);
561 static void
562 ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name,
563 const xmlChar *publicId, const xmlChar *systemId)
565 ngx_http_xslt_filter_ctx_t *ctx = data;
567 ctx->sax->notationDecl(ctx->ctxt, name, publicId, systemId);
571 static void
572 ngx_http_xslt_sax_unparsed_entity_decl(void *data, const xmlChar *name,
573 const xmlChar *publicId, const xmlChar *systemId,
574 const xmlChar *notationName)
576 ngx_http_xslt_filter_ctx_t *ctx = data;
578 ctx->sax->unparsedEntityDecl(ctx->ctxt, name, publicId, systemId,
579 notationName);
583 static void
584 ngx_http_xslt_sax_start_element(void *data, const xmlChar *localname,
585 const xmlChar *prefix, const xmlChar *URI, int nb_namespaces,
586 const xmlChar **namespaces, int nb_attributes, int nb_defaulted,
587 const xmlChar **attributes)
589 ngx_http_xslt_filter_ctx_t *ctx = data;
591 ctx->sax->startElementNs(ctx->ctxt, localname, prefix, URI, nb_namespaces,
592 namespaces, nb_attributes, nb_defaulted, attributes);
596 static void
597 ngx_http_xslt_sax_end_element(void *data,
598 const xmlChar * localname ATTRIBUTE_UNUSED,
599 const xmlChar * prefix ATTRIBUTE_UNUSED,
600 const xmlChar * URI ATTRIBUTE_UNUSED)
602 ngx_http_xslt_filter_ctx_t *ctx = data;
604 ctx->sax->endElementNs(ctx->ctxt, localname, prefix, URI);
608 static void
609 ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len)
611 ngx_http_xslt_filter_ctx_t *ctx = data;
613 ctx->sax->characters(ctx->ctxt, p, len);
617 static void
618 ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p, int len)
620 ngx_http_xslt_filter_ctx_t *ctx = data;
622 ctx->sax->cdataBlock(ctx->ctxt, p, len);
626 static xmlEntityPtr
627 ngx_http_xslt_sax_get_entity(void *data, const xmlChar *name)
629 ngx_http_xslt_filter_ctx_t *ctx = data;
631 return ctx->sax->getEntity(ctx->ctxt, name);
635 static xmlEntityPtr
636 ngx_http_xslt_sax_get_parameter_entity(void *data, const xmlChar *name)
638 ngx_http_xslt_filter_ctx_t *ctx = data;
640 return ctx->sax->getParameterEntity(ctx->ctxt, name);
644 static xmlParserInputPtr
645 ngx_http_xslt_sax_resolve_entity(void *data, const xmlChar *publicId,
646 const xmlChar *systemId)
648 ngx_http_xslt_filter_ctx_t *ctx = data;
650 return ctx->sax->resolveEntity(ctx->ctxt, publicId, systemId);
654 static void
655 ngx_http_xslt_sax_reference(void *data, const xmlChar *name)
657 ngx_http_xslt_filter_ctx_t *ctx = data;
659 ctx->sax->reference(ctx->ctxt, name);
663 static void
664 ngx_http_xslt_sax_comment(void *data, const xmlChar *value)
666 ngx_http_xslt_filter_ctx_t *ctx = data;
668 ctx->sax->comment(ctx->ctxt, value);
672 static void
673 ngx_http_xslt_sax_processing_instruction(void *data, const xmlChar *target,
674 const xmlChar *pidata)
676 ngx_http_xslt_filter_ctx_t *ctx = data;
678 ctx->sax->processingInstruction(ctx->ctxt, target, pidata);
682 static int
683 ngx_http_xslt_sax_is_standalone(void *data)
685 ngx_http_xslt_filter_ctx_t *ctx = data;
687 return ctx->sax->isStandalone(ctx->ctxt);
691 static int
692 ngx_http_xslt_sax_has_internal_subset(void *data)
694 ngx_http_xslt_filter_ctx_t *ctx = data;
696 return ctx->sax->hasInternalSubset(ctx->ctxt);
700 static int
701 ngx_http_xslt_sax_has_external_subset(void *data)
703 ngx_http_xslt_filter_ctx_t *ctx = data;
705 return ctx->sax->hasExternalSubset(ctx->ctxt);
709 static void ngx_cdecl
710 ngx_http_xslt_sax_error(void *data, const char *msg, ...)
712 ngx_http_xslt_filter_ctx_t *ctx = data;
714 size_t n;
715 va_list args;
716 u_char buf[NGX_MAX_ERROR_STR];
718 buf[0] = '\0';
720 va_start(args, msg);
721 n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
722 va_end(args);
724 while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
726 ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
727 "libxml2 error: \"%*s\"", n, buf);
731 static ngx_buf_t *
732 ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
733 ngx_http_xslt_filter_ctx_t *ctx)
735 int len, rc;
736 ngx_buf_t *b;
737 ngx_uint_t i;
738 xmlChar *buf;
739 xmlDocPtr doc, res;
740 ngx_http_xslt_sheet_t *sheet;
741 ngx_http_xslt_filter_conf_t *conf;
743 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
744 sheet = conf->sheets.elts;
745 doc = ctx->doc;
747 /* preallocate array for 4 params */
749 if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
750 != NGX_OK)
752 xmlFreeDoc(doc);
753 return NULL;
756 for (i = 0; i < conf->sheets.nelts; i++) {
758 if (ngx_http_xslt_params(r, ctx, &sheet[i].params) != NGX_OK) {
759 xmlFreeDoc(doc);
760 return NULL;
763 res = xsltApplyStylesheet(sheet[i].stylesheet, doc, ctx->params.elts);
765 xmlFreeDoc(doc);
767 if (res == NULL) {
768 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
769 "xsltApplyStylesheet() failed");
770 return NULL;
773 doc = res;
775 /* reset array elements */
776 ctx->params.nelts = 0;
779 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
780 "xslt filter doc type: %d", doc->type);
782 ctx->html = (doc->type == XML_HTML_DOCUMENT_NODE) ? 1 : 0;
784 rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
786 xmlFreeDoc(doc);
788 if (rc != 0) {
789 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
790 "xsltSaveResultToString() failed");
791 return NULL;
794 if (len == 0) {
795 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
796 "xsltSaveResultToString() returned zero-length result");
797 return NULL;
800 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
801 if (b == NULL) {
802 ngx_free(buf);
803 return NULL;
806 b->pos = buf;
807 b->last = buf + len;
808 b->memory = 1;
809 b->last_buf = 1;
811 return b;
815 static ngx_int_t
816 ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
817 ngx_array_t *params)
819 u_char *p, *last, *value, *dst, *src, **s;
820 size_t len;
821 ngx_uint_t i;
822 ngx_str_t string;
823 ngx_http_xslt_param_t *param;
825 param = params->elts;
827 for (i = 0; i < params->nelts; i++) {
829 if (ngx_http_script_run(r, &string, param[i].lengths->elts, 1,
830 param[i].values->elts)
831 == NULL)
833 return NGX_ERROR;
836 last = string.data + string.len - 1;
837 *last = '\0';
839 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
840 "xslt filter param: \"%s\"", string.data);
842 p = string.data;
844 while (p && *p) {
846 value = p;
847 p = (u_char *) ngx_strchr(p, '=');
848 if (p == NULL) {
849 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
850 "invalid libxslt parameter \"%s\"", value);
851 return NGX_ERROR;
853 *p++ = '\0';
855 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
856 "xslt filter param name: \"%s\"", value);
858 s = ngx_array_push(&ctx->params);
859 if (s == NULL) {
860 return NGX_ERROR;
863 *s = value;
865 value = p;
866 p = (u_char *) ngx_strchr(p, ':');
868 if (p) {
869 len = p - value;
870 *p++ = '\0';
872 } else {
873 len = last - value;
876 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
877 "xslt filter param value: \"%s\"", value);
879 dst = value;
880 src = value;
882 ngx_unescape_uri(&dst, &src, len, 0);
884 *dst = '\0';
886 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
887 "xslt filter param unescaped: \"%s\"", value);
889 s = ngx_array_push(&ctx->params);
890 if (s == NULL) {
891 return NGX_ERROR;
894 *s = value;
898 s = ngx_array_push(&ctx->params);
899 if (s == NULL) {
900 return NGX_ERROR;
903 *s = NULL;
905 return NGX_OK;
909 static void
910 ngx_http_xslt_cleanup(void *data)
912 ngx_free(data);
916 static char *
917 ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
919 ngx_http_xslt_filter_conf_t *xlcf = conf;
921 ngx_str_t *value;
923 if (xlcf->dtd) {
924 return "is duplicate";
927 value = cf->args->elts;
929 xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
931 if (xlcf->dtd == NULL) {
932 ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
933 return NGX_CONF_ERROR;
936 return NGX_CONF_OK;
941 static char *
942 ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
944 ngx_http_xslt_filter_conf_t *xlcf = conf;
946 ngx_str_t *value;
947 ngx_uint_t i, n;
948 ngx_pool_cleanup_t *cln;
949 ngx_http_xslt_sheet_t *sheet;
950 ngx_http_xslt_param_t *param;
951 ngx_http_script_compile_t sc;
953 value = cf->args->elts;
955 if (xlcf->sheets.elts == NULL) {
956 if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
957 sizeof(ngx_http_xslt_sheet_t))
958 != NGX_OK)
960 return NGX_CONF_ERROR;
964 sheet = ngx_array_push(&xlcf->sheets);
965 if (sheet == NULL) {
966 return NGX_CONF_ERROR;
969 ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
971 if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
972 return NGX_CONF_ERROR;
975 cln = ngx_pool_cleanup_add(cf->pool, 0);
976 if (cln == NULL) {
977 return NGX_CONF_ERROR;
980 sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
981 if (sheet->stylesheet == NULL) {
982 ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
983 "xsltParseStylesheetFile(\"%s\") failed",
984 value[1].data);
985 return NGX_CONF_ERROR;
988 cln->handler = ngx_http_xslt_cleanup_stylesheet;
989 cln->data = sheet->stylesheet;
991 n = cf->args->nelts;
993 if (n == 2) {
994 return NGX_CONF_OK;
997 if (ngx_array_init(&sheet->params, cf->pool, n - 2,
998 sizeof(ngx_http_xslt_param_t))
999 != NGX_OK)
1001 return NGX_CONF_ERROR;
1004 for (i = 2; i < n; i++) {
1006 param = ngx_array_push(&sheet->params);
1007 if (param == NULL) {
1008 return NGX_CONF_ERROR;
1011 param->lengths = NULL;
1012 param->values = NULL;
1014 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1016 sc.cf = cf;
1017 sc.source = &value[i];
1018 sc.lengths = &param->lengths;
1019 sc.values = &param->values;
1020 sc.variables = ngx_http_script_variables_count(&value[i]);
1021 sc.complete_lengths = 1;
1022 sc.complete_values = 1;
1024 if (ngx_http_script_compile(&sc) != NGX_OK) {
1025 return NGX_CONF_ERROR;
1029 return NGX_CONF_OK;
1033 static void
1034 ngx_http_xslt_cleanup_stylesheet(void *data)
1036 xsltStylesheetPtr stylesheet = data;
1038 xsltFreeStylesheet(stylesheet);
1043 static void *
1044 ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
1046 ngx_http_xslt_filter_conf_t *conf;
1048 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_conf_t));
1049 if (conf == NULL) {
1050 return NGX_CONF_ERROR;
1054 * set by ngx_pcalloc():
1056 * conf->dtd
1057 * conf->sheets
1060 return conf;
1064 static char *
1065 ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1067 ngx_http_xslt_filter_conf_t *prev = parent;
1068 ngx_http_xslt_filter_conf_t *conf = child;
1070 if (conf->dtd == NULL) {
1071 conf->dtd = prev->dtd;
1074 if (conf->sheets.nelts == 0) {
1075 conf->sheets = prev->sheets;
1078 if (ngx_http_merge_types(cf, conf->keys, &conf->types_hash, prev->keys,
1079 &prev->types_hash, ngx_http_xslt_default_types)
1080 != NGX_OK)
1082 return NGX_CONF_ERROR;
1085 return NGX_CONF_OK;
1089 static ngx_int_t
1090 ngx_http_xslt_filter_init(ngx_conf_t *cf)
1092 xmlInitParser();
1094 ngx_http_next_header_filter = ngx_http_top_header_filter;
1095 ngx_http_top_header_filter = ngx_http_xslt_header_filter;
1097 ngx_http_next_body_filter = ngx_http_top_body_filter;
1098 ngx_http_top_body_filter = ngx_http_xslt_body_filter;
1100 return NGX_OK;
1104 static void
1105 ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
1107 xsltCleanupGlobals();
1108 xmlCleanupParser();