comparison src/http/modules/ngx_http_xslt_filter_module.c @ 392:34fb3a573548 NGINX_0_7_8

nginx 0.7.8 *) Feature: the ngx_http_xslt_module. *) Feature: the "$arg_..." variables. *) Feature: Solaris directio support. Thanks to Ivan Debnar. *) Bugfix: now if FastCGI server sends a "Location" header line without status line, then nginx uses 302 status code. Thanks to Maxim Dounin.
author Igor Sysoev <http://sysoev.ru>
date Mon, 04 Aug 2008 00:00:00 +0400
parents
children 05981f639d21
comparison
equal deleted inserted replaced
391:4ec606a899d3 392:34fb3a573548
1
2 /*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
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>
17
18
19 #ifndef NGX_HTTP_XSLT_REUSE_DTD
20 #define NGX_HTTP_XSLT_REUSE_DTD 1
21 #endif
22
23
24 typedef struct {
25 ngx_array_t *lengths;
26 ngx_array_t *values;
27 } ngx_http_xslt_param_t;
28
29
30 typedef struct {
31 xsltStylesheetPtr stylesheet;
32 ngx_array_t params; /* ngx_http_xslt_param_t */
33 } ngx_http_xslt_sheet_t;
34
35
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;
42
43
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;
53
54
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);
60
61
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, ...);
106
107
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);
113
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);
123
124
125 ngx_str_t ngx_http_xslt_default_types[] = {
126 ngx_string("text/xml"),
127 ngx_null_string
128 };
129
130
131 static ngx_command_t ngx_http_xslt_filter_commands[] = {
132
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,
137 0,
138 NULL },
139
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,
144 0,
145 NULL },
146
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] },
153
154 ngx_null_command
155 };
156
157
158 static ngx_http_module_t ngx_http_xslt_filter_module_ctx = {
159 NULL, /* preconfiguration */
160 ngx_http_xslt_filter_init, /* postconfiguration */
161
162 NULL, /* create main configuration */
163 NULL, /* init main configuration */
164
165 NULL, /* create server configuration */
166 NULL, /* merge server configuration */
167
168 ngx_http_xslt_filter_create_conf, /* create location configuration */
169 ngx_http_xslt_filter_merge_conf /* merge location configuration */
170 };
171
172
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
186 };
187
188
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;
191
192
193 static ngx_int_t
194 ngx_http_xslt_header_filter(ngx_http_request_t *r)
195 {
196 ngx_http_xslt_filter_ctx_t *ctx;
197 ngx_http_xslt_filter_conf_t *conf;
198
199 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
200 "xslt filter header");
201
202 if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
203 return ngx_http_next_header_filter(r);
204 }
205
206 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
207
208 if (conf->sheets.nelts == 0
209 || ngx_http_test_content_type(r, &conf->types_hash) == NULL)
210 {
211 return ngx_http_next_header_filter(r);
212 }
213
214 ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
215
216 if (ctx) {
217 return ngx_http_next_header_filter(r);
218 }
219
220 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
221 if (ctx == NULL) {
222 return NGX_ERROR;
223 }
224
225 ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
226
227 r->main_filter_need_in_memory = 1;
228
229 return NGX_OK;
230 }
231
232
233 static ngx_int_t
234 ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
235 {
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) {
270
271 ctx->doc = ctx->ctxt->myDoc;
272
273 #if (NGX_HTTP_XSLT_REUSE_DTD)
274 ctx->doc->extSubset = NULL;
275 #endif
276
277 xmlFreeParserCtxt(ctx->ctxt);
278
279 if (ctx->ctxt->wellFormed) {
280 return ngx_http_xslt_send(r, ctx,
281 ngx_http_xslt_apply_stylesheet(r, ctx));
282 }
283
284 xmlFreeDoc(ctx->doc);
285
286 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
287 "not well formed XML document");
288
289 return ngx_http_xslt_send(r, ctx, NULL);
290 }
291 }
292
293 return NGX_OK;
294 }
295
296
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)
300 {
301 ngx_int_t rc;
302 ngx_chain_t out;
303 ngx_pool_cleanup_t *cln;
304
305 ctx->done = 1;
306
307 if (b == NULL) {
308 return ngx_http_xslt_filter_internal_error(r);
309 }
310
311 cln = ngx_pool_cleanup_add(r->pool, 0);
312
313 if (cln == NULL) {
314 ngx_free(b->pos);
315 return ngx_http_special_response_handler(r,
316 NGX_HTTP_INTERNAL_SERVER_ERROR);
317 }
318
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";
323 }
324
325 r->headers_out.content_length_n = b->last - b->pos;
326
327 if (r->headers_out.content_length) {
328 r->headers_out.content_length->hash = 0;
329 r->headers_out.content_length = NULL;
330 }
331
332 r->allow_ranges = 1;
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_filter_internal_error(ngx_http_request_t *r)
353 {
354 ngx_int_t rc;
355
356 /* clear the modules contexts */
357 ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
358
359 rc = ngx_http_special_response_handler(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
360
361 /* NGX_ERROR resets any pending data */
362
363 return (rc == NGX_OK) ? NGX_ERROR : rc;
364 }
365
366
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)
370 {
371 int err;
372 xmlSAXHandler *sax;
373 xmlParserCtxtPtr ctxt;
374
375 if (ctx->ctxt == NULL) {
376
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;
382 }
383
384 ctx->sax = ngx_palloc(r->pool, sizeof(xmlSAXHandler));
385 if (ctx->sax == NULL) {
386 return NGX_ERROR;
387 }
388
389 sax = ctxt->sax;
390
391 ngx_memcpy(ctx->sax, sax, sizeof(xmlSAXHandler));
392
393 sax->startDocument = ngx_http_xslt_sax_start_document;
394 sax->endDocument = ngx_http_xslt_sax_end_document;
395
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;
404
405 sax->startElementNs = ngx_http_xslt_sax_start_element;
406 sax->endElementNs = ngx_http_xslt_sax_end_element;
407
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;
417
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;
421
422 sax->warning = NULL;
423 sax->error = ngx_http_xslt_sax_error;
424 sax->fatalError = ngx_http_xslt_sax_error;
425
426 ctxt->userData = ctx;
427
428 ctxt->replaceEntities = 1;
429 ctxt->loadsubset = 1;
430
431 ctx->ctxt = ctxt;
432 ctx->request = r;
433 }
434
435 err = xmlParseChunk(ctx->ctxt, (char *) b->pos,
436 (int) (b->last - b->pos), b->last_buf);
437
438 if (err == 0) {
439 b->pos = b->last;
440 return NGX_OK;
441 }
442
443 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
444 "xmlParseChunk() failed, error:%d", err);
445
446 return NGX_ERROR;
447 }
448
449
450 static void
451 ngx_http_xslt_sax_start_document(void *data)
452 {
453 ngx_http_xslt_filter_ctx_t *ctx = data;
454
455 ctx->sax->startDocument(ctx->ctxt);
456 }
457
458
459 static void
460 ngx_http_xslt_sax_end_document(void *data)
461 {
462 ngx_http_xslt_filter_ctx_t *ctx = data;
463
464 ctx->sax->endDocument(ctx->ctxt);
465 }
466
467
468 static void
469 ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name,
470 const xmlChar *externalId, const xmlChar *systemId)
471 {
472 ngx_http_xslt_filter_ctx_t *ctx = data;
473
474 ctx->sax->internalSubset(ctx->ctxt, name, externalId, systemId);
475 }
476
477
478 static void
479 ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
480 const xmlChar *externalId, const xmlChar *systemId)
481 {
482 ngx_http_xslt_filter_ctx_t *ctx = data;
483
484 xmlDocPtr doc;
485 xmlDtdPtr dtd;
486 ngx_http_request_t *r;
487 ngx_http_xslt_filter_conf_t *conf;
488
489 r = ctx->request;
490
491 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
492
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 *) "");
498
499 doc = ctx->ctxt->myDoc;
500
501 #if (NGX_HTTP_XSLT_REUSE_DTD)
502
503 dtd = conf->dtd;
504
505 #else
506
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;
512 }
513
514 dtd->name = xmlStrdup(name);
515
516 if (doc->children == NULL) {
517 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
518
519 } else {
520 xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
521 }
522
523 #endif
524
525 doc->extSubset = dtd;
526 }
527
528
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)
532 {
533 ngx_http_xslt_filter_ctx_t *ctx = data;
534
535 ctx->sax->entityDecl(ctx->ctxt, name, type, publicId, systemId, content);
536 }
537
538
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)
543 {
544 ngx_http_xslt_filter_ctx_t *ctx = data;
545
546 ctx->sax->attributeDecl(ctx->ctxt, elem, fullname, type, def, defaultValue,
547 tree);
548 }
549
550
551 static void
552 ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name, int type,
553 xmlElementContentPtr content)
554 {
555 ngx_http_xslt_filter_ctx_t *ctx = data;
556
557 ctx->sax->elementDecl(ctx->ctxt, name, type, content);
558 }
559
560
561 static void
562 ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name,
563 const xmlChar *publicId, const xmlChar *systemId)
564 {
565 ngx_http_xslt_filter_ctx_t *ctx = data;
566
567 ctx->sax->notationDecl(ctx->ctxt, name, publicId, systemId);
568 }
569
570
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)
575 {
576 ngx_http_xslt_filter_ctx_t *ctx = data;
577
578 ctx->sax->unparsedEntityDecl(ctx->ctxt, name, publicId, systemId,
579 notationName);
580 }
581
582
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)
588 {
589 ngx_http_xslt_filter_ctx_t *ctx = data;
590
591 ctx->sax->startElementNs(ctx->ctxt, localname, prefix, URI, nb_namespaces,
592 namespaces, nb_attributes, nb_defaulted, attributes);
593 }
594
595
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)
601 {
602 ngx_http_xslt_filter_ctx_t *ctx = data;
603
604 ctx->sax->endElementNs(ctx->ctxt, localname, prefix, URI);
605 }
606
607
608 static void
609 ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len)
610 {
611 ngx_http_xslt_filter_ctx_t *ctx = data;
612
613 ctx->sax->characters(ctx->ctxt, p, len);
614 }
615
616
617 static void
618 ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p, int len)
619 {
620 ngx_http_xslt_filter_ctx_t *ctx = data;
621
622 ctx->sax->cdataBlock(ctx->ctxt, p, len);
623 }
624
625
626 static xmlEntityPtr
627 ngx_http_xslt_sax_get_entity(void *data, const xmlChar *name)
628 {
629 ngx_http_xslt_filter_ctx_t *ctx = data;
630
631 return ctx->sax->getEntity(ctx->ctxt, name);
632 }
633
634
635 static xmlEntityPtr
636 ngx_http_xslt_sax_get_parameter_entity(void *data, const xmlChar *name)
637 {
638 ngx_http_xslt_filter_ctx_t *ctx = data;
639
640 return ctx->sax->getParameterEntity(ctx->ctxt, name);
641 }
642
643
644 static xmlParserInputPtr
645 ngx_http_xslt_sax_resolve_entity(void *data, const xmlChar *publicId,
646 const xmlChar *systemId)
647 {
648 ngx_http_xslt_filter_ctx_t *ctx = data;
649
650 return ctx->sax->resolveEntity(ctx->ctxt, publicId, systemId);
651 }
652
653
654 static void
655 ngx_http_xslt_sax_reference(void *data, const xmlChar *name)
656 {
657 ngx_http_xslt_filter_ctx_t *ctx = data;
658
659 ctx->sax->reference(ctx->ctxt, name);
660 }
661
662
663 static void
664 ngx_http_xslt_sax_comment(void *data, const xmlChar *value)
665 {
666 ngx_http_xslt_filter_ctx_t *ctx = data;
667
668 ctx->sax->comment(ctx->ctxt, value);
669 }
670
671
672 static void
673 ngx_http_xslt_sax_processing_instruction(void *data, const xmlChar *target,
674 const xmlChar *pidata)
675 {
676 ngx_http_xslt_filter_ctx_t *ctx = data;
677
678 ctx->sax->processingInstruction(ctx->ctxt, target, pidata);
679 }
680
681
682 static int
683 ngx_http_xslt_sax_is_standalone(void *data)
684 {
685 ngx_http_xslt_filter_ctx_t *ctx = data;
686
687 return ctx->sax->isStandalone(ctx->ctxt);
688 }
689
690
691 static int
692 ngx_http_xslt_sax_has_internal_subset(void *data)
693 {
694 ngx_http_xslt_filter_ctx_t *ctx = data;
695
696 return ctx->sax->hasInternalSubset(ctx->ctxt);
697 }
698
699
700 static int
701 ngx_http_xslt_sax_has_external_subset(void *data)
702 {
703 ngx_http_xslt_filter_ctx_t *ctx = data;
704
705 return ctx->sax->hasExternalSubset(ctx->ctxt);
706 }
707
708
709 static void ngx_cdecl
710 ngx_http_xslt_sax_error(void *data, const char *msg, ...)
711 {
712 ngx_http_xslt_filter_ctx_t *ctx = data;
713
714 size_t n;
715 va_list args;
716 u_char buf[NGX_MAX_ERROR_STR];
717
718 buf[0] = '\0';
719
720 va_start(args, msg);
721 n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
722 va_end(args);
723
724 while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
725
726 ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
727 "libxml2 error: \"%*s\"", n, buf);
728 }
729
730
731 static ngx_buf_t *
732 ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
733 ngx_http_xslt_filter_ctx_t *ctx)
734 {
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;
742
743 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
744 sheet = conf->sheets.elts;
745 doc = ctx->doc;
746
747 /* preallocate array for 4 params */
748
749 if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
750 != NGX_OK)
751 {
752 xmlFreeDoc(doc);
753 return NULL;
754 }
755
756 for (i = 0; i < conf->sheets.nelts; i++) {
757
758 if (ngx_http_xslt_params(r, ctx, &sheet[i].params) != NGX_OK) {
759 xmlFreeDoc(doc);
760 return NULL;
761 }
762
763 res = xsltApplyStylesheet(sheet[i].stylesheet, doc, ctx->params.elts);
764
765 xmlFreeDoc(doc);
766
767 if (res == NULL) {
768 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
769 "xsltApplyStylesheet() failed");
770 return NULL;
771 }
772
773 doc = res;
774
775 /* reset array elements */
776 ctx->params.nelts = 0;
777 }
778
779 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
780 "xslt filter doc type: %d", doc->type);
781
782 ctx->html = (doc->type == XML_HTML_DOCUMENT_NODE) ? 1 : 0;
783
784 rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
785
786 xmlFreeDoc(doc);
787
788 if (rc != 0) {
789 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
790 "xsltSaveResultToString() failed");
791 return NULL;
792 }
793
794 if (len == 0) {
795 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
796 "xsltSaveResultToString() returned zero-length result");
797 return NULL;
798 }
799
800 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
801 if (b == NULL) {
802 ngx_free(buf);
803 return NULL;
804 }
805
806 b->pos = buf;
807 b->last = buf + len;
808 b->memory = 1;
809 b->last_buf = 1;
810
811 return b;
812 }
813
814
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)
818 {
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;
824
825 param = params->elts;
826
827 for (i = 0; i < params->nelts; i++) {
828
829 if (ngx_http_script_run(r, &string, param[i].lengths->elts, 1,
830 param[i].values->elts)
831 == NULL)
832 {
833 return NGX_ERROR;
834 }
835
836 last = string.data + string.len - 1;
837 *last = '\0';
838
839 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
840 "xslt filter param: \"%s\"", string.data);
841
842 p = string.data;
843
844 while (p && *p) {
845
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;
852 }
853 *p++ = '\0';
854
855 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
856 "xslt filter param name: \"%s\"", value);
857
858 s = ngx_array_push(&ctx->params);
859 if (s == NULL) {
860 return NGX_ERROR;
861 }
862
863 *s = value;
864
865 value = p;
866 p = (u_char *) ngx_strchr(p, ':');
867
868 if (p) {
869 len = p - value;
870 *p++ = '\0';
871
872 } else {
873 len = last - value;
874 }
875
876 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
877 "xslt filter param value: \"%s\"", value);
878
879 dst = value;
880 src = value;
881
882 ngx_unescape_uri(&dst, &src, len, 0);
883
884 *dst = '\0';
885
886 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
887 "xslt filter param unescaped: \"%s\"", value);
888
889 s = ngx_array_push(&ctx->params);
890 if (s == NULL) {
891 return NGX_ERROR;
892 }
893
894 *s = value;
895 }
896 }
897
898 s = ngx_array_push(&ctx->params);
899 if (s == NULL) {
900 return NGX_ERROR;
901 }
902
903 *s = NULL;
904
905 return NGX_OK;
906 }
907
908
909 static void
910 ngx_http_xslt_cleanup(void *data)
911 {
912 ngx_free(data);
913 }
914
915
916 static char *
917 ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
918 {
919 ngx_http_xslt_filter_conf_t *xlcf = conf;
920
921 ngx_str_t *value;
922
923 if (xlcf->dtd) {
924 return "is duplicate";
925 }
926
927 value = cf->args->elts;
928
929 xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
930
931 if (xlcf->dtd == NULL) {
932 ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
933 return NGX_CONF_ERROR;
934 }
935
936 return NGX_CONF_OK;
937 }
938
939
940
941 static char *
942 ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
943 {
944 ngx_http_xslt_filter_conf_t *xlcf = conf;
945
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;
952
953 value = cf->args->elts;
954
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)
959 {
960 return NGX_CONF_ERROR;
961 }
962 }
963
964 sheet = ngx_array_push(&xlcf->sheets);
965 if (sheet == NULL) {
966 return NGX_CONF_ERROR;
967 }
968
969 ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
970
971 if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
972 return NGX_CONF_ERROR;
973 }
974
975 cln = ngx_pool_cleanup_add(cf->pool, 0);
976 if (cln == NULL) {
977 return NGX_CONF_ERROR;
978 }
979
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;
986 }
987
988 cln->handler = ngx_http_xslt_cleanup_stylesheet;
989 cln->data = sheet->stylesheet;
990
991 n = cf->args->nelts;
992
993 if (n == 2) {
994 return NGX_CONF_OK;
995 }
996
997 if (ngx_array_init(&sheet->params, cf->pool, n - 2,
998 sizeof(ngx_http_xslt_param_t))
999 != NGX_OK)
1000 {
1001 return NGX_CONF_ERROR;
1002 }
1003
1004 for (i = 2; i < n; i++) {
1005
1006 param = ngx_array_push(&sheet->params);
1007 if (param == NULL) {
1008 return NGX_CONF_ERROR;
1009 }
1010
1011 param->lengths = NULL;
1012 param->values = NULL;
1013
1014 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1015
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;
1023
1024 if (ngx_http_script_compile(&sc) != NGX_OK) {
1025 return NGX_CONF_ERROR;
1026 }
1027 }
1028
1029 return NGX_CONF_OK;
1030 }
1031
1032
1033 static void
1034 ngx_http_xslt_cleanup_stylesheet(void *data)
1035 {
1036 xsltStylesheetPtr stylesheet = data;
1037
1038 xsltFreeStylesheet(stylesheet);
1039 }
1040
1041
1042
1043 static void *
1044 ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
1045 {
1046 ngx_http_xslt_filter_conf_t *conf;
1047
1048 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_conf_t));
1049 if (conf == NULL) {
1050 return NGX_CONF_ERROR;
1051 }
1052
1053 /*
1054 * set by ngx_pcalloc():
1055 *
1056 * conf->dtd
1057 * conf->sheets
1058 */
1059
1060 return conf;
1061 }
1062
1063
1064 static char *
1065 ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1066 {
1067 ngx_http_xslt_filter_conf_t *prev = parent;
1068 ngx_http_xslt_filter_conf_t *conf = child;
1069
1070 if (conf->dtd == NULL) {
1071 conf->dtd = prev->dtd;
1072 }
1073
1074 if (conf->sheets.nelts == 0) {
1075 conf->sheets = prev->sheets;
1076 }
1077
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)
1081 {
1082 return NGX_CONF_ERROR;
1083 }
1084
1085 return NGX_CONF_OK;
1086 }
1087
1088
1089 static ngx_int_t
1090 ngx_http_xslt_filter_init(ngx_conf_t *cf)
1091 {
1092 xmlInitParser();
1093
1094 ngx_http_next_header_filter = ngx_http_top_header_filter;
1095 ngx_http_top_header_filter = ngx_http_xslt_header_filter;
1096
1097 ngx_http_next_body_filter = ngx_http_top_body_filter;
1098 ngx_http_top_body_filter = ngx_http_xslt_body_filter;
1099
1100 return NGX_OK;
1101 }
1102
1103
1104 static void
1105 ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
1106 {
1107 xsltCleanupGlobals();
1108 xmlCleanupParser();
1109 }