Mercurial > hg > nginx
annotate src/http/modules/ngx_http_xslt_filter_module.c @ 2152:b23e83f5b182
we do not need dtd->name
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Tue, 05 Aug 2008 13:08:35 +0000 |
parents | c8331f70d799 |
children | 6476e445d6ce |
rev | line source |
---|---|
2139 | 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 */ | |
2148 | 39 ngx_hash_t types; |
40 ngx_array_t *types_keys; | |
2139 | 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"), | |
2150
50cede290146
fix xslt module context levels
Igor Sysoev <igor@sysoev.ru>
parents:
2148
diff
changeset
|
134 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
2139 | 135 ngx_http_xslt_entities, |
136 NGX_HTTP_LOC_CONF_OFFSET, | |
137 0, | |
138 NULL }, | |
139 | |
140 { ngx_string("xslt_stylesheet"), | |
2151
c8331f70d799
xslt_stylesheet should be valid for location only
Igor Sysoev <igor@sysoev.ru>
parents:
2150
diff
changeset
|
141 NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, |
2139 | 142 ngx_http_xslt_stylesheet, |
143 NGX_HTTP_LOC_CONF_OFFSET, | |
144 0, | |
145 NULL }, | |
146 | |
147 { ngx_string("xslt_types"), | |
2150
50cede290146
fix xslt module context levels
Igor Sysoev <igor@sysoev.ru>
parents:
2148
diff
changeset
|
148 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, |
2139 | 149 ngx_http_types_slot, |
150 NGX_HTTP_LOC_CONF_OFFSET, | |
2148 | 151 offsetof(ngx_http_xslt_filter_conf_t, types_keys), |
2139 | 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 | |
2148 | 209 || ngx_http_test_content_type(r, &conf->types) == NULL) |
2139 | 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 if (doc->children == NULL) { | |
515 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd); | |
516 | |
517 } else { | |
518 xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd); | |
519 } | |
520 | |
521 #endif | |
522 | |
523 doc->extSubset = dtd; | |
524 } | |
525 | |
526 | |
527 static void | |
528 ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name, int type, | |
529 const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) | |
530 { | |
531 ngx_http_xslt_filter_ctx_t *ctx = data; | |
532 | |
533 ctx->sax->entityDecl(ctx->ctxt, name, type, publicId, systemId, content); | |
534 } | |
535 | |
536 | |
537 static void | |
538 ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem, | |
539 const xmlChar *fullname, int type, int def, const xmlChar *defaultValue, | |
540 xmlEnumerationPtr tree) | |
541 { | |
542 ngx_http_xslt_filter_ctx_t *ctx = data; | |
543 | |
544 ctx->sax->attributeDecl(ctx->ctxt, elem, fullname, type, def, defaultValue, | |
545 tree); | |
546 } | |
547 | |
548 | |
549 static void | |
550 ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name, int type, | |
551 xmlElementContentPtr content) | |
552 { | |
553 ngx_http_xslt_filter_ctx_t *ctx = data; | |
554 | |
555 ctx->sax->elementDecl(ctx->ctxt, name, type, content); | |
556 } | |
557 | |
558 | |
559 static void | |
560 ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name, | |
561 const xmlChar *publicId, const xmlChar *systemId) | |
562 { | |
563 ngx_http_xslt_filter_ctx_t *ctx = data; | |
564 | |
565 ctx->sax->notationDecl(ctx->ctxt, name, publicId, systemId); | |
566 } | |
567 | |
568 | |
569 static void | |
570 ngx_http_xslt_sax_unparsed_entity_decl(void *data, const xmlChar *name, | |
571 const xmlChar *publicId, const xmlChar *systemId, | |
572 const xmlChar *notationName) | |
573 { | |
574 ngx_http_xslt_filter_ctx_t *ctx = data; | |
575 | |
576 ctx->sax->unparsedEntityDecl(ctx->ctxt, name, publicId, systemId, | |
577 notationName); | |
578 } | |
579 | |
580 | |
581 static void | |
582 ngx_http_xslt_sax_start_element(void *data, const xmlChar *localname, | |
583 const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, | |
584 const xmlChar **namespaces, int nb_attributes, int nb_defaulted, | |
585 const xmlChar **attributes) | |
586 { | |
587 ngx_http_xslt_filter_ctx_t *ctx = data; | |
588 | |
589 ctx->sax->startElementNs(ctx->ctxt, localname, prefix, URI, nb_namespaces, | |
590 namespaces, nb_attributes, nb_defaulted, attributes); | |
591 } | |
592 | |
593 | |
594 static void | |
595 ngx_http_xslt_sax_end_element(void *data, | |
596 const xmlChar * localname ATTRIBUTE_UNUSED, | |
597 const xmlChar * prefix ATTRIBUTE_UNUSED, | |
598 const xmlChar * URI ATTRIBUTE_UNUSED) | |
599 { | |
600 ngx_http_xslt_filter_ctx_t *ctx = data; | |
601 | |
602 ctx->sax->endElementNs(ctx->ctxt, localname, prefix, URI); | |
603 } | |
604 | |
605 | |
606 static void | |
607 ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len) | |
608 { | |
609 ngx_http_xslt_filter_ctx_t *ctx = data; | |
610 | |
611 ctx->sax->characters(ctx->ctxt, p, len); | |
612 } | |
613 | |
614 | |
615 static void | |
616 ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p, int len) | |
617 { | |
618 ngx_http_xslt_filter_ctx_t *ctx = data; | |
619 | |
620 ctx->sax->cdataBlock(ctx->ctxt, p, len); | |
621 } | |
622 | |
623 | |
624 static xmlEntityPtr | |
625 ngx_http_xslt_sax_get_entity(void *data, const xmlChar *name) | |
626 { | |
627 ngx_http_xslt_filter_ctx_t *ctx = data; | |
628 | |
629 return ctx->sax->getEntity(ctx->ctxt, name); | |
630 } | |
631 | |
632 | |
633 static xmlEntityPtr | |
634 ngx_http_xslt_sax_get_parameter_entity(void *data, const xmlChar *name) | |
635 { | |
636 ngx_http_xslt_filter_ctx_t *ctx = data; | |
637 | |
638 return ctx->sax->getParameterEntity(ctx->ctxt, name); | |
639 } | |
640 | |
641 | |
642 static xmlParserInputPtr | |
643 ngx_http_xslt_sax_resolve_entity(void *data, const xmlChar *publicId, | |
644 const xmlChar *systemId) | |
645 { | |
646 ngx_http_xslt_filter_ctx_t *ctx = data; | |
647 | |
648 return ctx->sax->resolveEntity(ctx->ctxt, publicId, systemId); | |
649 } | |
650 | |
651 | |
652 static void | |
653 ngx_http_xslt_sax_reference(void *data, const xmlChar *name) | |
654 { | |
655 ngx_http_xslt_filter_ctx_t *ctx = data; | |
656 | |
657 ctx->sax->reference(ctx->ctxt, name); | |
658 } | |
659 | |
660 | |
661 static void | |
662 ngx_http_xslt_sax_comment(void *data, const xmlChar *value) | |
663 { | |
664 ngx_http_xslt_filter_ctx_t *ctx = data; | |
665 | |
666 ctx->sax->comment(ctx->ctxt, value); | |
667 } | |
668 | |
669 | |
670 static void | |
671 ngx_http_xslt_sax_processing_instruction(void *data, const xmlChar *target, | |
672 const xmlChar *pidata) | |
673 { | |
674 ngx_http_xslt_filter_ctx_t *ctx = data; | |
675 | |
676 ctx->sax->processingInstruction(ctx->ctxt, target, pidata); | |
677 } | |
678 | |
679 | |
680 static int | |
681 ngx_http_xslt_sax_is_standalone(void *data) | |
682 { | |
683 ngx_http_xslt_filter_ctx_t *ctx = data; | |
684 | |
685 return ctx->sax->isStandalone(ctx->ctxt); | |
686 } | |
687 | |
688 | |
689 static int | |
690 ngx_http_xslt_sax_has_internal_subset(void *data) | |
691 { | |
692 ngx_http_xslt_filter_ctx_t *ctx = data; | |
693 | |
694 return ctx->sax->hasInternalSubset(ctx->ctxt); | |
695 } | |
696 | |
697 | |
698 static int | |
699 ngx_http_xslt_sax_has_external_subset(void *data) | |
700 { | |
701 ngx_http_xslt_filter_ctx_t *ctx = data; | |
702 | |
703 return ctx->sax->hasExternalSubset(ctx->ctxt); | |
704 } | |
705 | |
706 | |
707 static void ngx_cdecl | |
708 ngx_http_xslt_sax_error(void *data, const char *msg, ...) | |
709 { | |
710 ngx_http_xslt_filter_ctx_t *ctx = data; | |
711 | |
712 size_t n; | |
713 va_list args; | |
714 u_char buf[NGX_MAX_ERROR_STR]; | |
715 | |
716 buf[0] = '\0'; | |
717 | |
718 va_start(args, msg); | |
719 n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args); | |
720 va_end(args); | |
721 | |
722 while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ } | |
723 | |
724 ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, | |
725 "libxml2 error: \"%*s\"", n, buf); | |
726 } | |
727 | |
728 | |
729 static ngx_buf_t * | |
730 ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r, | |
731 ngx_http_xslt_filter_ctx_t *ctx) | |
732 { | |
733 int len, rc; | |
734 ngx_buf_t *b; | |
735 ngx_uint_t i; | |
736 xmlChar *buf; | |
737 xmlDocPtr doc, res; | |
738 ngx_http_xslt_sheet_t *sheet; | |
739 ngx_http_xslt_filter_conf_t *conf; | |
740 | |
741 conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module); | |
742 sheet = conf->sheets.elts; | |
743 doc = ctx->doc; | |
744 | |
745 /* preallocate array for 4 params */ | |
746 | |
747 if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *)) | |
748 != NGX_OK) | |
749 { | |
750 xmlFreeDoc(doc); | |
751 return NULL; | |
752 } | |
753 | |
754 for (i = 0; i < conf->sheets.nelts; i++) { | |
755 | |
756 if (ngx_http_xslt_params(r, ctx, &sheet[i].params) != NGX_OK) { | |
757 xmlFreeDoc(doc); | |
758 return NULL; | |
759 } | |
760 | |
761 res = xsltApplyStylesheet(sheet[i].stylesheet, doc, ctx->params.elts); | |
762 | |
763 xmlFreeDoc(doc); | |
764 | |
765 if (res == NULL) { | |
766 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
767 "xsltApplyStylesheet() failed"); | |
768 return NULL; | |
769 } | |
770 | |
771 doc = res; | |
772 | |
773 /* reset array elements */ | |
774 ctx->params.nelts = 0; | |
775 } | |
776 | |
777 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
778 "xslt filter doc type: %d", doc->type); | |
779 | |
780 ctx->html = (doc->type == XML_HTML_DOCUMENT_NODE) ? 1 : 0; | |
781 | |
782 rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet); | |
783 | |
784 xmlFreeDoc(doc); | |
785 | |
786 if (rc != 0) { | |
787 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
788 "xsltSaveResultToString() failed"); | |
789 return NULL; | |
790 } | |
791 | |
792 if (len == 0) { | |
793 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
794 "xsltSaveResultToString() returned zero-length result"); | |
795 return NULL; | |
796 } | |
797 | |
798 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); | |
799 if (b == NULL) { | |
800 ngx_free(buf); | |
801 return NULL; | |
802 } | |
803 | |
804 b->pos = buf; | |
805 b->last = buf + len; | |
806 b->memory = 1; | |
807 b->last_buf = 1; | |
808 | |
809 return b; | |
810 } | |
811 | |
812 | |
813 static ngx_int_t | |
814 ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, | |
815 ngx_array_t *params) | |
816 { | |
817 u_char *p, *last, *value, *dst, *src, **s; | |
818 size_t len; | |
819 ngx_uint_t i; | |
820 ngx_str_t string; | |
821 ngx_http_xslt_param_t *param; | |
822 | |
823 param = params->elts; | |
824 | |
825 for (i = 0; i < params->nelts; i++) { | |
826 | |
827 if (ngx_http_script_run(r, &string, param[i].lengths->elts, 1, | |
828 param[i].values->elts) | |
829 == NULL) | |
830 { | |
831 return NGX_ERROR; | |
832 } | |
833 | |
834 last = string.data + string.len - 1; | |
835 *last = '\0'; | |
836 | |
837 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
838 "xslt filter param: \"%s\"", string.data); | |
839 | |
840 p = string.data; | |
841 | |
842 while (p && *p) { | |
843 | |
844 value = p; | |
845 p = (u_char *) ngx_strchr(p, '='); | |
846 if (p == NULL) { | |
847 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
848 "invalid libxslt parameter \"%s\"", value); | |
849 return NGX_ERROR; | |
850 } | |
851 *p++ = '\0'; | |
852 | |
853 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
854 "xslt filter param name: \"%s\"", value); | |
855 | |
856 s = ngx_array_push(&ctx->params); | |
857 if (s == NULL) { | |
858 return NGX_ERROR; | |
859 } | |
860 | |
861 *s = value; | |
862 | |
863 value = p; | |
864 p = (u_char *) ngx_strchr(p, ':'); | |
865 | |
866 if (p) { | |
867 len = p - value; | |
868 *p++ = '\0'; | |
869 | |
870 } else { | |
871 len = last - value; | |
872 } | |
873 | |
874 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
875 "xslt filter param value: \"%s\"", value); | |
876 | |
877 dst = value; | |
878 src = value; | |
879 | |
880 ngx_unescape_uri(&dst, &src, len, 0); | |
881 | |
882 *dst = '\0'; | |
883 | |
884 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
885 "xslt filter param unescaped: \"%s\"", value); | |
886 | |
887 s = ngx_array_push(&ctx->params); | |
888 if (s == NULL) { | |
889 return NGX_ERROR; | |
890 } | |
891 | |
892 *s = value; | |
893 } | |
894 } | |
895 | |
896 s = ngx_array_push(&ctx->params); | |
897 if (s == NULL) { | |
898 return NGX_ERROR; | |
899 } | |
900 | |
901 *s = NULL; | |
902 | |
903 return NGX_OK; | |
904 } | |
905 | |
906 | |
907 static void | |
908 ngx_http_xslt_cleanup(void *data) | |
909 { | |
910 ngx_free(data); | |
911 } | |
912 | |
913 | |
914 static char * | |
915 ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
916 { | |
917 ngx_http_xslt_filter_conf_t *xlcf = conf; | |
918 | |
919 ngx_str_t *value; | |
920 | |
921 if (xlcf->dtd) { | |
922 return "is duplicate"; | |
923 } | |
924 | |
925 value = cf->args->elts; | |
926 | |
927 xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data); | |
928 | |
929 if (xlcf->dtd == NULL) { | |
930 ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed"); | |
931 return NGX_CONF_ERROR; | |
932 } | |
933 | |
934 return NGX_CONF_OK; | |
935 } | |
936 | |
937 | |
938 | |
939 static char * | |
940 ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
941 { | |
942 ngx_http_xslt_filter_conf_t *xlcf = conf; | |
943 | |
944 ngx_str_t *value; | |
945 ngx_uint_t i, n; | |
946 ngx_pool_cleanup_t *cln; | |
947 ngx_http_xslt_sheet_t *sheet; | |
948 ngx_http_xslt_param_t *param; | |
949 ngx_http_script_compile_t sc; | |
950 | |
951 value = cf->args->elts; | |
952 | |
953 if (xlcf->sheets.elts == NULL) { | |
954 if (ngx_array_init(&xlcf->sheets, cf->pool, 1, | |
955 sizeof(ngx_http_xslt_sheet_t)) | |
956 != NGX_OK) | |
957 { | |
958 return NGX_CONF_ERROR; | |
959 } | |
960 } | |
961 | |
962 sheet = ngx_array_push(&xlcf->sheets); | |
963 if (sheet == NULL) { | |
964 return NGX_CONF_ERROR; | |
965 } | |
966 | |
967 ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t)); | |
968 | |
969 if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { | |
970 return NGX_CONF_ERROR; | |
971 } | |
972 | |
973 cln = ngx_pool_cleanup_add(cf->pool, 0); | |
974 if (cln == NULL) { | |
975 return NGX_CONF_ERROR; | |
976 } | |
977 | |
978 sheet->stylesheet = xsltParseStylesheetFile(value[1].data); | |
979 if (sheet->stylesheet == NULL) { | |
980 ngx_conf_log_error(NGX_LOG_ERR, cf, 0, | |
981 "xsltParseStylesheetFile(\"%s\") failed", | |
982 value[1].data); | |
983 return NGX_CONF_ERROR; | |
984 } | |
985 | |
986 cln->handler = ngx_http_xslt_cleanup_stylesheet; | |
987 cln->data = sheet->stylesheet; | |
988 | |
989 n = cf->args->nelts; | |
990 | |
991 if (n == 2) { | |
992 return NGX_CONF_OK; | |
993 } | |
994 | |
995 if (ngx_array_init(&sheet->params, cf->pool, n - 2, | |
996 sizeof(ngx_http_xslt_param_t)) | |
997 != NGX_OK) | |
998 { | |
999 return NGX_CONF_ERROR; | |
1000 } | |
1001 | |
1002 for (i = 2; i < n; i++) { | |
1003 | |
1004 param = ngx_array_push(&sheet->params); | |
1005 if (param == NULL) { | |
1006 return NGX_CONF_ERROR; | |
1007 } | |
1008 | |
1009 param->lengths = NULL; | |
1010 param->values = NULL; | |
1011 | |
1012 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); | |
1013 | |
1014 sc.cf = cf; | |
1015 sc.source = &value[i]; | |
1016 sc.lengths = ¶m->lengths; | |
1017 sc.values = ¶m->values; | |
1018 sc.variables = ngx_http_script_variables_count(&value[i]); | |
1019 sc.complete_lengths = 1; | |
1020 sc.complete_values = 1; | |
1021 | |
1022 if (ngx_http_script_compile(&sc) != NGX_OK) { | |
1023 return NGX_CONF_ERROR; | |
1024 } | |
1025 } | |
1026 | |
1027 return NGX_CONF_OK; | |
1028 } | |
1029 | |
1030 | |
1031 static void | |
1032 ngx_http_xslt_cleanup_stylesheet(void *data) | |
1033 { | |
1034 xsltStylesheetPtr stylesheet = data; | |
1035 | |
1036 xsltFreeStylesheet(stylesheet); | |
1037 } | |
1038 | |
1039 | |
1040 | |
1041 static void * | |
1042 ngx_http_xslt_filter_create_conf(ngx_conf_t *cf) | |
1043 { | |
1044 ngx_http_xslt_filter_conf_t *conf; | |
1045 | |
1046 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_conf_t)); | |
1047 if (conf == NULL) { | |
1048 return NGX_CONF_ERROR; | |
1049 } | |
1050 | |
1051 /* | |
1052 * set by ngx_pcalloc(): | |
1053 * | |
2148 | 1054 * conf->dtd = NULL; |
1055 * conf->sheets = { NULL }; | |
1056 * conf->types = { NULL }; | |
1057 * conf->types_keys = NULL; | |
2139 | 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 | |
2148 | 1078 if (ngx_http_merge_types(cf, conf->types_keys, &conf->types, |
1079 prev->types_keys, &prev->types, | |
1080 ngx_http_xslt_default_types) | |
2139 | 1081 != NGX_OK) |
1082 { | |
1083 return NGX_CONF_ERROR; | |
1084 } | |
1085 | |
1086 return NGX_CONF_OK; | |
1087 } | |
1088 | |
1089 | |
1090 static ngx_int_t | |
1091 ngx_http_xslt_filter_init(ngx_conf_t *cf) | |
1092 { | |
1093 xmlInitParser(); | |
1094 | |
1095 ngx_http_next_header_filter = ngx_http_top_header_filter; | |
1096 ngx_http_top_header_filter = ngx_http_xslt_header_filter; | |
1097 | |
1098 ngx_http_next_body_filter = ngx_http_top_body_filter; | |
1099 ngx_http_top_body_filter = ngx_http_xslt_body_filter; | |
1100 | |
1101 return NGX_OK; | |
1102 } | |
1103 | |
1104 | |
1105 static void | |
1106 ngx_http_xslt_filter_exit(ngx_cycle_t *cycle) | |
1107 { | |
1108 xsltCleanupGlobals(); | |
1109 xmlCleanupParser(); | |
1110 } |