Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_xslt_filter_module.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 
12 #include <libxml/parser.h>
13 #include <libxml/tree.h>
14 #include <libxslt/xslt.h>
15 #include <libxslt/xsltInternals.h>
16 #include <libxslt/transform.h>
17 #include <libxslt/variables.h>
18 #include <libxslt/xsltutils.h>
19 
20 #if (NGX_HAVE_EXSLT)
21 #include <libexslt/exslt.h>
22 #endif
23 
24 
25 #ifndef NGX_HTTP_XSLT_REUSE_DTD
26 #define NGX_HTTP_XSLT_REUSE_DTD 1
27 #endif
28 
29 
30 typedef struct {
31  u_char *name;
32  void *data;
34 
35 
36 typedef struct {
37  ngx_array_t dtd_files; /* ngx_http_xslt_file_t */
38  ngx_array_t sheet_files; /* ngx_http_xslt_file_t */
40 
41 
42 typedef struct {
43  u_char *name;
45  ngx_uint_t quote; /* unsigned quote:1; */
47 
48 
49 typedef struct {
50  xsltStylesheetPtr stylesheet;
51  ngx_array_t params; /* ngx_http_xslt_param_t */
53 
54 
55 typedef struct {
56  xmlDtdPtr dtd;
57  ngx_array_t sheets; /* ngx_http_xslt_sheet_t */
60  ngx_array_t *params; /* ngx_http_xslt_param_t */
62 
63 
64 typedef struct {
65  xmlDocPtr doc;
66  xmlParserCtxtPtr ctxt;
67  xsltTransformContextPtr transform;
70 
71  ngx_uint_t done; /* unsigned done:1; */
73 
74 
75 static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
77 static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
79 
80 
81 static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
82  const xmlChar *externalId, const xmlChar *systemId);
83 static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);
84 
85 
86 static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
88 static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,
90 static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s);
91 static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s);
92 static void ngx_http_xslt_cleanup(void *data);
93 
94 static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,
95  void *conf);
96 static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,
97  void *conf);
98 static char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd,
99  void *conf);
100 static void ngx_http_xslt_cleanup_dtd(void *data);
101 static void ngx_http_xslt_cleanup_stylesheet(void *data);
102 static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf);
103 static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
104 static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
105  void *child);
106 static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
107 static void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle);
108 
109 
111  ngx_string("text/xml"),
113 };
114 
115 
116 static ngx_command_t ngx_http_xslt_filter_commands[] = {
117 
118  { ngx_string("xml_entities"),
120  ngx_http_xslt_entities,
122  0,
123  NULL },
124 
125  { ngx_string("xslt_stylesheet"),
127  ngx_http_xslt_stylesheet,
129  0,
130  NULL },
131 
132  { ngx_string("xslt_param"),
134  ngx_http_xslt_param,
136  0,
137  NULL },
138 
139  { ngx_string("xslt_string_param"),
141  ngx_http_xslt_param,
143  0,
144  (void *) 1 },
145 
146  { ngx_string("xslt_types"),
150  offsetof(ngx_http_xslt_filter_loc_conf_t, types_keys),
151  &ngx_http_xslt_default_types[0] },
152 
154 };
155 
156 
157 static ngx_http_module_t ngx_http_xslt_filter_module_ctx = {
158  NULL, /* preconfiguration */
159  ngx_http_xslt_filter_init, /* postconfiguration */
160 
161  ngx_http_xslt_filter_create_main_conf, /* create main configuration */
162  NULL, /* init main configuration */
163 
164  NULL, /* create server configuration */
165  NULL, /* merge server configuration */
166 
167  ngx_http_xslt_filter_create_conf, /* create location configuration */
168  ngx_http_xslt_filter_merge_conf /* merge location configuration */
169 };
170 
171 
174  &ngx_http_xslt_filter_module_ctx, /* module context */
175  ngx_http_xslt_filter_commands, /* module directives */
176  NGX_HTTP_MODULE, /* module type */
177  NULL, /* init master */
178  NULL, /* init module */
179  NULL, /* init process */
180  NULL, /* init thread */
181  NULL, /* exit thread */
182  ngx_http_xslt_filter_exit, /* exit process */
183  ngx_http_xslt_filter_exit, /* exit master */
185 };
186 
187 
188 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
189 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
190 
191 
192 static ngx_int_t
193 ngx_http_xslt_header_filter(ngx_http_request_t *r)
194 {
197 
199  "xslt filter header");
200 
202  return ngx_http_next_header_filter(r);
203  }
204 
205  conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
206 
207  if (conf->sheets.nelts == 0
208  || ngx_http_test_content_type(r, &conf->types) == NULL)
209  {
210  return ngx_http_next_header_filter(r);
211  }
212 
213  ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
214 
215  if (ctx) {
216  return ngx_http_next_header_filter(r);
217  }
218 
219  ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
220  if (ctx == NULL) {
221  return NGX_ERROR;
222  }
223 
224  ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
225 
227 
228  return NGX_OK;
229 }
230 
231 
232 static ngx_int_t
233 ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
234 {
235  int wellFormed;
236  ngx_chain_t *cl;
238 
240  "xslt filter body");
241 
242  if (in == NULL) {
243  return ngx_http_next_body_filter(r, in);
244  }
245 
246  ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
247 
248  if (ctx == NULL || ctx->done) {
249  return ngx_http_next_body_filter(r, in);
250  }
251 
252  for (cl = in; cl; cl = cl->next) {
253 
254  if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
255 
256  if (ctx->ctxt->myDoc) {
257 
258 #if (NGX_HTTP_XSLT_REUSE_DTD)
259  ctx->ctxt->myDoc->extSubset = NULL;
260 #endif
261  xmlFreeDoc(ctx->ctxt->myDoc);
262  }
263 
264  xmlFreeParserCtxt(ctx->ctxt);
265 
266  return ngx_http_xslt_send(r, ctx, NULL);
267  }
268 
269  if (cl->buf->last_buf || cl->buf->last_in_chain) {
270 
271  ctx->doc = ctx->ctxt->myDoc;
272 
273 #if (NGX_HTTP_XSLT_REUSE_DTD)
274  ctx->doc->extSubset = NULL;
275 #endif
276 
277  wellFormed = ctx->ctxt->wellFormed;
278 
279  xmlFreeParserCtxt(ctx->ctxt);
280 
281  if (wellFormed) {
282  return ngx_http_xslt_send(r, ctx,
283  ngx_http_xslt_apply_stylesheet(r, ctx));
284  }
285 
286  xmlFreeDoc(ctx->doc);
287 
289  "not well formed XML document");
290 
291  return ngx_http_xslt_send(r, ctx, NULL);
292  }
293  }
294 
295  return NGX_OK;
296 }
297 
298 
299 static ngx_int_t
300 ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
301  ngx_buf_t *b)
302 {
303  ngx_int_t rc;
304  ngx_chain_t out;
305  ngx_pool_cleanup_t *cln;
306 
307  ctx->done = 1;
308 
309  if (b == NULL) {
310  return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
312  }
313 
314  cln = ngx_pool_cleanup_add(r->pool, 0);
315 
316  if (cln == NULL) {
317  ngx_free(b->pos);
318  return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
320  }
321 
322  if (r == r->main) {
323  r->headers_out.content_length_n = b->last - b->pos;
324 
325  if (r->headers_out.content_length) {
327  r->headers_out.content_length = NULL;
328  }
329 
332  }
333 
334  rc = ngx_http_next_header_filter(r);
335 
336  if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
337  ngx_free(b->pos);
338  return rc;
339  }
340 
341  cln->handler = ngx_http_xslt_cleanup;
342  cln->data = b->pos;
343 
344  out.buf = b;
345  out.next = NULL;
346 
347  return ngx_http_next_body_filter(r, &out);
348 }
349 
350 
351 static ngx_int_t
352 ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
353  ngx_buf_t *b)
354 {
355  int err;
356  xmlParserCtxtPtr ctxt;
357 
358  if (ctx->ctxt == NULL) {
359 
360  ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
361  if (ctxt == NULL) {
363  "xmlCreatePushParserCtxt() failed");
364  return NGX_ERROR;
365  }
366  xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD
367  |XML_PARSE_NOWARNING);
368 
369  ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset;
370  ctxt->sax->setDocumentLocator = NULL;
371  ctxt->sax->error = ngx_http_xslt_sax_error;
372  ctxt->sax->fatalError = ngx_http_xslt_sax_error;
373  ctxt->sax->_private = ctx;
374 
375  ctx->ctxt = ctxt;
376  ctx->request = r;
377  }
378 
379  err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),
380  (b->last_buf) || (b->last_in_chain));
381 
382  if (err == 0) {
383  b->pos = b->last;
384  return NGX_OK;
385  }
386 
388  "xmlParseChunk() failed, error:%d", err);
389 
390  return NGX_ERROR;
391 }
392 
393 
394 static void
395 ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
396  const xmlChar *externalId, const xmlChar *systemId)
397 {
398  xmlParserCtxtPtr ctxt = data;
399 
400  xmlDocPtr doc;
401  xmlDtdPtr dtd;
405 
406  ctx = ctxt->sax->_private;
407  r = ctx->request;
408 
409  conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
410 
412  "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
413  name ? name : (xmlChar *) "",
414  externalId ? externalId : (xmlChar *) "",
415  systemId ? systemId : (xmlChar *) "");
416 
417  doc = ctxt->myDoc;
418 
419 #if (NGX_HTTP_XSLT_REUSE_DTD)
420 
421  dtd = conf->dtd;
422 
423 #else
424 
425  dtd = xmlCopyDtd(conf->dtd);
426  if (dtd == NULL) {
428  "xmlCopyDtd() failed");
429  return;
430  }
431 
432  if (doc->children == NULL) {
433  xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
434 
435  } else {
436  xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
437  }
438 
439 #endif
440 
441  doc->extSubset = dtd;
442 }
443 
444 
445 static void ngx_cdecl
446 ngx_http_xslt_sax_error(void *data, const char *msg, ...)
447 {
448  xmlParserCtxtPtr ctxt = data;
449 
450  size_t n;
451  va_list args;
453  u_char buf[NGX_MAX_ERROR_STR];
454 
455  ctx = ctxt->sax->_private;
456 
457  buf[0] = '\0';
458 
459  va_start(args, msg);
460  n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
461  va_end(args);
462 
463  while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
464 
466  "libxml2 error: \"%*s\"", n + 1, buf);
467 }
468 
469 
470 static ngx_buf_t *
471 ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
473 {
474  int len, rc, doc_type;
475  u_char *type, *encoding;
476  ngx_buf_t *b;
477  ngx_uint_t i;
478  xmlChar *buf;
479  xmlDocPtr doc, res;
480  ngx_http_xslt_sheet_t *sheet;
482 
483  conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
484  sheet = conf->sheets.elts;
485  doc = ctx->doc;
486 
487  /* preallocate array for 4 params */
488 
489  if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
490  != NGX_OK)
491  {
492  xmlFreeDoc(doc);
493  return NULL;
494  }
495 
496  for (i = 0; i < conf->sheets.nelts; i++) {
497 
498  ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);
499  if (ctx->transform == NULL) {
500  xmlFreeDoc(doc);
501  return NULL;
502  }
503 
504  if (conf->params
505  && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)
506  {
507  xsltFreeTransformContext(ctx->transform);
508  xmlFreeDoc(doc);
509  return NULL;
510  }
511 
512  if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {
513  xsltFreeTransformContext(ctx->transform);
514  xmlFreeDoc(doc);
515  return NULL;
516  }
517 
518  res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,
519  ctx->params.elts, NULL, NULL,
520  ctx->transform);
521 
522  xsltFreeTransformContext(ctx->transform);
523  xmlFreeDoc(doc);
524 
525  if (res == NULL) {
527  "xsltApplyStylesheet() failed");
528  return NULL;
529  }
530 
531  doc = res;
532 
533  /* reset array elements */
534  ctx->params.nelts = 0;
535  }
536 
537  /* there must be at least one stylesheet */
538 
539  if (r == r->main) {
540  type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);
541 
542  } else {
543  type = NULL;
544  }
545 
546  encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);
547  doc_type = doc->type;
548 
550  "xslt filter type: %d t:%s e:%s",
551  doc_type, type ? type : (u_char *) "(null)",
552  encoding ? encoding : (u_char *) "(null)");
553 
554  rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
555 
556  xmlFreeDoc(doc);
557 
558  if (rc != 0) {
560  "xsltSaveResultToString() failed");
561  return NULL;
562  }
563 
564  if (len == 0) {
566  "xsltSaveResultToString() returned zero-length result");
567  return NULL;
568  }
569 
570  b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
571  if (b == NULL) {
572  ngx_free(buf);
573  return NULL;
574  }
575 
576  b->pos = buf;
577  b->last = buf + len;
578  b->memory = 1;
579 
580  if (encoding) {
581  r->headers_out.charset.len = ngx_strlen(encoding);
582  r->headers_out.charset.data = encoding;
583  }
584 
585  if (r != r->main) {
586  return b;
587  }
588 
589  b->last_buf = 1;
590 
591  if (type) {
592  len = ngx_strlen(type);
593 
594  r->headers_out.content_type_len = len;
595  r->headers_out.content_type.len = len;
597 
598  } else if (doc_type == XML_HTML_DOCUMENT_NODE) {
599 
600  r->headers_out.content_type_len = sizeof("text/html") - 1;
601  ngx_str_set(&r->headers_out.content_type, "text/html");
602  }
603 
605 
606  return b;
607 }
608 
609 
610 static ngx_int_t
611 ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
612  ngx_array_t *params, ngx_uint_t final)
613 {
614  u_char *p, *last, *value, *dst, *src, **s;
615  size_t len;
616  ngx_uint_t i;
617  ngx_str_t string;
618  ngx_http_xslt_param_t *param;
619 
620  param = params->elts;
621 
622  for (i = 0; i < params->nelts; i++) {
623 
624  if (ngx_http_complex_value(r, &param[i].value, &string) != NGX_OK) {
625  return NGX_ERROR;
626  }
627 
629  "xslt filter param: \"%s\"", string.data);
630 
631  if (param[i].name) {
632 
634  "xslt filter param name: \"%s\"", param[i].name);
635 
636  if (param[i].quote) {
637  if (xsltQuoteOneUserParam(ctx->transform, param[i].name,
638  string.data)
639  != 0)
640  {
642  "xsltQuoteOneUserParam(\"%s\", \"%s\") failed",
643  param[i].name, string.data);
644  return NGX_ERROR;
645  }
646 
647  continue;
648  }
649 
650  s = ngx_array_push(&ctx->params);
651  if (s == NULL) {
652  return NGX_ERROR;
653  }
654 
655  *s = param[i].name;
656 
657  s = ngx_array_push(&ctx->params);
658  if (s == NULL) {
659  return NGX_ERROR;
660  }
661 
662  *s = string.data;
663 
664  continue;
665  }
666 
667  /*
668  * parse param1=value1:param2=value2 syntax as used by parameters
669  * specified in xslt_stylesheet directives
670  */
671 
672  p = string.data;
673  last = string.data + string.len;
674 
675  while (p && *p) {
676 
677  value = p;
678  p = (u_char *) ngx_strchr(p, '=');
679  if (p == NULL) {
681  "invalid libxslt parameter \"%s\"", value);
682  return NGX_ERROR;
683  }
684  *p++ = '\0';
685 
687  "xslt filter param name: \"%s\"", value);
688 
689  s = ngx_array_push(&ctx->params);
690  if (s == NULL) {
691  return NGX_ERROR;
692  }
693 
694  *s = value;
695 
696  value = p;
697  p = (u_char *) ngx_strchr(p, ':');
698 
699  if (p) {
700  len = p - value;
701  *p++ = '\0';
702 
703  } else {
704  len = last - value;
705  }
706 
708  "xslt filter param value: \"%s\"", value);
709 
710  dst = value;
711  src = value;
712 
713  ngx_unescape_uri(&dst, &src, len, 0);
714 
715  *dst = '\0';
716 
718  "xslt filter param unescaped: \"%s\"", value);
719 
720  s = ngx_array_push(&ctx->params);
721  if (s == NULL) {
722  return NGX_ERROR;
723  }
724 
725  *s = value;
726  }
727  }
728 
729  if (final) {
730  s = ngx_array_push(&ctx->params);
731  if (s == NULL) {
732  return NGX_ERROR;
733  }
734 
735  *s = NULL;
736  }
737 
738  return NGX_OK;
739 }
740 
741 
742 static u_char *
743 ngx_http_xslt_content_type(xsltStylesheetPtr s)
744 {
745  u_char *type;
746 
747  if (s->mediaType) {
748  return s->mediaType;
749  }
750 
751  for (s = s->imports; s; s = s->next) {
752 
753  type = ngx_http_xslt_content_type(s);
754 
755  if (type) {
756  return type;
757  }
758  }
759 
760  return NULL;
761 }
762 
763 
764 static u_char *
765 ngx_http_xslt_encoding(xsltStylesheetPtr s)
766 {
767  u_char *encoding;
768 
769  if (s->encoding) {
770  return s->encoding;
771  }
772 
773  for (s = s->imports; s; s = s->next) {
774 
775  encoding = ngx_http_xslt_encoding(s);
776 
777  if (encoding) {
778  return encoding;
779  }
780  }
781 
782  return NULL;
783 }
784 
785 
786 static void
787 ngx_http_xslt_cleanup(void *data)
788 {
789  ngx_free(data);
790 }
791 
792 
793 static char *
794 ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
795 {
796  ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
797 
798  ngx_str_t *value;
799  ngx_uint_t i;
800  ngx_pool_cleanup_t *cln;
801  ngx_http_xslt_file_t *file;
803 
804  if (xlcf->dtd) {
805  return "is duplicate";
806  }
807 
808  value = cf->args->elts;
809 
810  xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
811 
812  file = xmcf->dtd_files.elts;
813  for (i = 0; i < xmcf->dtd_files.nelts; i++) {
814  if (ngx_strcmp(file[i].name, value[1].data) == 0) {
815  xlcf->dtd = file[i].data;
816  return NGX_CONF_OK;
817  }
818  }
819 
820  cln = ngx_pool_cleanup_add(cf->pool, 0);
821  if (cln == NULL) {
822  return NGX_CONF_ERROR;
823  }
824 
825  xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
826 
827  if (xlcf->dtd == NULL) {
828  ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
829  return NGX_CONF_ERROR;
830  }
831 
832  cln->handler = ngx_http_xslt_cleanup_dtd;
833  cln->data = xlcf->dtd;
834 
835  file = ngx_array_push(&xmcf->dtd_files);
836  if (file == NULL) {
837  return NGX_CONF_ERROR;
838  }
839 
840  file->name = value[1].data;
841  file->data = xlcf->dtd;
842 
843  return NGX_CONF_OK;
844 }
845 
846 
847 
848 static char *
849 ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
850 {
851  ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
852 
853  ngx_str_t *value;
854  ngx_uint_t i, n;
855  ngx_pool_cleanup_t *cln;
856  ngx_http_xslt_file_t *file;
857  ngx_http_xslt_sheet_t *sheet;
858  ngx_http_xslt_param_t *param;
861 
862  value = cf->args->elts;
863 
864  if (xlcf->sheets.elts == NULL) {
865  if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
866  sizeof(ngx_http_xslt_sheet_t))
867  != NGX_OK)
868  {
869  return NGX_CONF_ERROR;
870  }
871  }
872 
873  sheet = ngx_array_push(&xlcf->sheets);
874  if (sheet == NULL) {
875  return NGX_CONF_ERROR;
876  }
877 
878  ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
879 
880  if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
881  return NGX_CONF_ERROR;
882  }
883 
884  xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
885 
886  file = xmcf->sheet_files.elts;
887  for (i = 0; i < xmcf->sheet_files.nelts; i++) {
888  if (ngx_strcmp(file[i].name, value[1].data) == 0) {
889  sheet->stylesheet = file[i].data;
890  goto found;
891  }
892  }
893 
894  cln = ngx_pool_cleanup_add(cf->pool, 0);
895  if (cln == NULL) {
896  return NGX_CONF_ERROR;
897  }
898 
899  sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
900  if (sheet->stylesheet == NULL) {
902  "xsltParseStylesheetFile(\"%s\") failed",
903  value[1].data);
904  return NGX_CONF_ERROR;
905  }
906 
907  cln->handler = ngx_http_xslt_cleanup_stylesheet;
908  cln->data = sheet->stylesheet;
909 
910  file = ngx_array_push(&xmcf->sheet_files);
911  if (file == NULL) {
912  return NGX_CONF_ERROR;
913  }
914 
915  file->name = value[1].data;
916  file->data = sheet->stylesheet;
917 
918 found:
919 
920  n = cf->args->nelts;
921 
922  if (n == 2) {
923  return NGX_CONF_OK;
924  }
925 
926  if (ngx_array_init(&sheet->params, cf->pool, n - 2,
927  sizeof(ngx_http_xslt_param_t))
928  != NGX_OK)
929  {
930  return NGX_CONF_ERROR;
931  }
932 
933  for (i = 2; i < n; i++) {
934 
935  param = ngx_array_push(&sheet->params);
936  if (param == NULL) {
937  return NGX_CONF_ERROR;
938  }
939 
940  ngx_memzero(param, sizeof(ngx_http_xslt_param_t));
942 
943  ccv.cf = cf;
944  ccv.value = &value[i];
945  ccv.complex_value = &param->value;
946  ccv.zero = 1;
947 
948  if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
949  return NGX_CONF_ERROR;
950  }
951  }
952 
953  return NGX_CONF_OK;
954 }
955 
956 
957 static char *
958 ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
959 {
960  ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
961 
962  ngx_http_xslt_param_t *param;
964  ngx_str_t *value;
965 
966  value = cf->args->elts;
967 
968  if (xlcf->params == NULL) {
969  xlcf->params = ngx_array_create(cf->pool, 2,
970  sizeof(ngx_http_xslt_param_t));
971  if (xlcf->params == NULL) {
972  return NGX_CONF_ERROR;
973  }
974  }
975 
976  param = ngx_array_push(xlcf->params);
977  if (param == NULL) {
978  return NGX_CONF_ERROR;
979  }
980 
981  param->name = value[1].data;
982  param->quote = (cmd->post == NULL) ? 0 : 1;
983 
985 
986  ccv.cf = cf;
987  ccv.value = &value[2];
988  ccv.complex_value = &param->value;
989  ccv.zero = 1;
990 
991  if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
992  return NGX_CONF_ERROR;
993  }
994 
995  return NGX_CONF_OK;
996 }
997 
998 
999 static void
1000 ngx_http_xslt_cleanup_dtd(void *data)
1001 {
1002  xmlFreeDtd(data);
1003 }
1004 
1005 
1006 static void
1007 ngx_http_xslt_cleanup_stylesheet(void *data)
1008 {
1009  xsltFreeStylesheet(data);
1010 }
1011 
1012 
1013 static void *
1014 ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)
1015 {
1017 
1018  conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
1019  if (conf == NULL) {
1020  return NULL;
1021  }
1022 
1023  if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
1024  sizeof(ngx_http_xslt_file_t))
1025  != NGX_OK)
1026  {
1027  return NULL;
1028  }
1029 
1030  if (ngx_array_init(&conf->sheet_files, cf->pool, 1,
1031  sizeof(ngx_http_xslt_file_t))
1032  != NGX_OK)
1033  {
1034  return NULL;
1035  }
1036 
1037  return conf;
1038 }
1039 
1040 
1041 static void *
1042 ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
1043 {
1045 
1046  conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
1047  if (conf == NULL) {
1048  return NULL;
1049  }
1050 
1051  /*
1052  * set by ngx_pcalloc():
1053  *
1054  * conf->dtd = NULL;
1055  * conf->sheets = { NULL };
1056  * conf->types = { NULL };
1057  * conf->types_keys = NULL;
1058  * conf->params = NULL;
1059  */
1060 
1061  return conf;
1062 }
1063 
1064 
1065 static char *
1066 ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1067 {
1068  ngx_http_xslt_filter_loc_conf_t *prev = parent;
1069  ngx_http_xslt_filter_loc_conf_t *conf = child;
1070 
1071  if (conf->dtd == NULL) {
1072  conf->dtd = prev->dtd;
1073  }
1074 
1075  if (conf->sheets.nelts == 0) {
1076  conf->sheets = prev->sheets;
1077  }
1078 
1079  if (conf->params == NULL) {
1080  conf->params = prev->params;
1081  }
1082 
1083  if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
1084  &prev->types_keys, &prev->types,
1085  ngx_http_xslt_default_types)
1086  != NGX_OK)
1087  {
1088  return NGX_CONF_ERROR;
1089  }
1090 
1091  return NGX_CONF_OK;
1092 }
1093 
1094 
1095 static ngx_int_t
1096 ngx_http_xslt_filter_init(ngx_conf_t *cf)
1097 {
1098  xmlInitParser();
1099 
1100 #if (NGX_HAVE_EXSLT)
1101  exsltRegisterAll();
1102 #endif
1103 
1104  ngx_http_next_header_filter = ngx_http_top_header_filter;
1105  ngx_http_top_header_filter = ngx_http_xslt_header_filter;
1106 
1107  ngx_http_next_body_filter = ngx_http_top_body_filter;
1108  ngx_http_top_body_filter = ngx_http_xslt_body_filter;
1109 
1110  return NGX_OK;
1111 }
1112 
1113 
1114 static void
1115 ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
1116 {
1117  xsltCleanupGlobals();
1118  xmlCleanupParser();
1119 }