Mercurial > hg > nginx-mail
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 = ¶m->lengths; | |
1019 sc.values = ¶m->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 } |