src/http/modules/ngx_http_xslt_filter_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>

  8. #include <libxml/parser.h>
  9. #include <libxml/tree.h>
  10. #include <libxslt/xslt.h>
  11. #include <libxslt/xsltInternals.h>
  12. #include <libxslt/transform.h>
  13. #include <libxslt/variables.h>
  14. #include <libxslt/xsltutils.h>

  15. #if (NGX_HAVE_EXSLT)
  16. #include <libexslt/exslt.h>
  17. #endif


  18. #ifndef NGX_HTTP_XSLT_REUSE_DTD
  19. #define NGX_HTTP_XSLT_REUSE_DTD  1
  20. #endif


  21. typedef struct {
  22.     u_char                    *name;
  23.     void                      *data;
  24. } ngx_http_xslt_file_t;


  25. typedef struct {
  26.     ngx_array_t                dtd_files;    /* ngx_http_xslt_file_t */
  27.     ngx_array_t                sheet_files;  /* ngx_http_xslt_file_t */
  28. } ngx_http_xslt_filter_main_conf_t;


  29. typedef struct {
  30.     u_char                    *name;
  31.     ngx_http_complex_value_t   value;
  32.     ngx_uint_t                 quote;        /* unsigned  quote:1; */
  33. } ngx_http_xslt_param_t;


  34. typedef struct {
  35.     xsltStylesheetPtr          stylesheet;
  36.     ngx_array_t                params;       /* ngx_http_xslt_param_t */
  37. } ngx_http_xslt_sheet_t;


  38. typedef struct {
  39.     xmlDtdPtr                  dtd;
  40.     ngx_array_t                sheets;       /* ngx_http_xslt_sheet_t */
  41.     ngx_hash_t                 types;
  42.     ngx_array_t               *types_keys;
  43.     ngx_array_t               *params;       /* ngx_http_xslt_param_t */
  44.     ngx_flag_t                 last_modified;
  45. } ngx_http_xslt_filter_loc_conf_t;


  46. typedef struct {
  47.     xmlDocPtr                  doc;
  48.     xmlParserCtxtPtr           ctxt;
  49.     xsltTransformContextPtr    transform;
  50.     ngx_http_request_t        *request;
  51.     ngx_array_t                params;

  52.     ngx_uint_t                 done;         /* unsigned  done:1; */
  53. } ngx_http_xslt_filter_ctx_t;


  54. static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
  55.     ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
  56. static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
  57.     ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);


  58. static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
  59.     const xmlChar *externalId, const xmlChar *systemId);
  60. static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);


  61. static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
  62.     ngx_http_xslt_filter_ctx_t *ctx);
  63. static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,
  64.     ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params, ngx_uint_t final);
  65. static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s);
  66. static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s);
  67. static void ngx_http_xslt_cleanup(void *data);

  68. static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,
  69.     void *conf);
  70. static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,
  71.     void *conf);
  72. static char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd,
  73.     void *conf);
  74. static void ngx_http_xslt_cleanup_dtd(void *data);
  75. static void ngx_http_xslt_cleanup_stylesheet(void *data);
  76. static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf);
  77. static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
  78. static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
  79.     void *child);
  80. static ngx_int_t ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf);
  81. static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
  82. static void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle);


  83. ngx_str_t  ngx_http_xslt_default_types[] = {
  84.     ngx_string("text/xml"),
  85.     ngx_null_string
  86. };


  87. static ngx_command_t  ngx_http_xslt_filter_commands[] = {

  88.     { ngx_string("xml_entities"),
  89.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  90.       ngx_http_xslt_entities,
  91.       NGX_HTTP_LOC_CONF_OFFSET,
  92.       0,
  93.       NULL },

  94.     { ngx_string("xslt_stylesheet"),
  95.       NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  96.       ngx_http_xslt_stylesheet,
  97.       NGX_HTTP_LOC_CONF_OFFSET,
  98.       0,
  99.       NULL },

  100.     { ngx_string("xslt_param"),
  101.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
  102.       ngx_http_xslt_param,
  103.       NGX_HTTP_LOC_CONF_OFFSET,
  104.       0,
  105.       NULL },

  106.     { ngx_string("xslt_string_param"),
  107.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
  108.       ngx_http_xslt_param,
  109.       NGX_HTTP_LOC_CONF_OFFSET,
  110.       0,
  111.       (void *) 1 },

  112.     { ngx_string("xslt_types"),
  113.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  114.       ngx_http_types_slot,
  115.       NGX_HTTP_LOC_CONF_OFFSET,
  116.       offsetof(ngx_http_xslt_filter_loc_conf_t, types_keys),
  117.       &ngx_http_xslt_default_types[0] },

  118.     { ngx_string("xslt_last_modified"),
  119.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  120.       ngx_conf_set_flag_slot,
  121.       NGX_HTTP_LOC_CONF_OFFSET,
  122.       offsetof(ngx_http_xslt_filter_loc_conf_t, last_modified),
  123.       NULL },

  124.       ngx_null_command
  125. };


  126. static ngx_http_module_t  ngx_http_xslt_filter_module_ctx = {
  127.     ngx_http_xslt_filter_preconfiguration, /* preconfiguration */
  128.     ngx_http_xslt_filter_init,             /* postconfiguration */

  129.     ngx_http_xslt_filter_create_main_conf, /* create main configuration */
  130.     NULL,                                  /* init main configuration */

  131.     NULL,                                  /* create server configuration */
  132.     NULL,                                  /* merge server configuration */

  133.     ngx_http_xslt_filter_create_conf,      /* create location configuration */
  134.     ngx_http_xslt_filter_merge_conf        /* merge location configuration */
  135. };


  136. ngx_module_t  ngx_http_xslt_filter_module = {
  137.     NGX_MODULE_V1,
  138.     &ngx_http_xslt_filter_module_ctx,      /* module context */
  139.     ngx_http_xslt_filter_commands,         /* module directives */
  140.     NGX_HTTP_MODULE,                       /* module type */
  141.     NULL,                                  /* init master */
  142.     NULL,                                  /* init module */
  143.     NULL,                                  /* init process */
  144.     NULL,                                  /* init thread */
  145.     NULL,                                  /* exit thread */
  146.     ngx_http_xslt_filter_exit,             /* exit process */
  147.     ngx_http_xslt_filter_exit,             /* exit master */
  148.     NGX_MODULE_V1_PADDING
  149. };


  150. static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
  151. static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;


  152. static ngx_int_t
  153. ngx_http_xslt_header_filter(ngx_http_request_t *r)
  154. {
  155.     ngx_http_xslt_filter_ctx_t       *ctx;
  156.     ngx_http_xslt_filter_loc_conf_t  *conf;

  157.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  158.                    "xslt filter header");

  159.     if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
  160.         return ngx_http_next_header_filter(r);
  161.     }

  162.     conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);

  163.     if (conf->sheets.nelts == 0
  164.         || ngx_http_test_content_type(r, &conf->types) == NULL)
  165.     {
  166.         return ngx_http_next_header_filter(r);
  167.     }

  168.     ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);

  169.     if (ctx) {
  170.         return ngx_http_next_header_filter(r);
  171.     }

  172.     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
  173.     if (ctx == NULL) {
  174.         return NGX_ERROR;
  175.     }

  176.     ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);

  177.     r->main_filter_need_in_memory = 1;

  178.     return NGX_OK;
  179. }


  180. static ngx_int_t
  181. ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
  182. {
  183.     int                          wellFormed;
  184.     ngx_chain_t                 *cl;
  185.     ngx_http_xslt_filter_ctx_t  *ctx;

  186.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  187.                    "xslt filter body");

  188.     if (in == NULL) {
  189.         return ngx_http_next_body_filter(r, in);
  190.     }

  191.     ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);

  192.     if (ctx == NULL || ctx->done) {
  193.         return ngx_http_next_body_filter(r, in);
  194.     }

  195.     for (cl = in; cl; cl = cl->next) {

  196.         if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {

  197.             if (ctx->ctxt->myDoc) {

  198. #if (NGX_HTTP_XSLT_REUSE_DTD)
  199.                 ctx->ctxt->myDoc->extSubset = NULL;
  200. #endif
  201.                 xmlFreeDoc(ctx->ctxt->myDoc);
  202.             }

  203.             xmlFreeParserCtxt(ctx->ctxt);

  204.             return ngx_http_xslt_send(r, ctx, NULL);
  205.         }

  206.         if (cl->buf->last_buf || cl->buf->last_in_chain) {

  207.             ctx->doc = ctx->ctxt->myDoc;

  208. #if (NGX_HTTP_XSLT_REUSE_DTD)
  209.             ctx->doc->extSubset = NULL;
  210. #endif

  211.             wellFormed = ctx->ctxt->wellFormed;

  212.             xmlFreeParserCtxt(ctx->ctxt);

  213.             if (wellFormed) {
  214.                 return ngx_http_xslt_send(r, ctx,
  215.                                        ngx_http_xslt_apply_stylesheet(r, ctx));
  216.             }

  217.             xmlFreeDoc(ctx->doc);

  218.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  219.                           "not well formed XML document");

  220.             return ngx_http_xslt_send(r, ctx, NULL);
  221.         }
  222.     }

  223.     return NGX_OK;
  224. }


  225. static ngx_int_t
  226. ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
  227.     ngx_buf_t *b)
  228. {
  229.     ngx_int_t                         rc;
  230.     ngx_chain_t                       out;
  231.     ngx_pool_cleanup_t               *cln;
  232.     ngx_http_xslt_filter_loc_conf_t  *conf;

  233.     ctx->done = 1;

  234.     if (b == NULL) {
  235.         return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
  236.                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
  237.     }

  238.     cln = ngx_pool_cleanup_add(r->pool, 0);

  239.     if (cln == NULL) {
  240.         ngx_free(b->pos);
  241.         return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
  242.                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
  243.     }

  244.     if (r == r->main) {
  245.         r->headers_out.content_length_n = b->last - b->pos;

  246.         if (r->headers_out.content_length) {
  247.             r->headers_out.content_length->hash = 0;
  248.             r->headers_out.content_length = NULL;
  249.         }

  250.         conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);

  251.         if (!conf->last_modified) {
  252.             ngx_http_clear_last_modified(r);
  253.             ngx_http_clear_etag(r);

  254.         } else {
  255.             ngx_http_weak_etag(r);
  256.         }
  257.     }

  258.     rc = ngx_http_next_header_filter(r);

  259.     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
  260.         ngx_free(b->pos);
  261.         return rc;
  262.     }

  263.     cln->handler = ngx_http_xslt_cleanup;
  264.     cln->data = b->pos;

  265.     out.buf = b;
  266.     out.next = NULL;

  267.     return ngx_http_next_body_filter(r, &out);
  268. }


  269. static ngx_int_t
  270. ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
  271.     ngx_buf_t *b)
  272. {
  273.     int               err;
  274.     xmlParserCtxtPtr  ctxt;

  275.     if (ctx->ctxt == NULL) {

  276.         ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
  277.         if (ctxt == NULL) {
  278.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  279.                           "xmlCreatePushParserCtxt() failed");
  280.             return NGX_ERROR;
  281.         }
  282.         xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD
  283.                                                |XML_PARSE_NOWARNING);

  284.         ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset;
  285.         ctxt->sax->setDocumentLocator = NULL;
  286.         ctxt->sax->error = ngx_http_xslt_sax_error;
  287.         ctxt->sax->fatalError = ngx_http_xslt_sax_error;
  288.         ctxt->sax->_private = ctx;

  289.         ctx->ctxt = ctxt;
  290.         ctx->request = r;
  291.     }

  292.     err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),
  293.                         (b->last_buf) || (b->last_in_chain));

  294.     if (err == 0) {
  295.         b->pos = b->last;
  296.         return NGX_OK;
  297.     }

  298.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  299.                   "xmlParseChunk() failed, error:%d", err);

  300.     return NGX_ERROR;
  301. }


  302. static void
  303. ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
  304.     const xmlChar *externalId, const xmlChar *systemId)
  305. {
  306.     xmlParserCtxtPtr ctxt = data;

  307.     xmlDocPtr                         doc;
  308.     xmlDtdPtr                         dtd;
  309.     ngx_http_request_t               *r;
  310.     ngx_http_xslt_filter_ctx_t       *ctx;
  311.     ngx_http_xslt_filter_loc_conf_t  *conf;

  312.     ctx = ctxt->sax->_private;
  313.     r = ctx->request;

  314.     conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);

  315.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  316.                    "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
  317.                    name ? name : (xmlChar *) "",
  318.                    externalId ? externalId : (xmlChar *) "",
  319.                    systemId ? systemId : (xmlChar *) "");

  320.     doc = ctxt->myDoc;

  321. #if (NGX_HTTP_XSLT_REUSE_DTD)

  322.     dtd = conf->dtd;

  323. #else

  324.     dtd = xmlCopyDtd(conf->dtd);
  325.     if (dtd == NULL) {
  326.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  327.                       "xmlCopyDtd() failed");
  328.         return;
  329.     }

  330.     if (doc->children == NULL) {
  331.         xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);

  332.     } else {
  333.         xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
  334.     }

  335. #endif

  336.     doc->extSubset = dtd;
  337. }


  338. static void ngx_cdecl
  339. ngx_http_xslt_sax_error(void *data, const char *msg, ...)
  340. {
  341.     xmlParserCtxtPtr ctxt = data;

  342.     size_t                       n;
  343.     va_list                      args;
  344.     ngx_http_xslt_filter_ctx_t  *ctx;
  345.     u_char                       buf[NGX_MAX_ERROR_STR];

  346.     ctx = ctxt->sax->_private;

  347.     buf[0] = '\0';

  348.     va_start(args, msg);
  349.     n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
  350.     va_end(args);

  351.     while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }

  352.     ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
  353.                   "libxml2 error: \"%*s\"", n + 1, buf);
  354. }


  355. static ngx_buf_t *
  356. ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
  357.     ngx_http_xslt_filter_ctx_t *ctx)
  358. {
  359.     int                               len, rc, doc_type;
  360.     u_char                           *type, *encoding;
  361.     ngx_buf_t                        *b;
  362.     ngx_uint_t                        i;
  363.     xmlChar                          *buf;
  364.     xmlDocPtr                         doc, res;
  365.     ngx_http_xslt_sheet_t            *sheet;
  366.     ngx_http_xslt_filter_loc_conf_t  *conf;

  367.     conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
  368.     sheet = conf->sheets.elts;
  369.     doc = ctx->doc;

  370.     /* preallocate array for 4 params */

  371.     if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
  372.         != NGX_OK)
  373.     {
  374.         xmlFreeDoc(doc);
  375.         return NULL;
  376.     }

  377.     for (i = 0; i < conf->sheets.nelts; i++) {

  378.         ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);
  379.         if (ctx->transform == NULL) {
  380.             xmlFreeDoc(doc);
  381.             return NULL;
  382.         }

  383.         if (conf->params
  384.             && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)
  385.         {
  386.             xsltFreeTransformContext(ctx->transform);
  387.             xmlFreeDoc(doc);
  388.             return NULL;
  389.         }

  390.         if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {
  391.             xsltFreeTransformContext(ctx->transform);
  392.             xmlFreeDoc(doc);
  393.             return NULL;
  394.         }

  395.         res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,
  396.                                       ctx->params.elts, NULL, NULL,
  397.                                       ctx->transform);

  398.         xsltFreeTransformContext(ctx->transform);
  399.         xmlFreeDoc(doc);

  400.         if (res == NULL) {
  401.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  402.                           "xsltApplyStylesheet() failed");
  403.             return NULL;
  404.         }

  405.         doc = res;

  406.         /* reset array elements */
  407.         ctx->params.nelts = 0;
  408.     }

  409.     /* there must be at least one stylesheet */

  410.     if (r == r->main) {
  411.         type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);

  412.     } else {
  413.         type = NULL;
  414.     }

  415.     encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);
  416.     doc_type = doc->type;

  417.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  418.                    "xslt filter type: %d t:%s e:%s",
  419.                    doc_type, type ? type : (u_char *) "(null)",
  420.                    encoding ? encoding : (u_char *) "(null)");

  421.     rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);

  422.     xmlFreeDoc(doc);

  423.     if (rc != 0) {
  424.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  425.                       "xsltSaveResultToString() failed");
  426.         return NULL;
  427.     }

  428.     if (len == 0) {
  429.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  430.                       "xsltSaveResultToString() returned zero-length result");
  431.         return NULL;
  432.     }

  433.     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
  434.     if (b == NULL) {
  435.         ngx_free(buf);
  436.         return NULL;
  437.     }

  438.     b->pos = buf;
  439.     b->last = buf + len;
  440.     b->memory = 1;

  441.     if (encoding) {
  442.         r->headers_out.charset.len = ngx_strlen(encoding);
  443.         r->headers_out.charset.data = encoding;
  444.     }

  445.     if (r != r->main) {
  446.         return b;
  447.     }

  448.     b->last_buf = 1;

  449.     if (type) {
  450.         len = ngx_strlen(type);

  451.         r->headers_out.content_type_len = len;
  452.         r->headers_out.content_type.len = len;
  453.         r->headers_out.content_type.data = type;

  454.     } else if (doc_type == XML_HTML_DOCUMENT_NODE) {

  455.         r->headers_out.content_type_len = sizeof("text/html") - 1;
  456.         ngx_str_set(&r->headers_out.content_type, "text/html");
  457.     }

  458.     r->headers_out.content_type_lowcase = NULL;

  459.     return b;
  460. }


  461. static ngx_int_t
  462. ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
  463.     ngx_array_t *params, ngx_uint_t final)
  464. {
  465.     u_char                 *p, *last, *value, *dst, *src, **s;
  466.     size_t                  len;
  467.     ngx_uint_t              i;
  468.     ngx_str_t               string;
  469.     ngx_http_xslt_param_t  *param;

  470.     param = params->elts;

  471.     for (i = 0; i < params->nelts; i++) {

  472.         if (ngx_http_complex_value(r, &param[i].value, &string) != NGX_OK) {
  473.             return NGX_ERROR;
  474.         }

  475.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  476.                        "xslt filter param: \"%s\"", string.data);

  477.         if (param[i].name) {

  478.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  479.                            "xslt filter param name: \"%s\"", param[i].name);

  480.             if (param[i].quote) {
  481.                 if (xsltQuoteOneUserParam(ctx->transform, param[i].name,
  482.                                           string.data)
  483.                     != 0)
  484.                 {
  485.                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  486.                                 "xsltQuoteOneUserParam(\"%s\", \"%s\") failed",
  487.                                 param[i].name, string.data);
  488.                     return NGX_ERROR;
  489.                 }

  490.                 continue;
  491.             }

  492.             s = ngx_array_push(&ctx->params);
  493.             if (s == NULL) {
  494.                 return NGX_ERROR;
  495.             }

  496.             *s = param[i].name;

  497.             s = ngx_array_push(&ctx->params);
  498.             if (s == NULL) {
  499.                 return NGX_ERROR;
  500.             }

  501.             *s = string.data;

  502.             continue;
  503.         }

  504.         /*
  505.          * parse param1=value1:param2=value2 syntax as used by parameters
  506.          * specified in xslt_stylesheet directives
  507.          */

  508.         p = string.data;
  509.         last = string.data + string.len;

  510.         while (p && *p) {

  511.             value = p;
  512.             p = (u_char *) ngx_strchr(p, '=');
  513.             if (p == NULL) {
  514.                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  515.                                 "invalid libxslt parameter \"%s\"", value);
  516.                 return NGX_ERROR;
  517.             }
  518.             *p++ = '\0';

  519.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  520.                            "xslt filter param name: \"%s\"", value);

  521.             s = ngx_array_push(&ctx->params);
  522.             if (s == NULL) {
  523.                 return NGX_ERROR;
  524.             }

  525.             *s = value;

  526.             value = p;
  527.             p = (u_char *) ngx_strchr(p, ':');

  528.             if (p) {
  529.                 len = p - value;
  530.                 *p++ = '\0';

  531.             } else {
  532.                 len = last - value;
  533.             }

  534.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  535.                            "xslt filter param value: \"%s\"", value);

  536.             dst = value;
  537.             src = value;

  538.             ngx_unescape_uri(&dst, &src, len, 0);

  539.             *dst = '\0';

  540.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  541.                            "xslt filter param unescaped: \"%s\"", value);

  542.             s = ngx_array_push(&ctx->params);
  543.             if (s == NULL) {
  544.                 return NGX_ERROR;
  545.             }

  546.             *s = value;
  547.         }
  548.     }

  549.     if (final) {
  550.         s = ngx_array_push(&ctx->params);
  551.         if (s == NULL) {
  552.             return NGX_ERROR;
  553.         }

  554.         *s = NULL;
  555.     }

  556.     return NGX_OK;
  557. }


  558. static u_char *
  559. ngx_http_xslt_content_type(xsltStylesheetPtr s)
  560. {
  561.     u_char  *type;

  562.     if (s->mediaType) {
  563.         return s->mediaType;
  564.     }

  565.     for (s = s->imports; s; s = s->next) {

  566.         type = ngx_http_xslt_content_type(s);

  567.         if (type) {
  568.             return type;
  569.         }
  570.     }

  571.     return NULL;
  572. }


  573. static u_char *
  574. ngx_http_xslt_encoding(xsltStylesheetPtr s)
  575. {
  576.     u_char  *encoding;

  577.     if (s->encoding) {
  578.         return s->encoding;
  579.     }

  580.     for (s = s->imports; s; s = s->next) {

  581.         encoding = ngx_http_xslt_encoding(s);

  582.         if (encoding) {
  583.             return encoding;
  584.         }
  585.     }

  586.     return NULL;
  587. }


  588. static void
  589. ngx_http_xslt_cleanup(void *data)
  590. {
  591.     ngx_free(data);
  592. }


  593. static char *
  594. ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  595. {
  596.     ngx_http_xslt_filter_loc_conf_t *xlcf = conf;

  597.     ngx_str_t                         *value;
  598.     ngx_uint_t                         i;
  599.     ngx_pool_cleanup_t                *cln;
  600.     ngx_http_xslt_file_t              *file;
  601.     ngx_http_xslt_filter_main_conf_t  *xmcf;

  602.     if (xlcf->dtd) {
  603.         return "is duplicate";
  604.     }

  605.     value = cf->args->elts;

  606.     xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);

  607.     file = xmcf->dtd_files.elts;
  608.     for (i = 0; i < xmcf->dtd_files.nelts; i++) {
  609.         if (ngx_strcmp(file[i].name, value[1].data) == 0) {
  610.             xlcf->dtd = file[i].data;
  611.             return NGX_CONF_OK;
  612.         }
  613.     }

  614.     cln = ngx_pool_cleanup_add(cf->pool, 0);
  615.     if (cln == NULL) {
  616.         return NGX_CONF_ERROR;
  617.     }

  618.     xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);

  619.     if (xlcf->dtd == NULL) {
  620.         ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
  621.         return NGX_CONF_ERROR;
  622.     }

  623.     cln->handler = ngx_http_xslt_cleanup_dtd;
  624.     cln->data = xlcf->dtd;

  625.     file = ngx_array_push(&xmcf->dtd_files);
  626.     if (file == NULL) {
  627.         return NGX_CONF_ERROR;
  628.     }

  629.     file->name = value[1].data;
  630.     file->data = xlcf->dtd;

  631.     return NGX_CONF_OK;
  632. }



  633. static char *
  634. ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  635. {
  636.     ngx_http_xslt_filter_loc_conf_t *xlcf = conf;

  637.     ngx_str_t                         *value;
  638.     ngx_uint_t                         i, n;
  639.     ngx_pool_cleanup_t                *cln;
  640.     ngx_http_xslt_file_t              *file;
  641.     ngx_http_xslt_sheet_t             *sheet;
  642.     ngx_http_xslt_param_t             *param;
  643.     ngx_http_compile_complex_value_t   ccv;
  644.     ngx_http_xslt_filter_main_conf_t  *xmcf;

  645.     value = cf->args->elts;

  646.     if (xlcf->sheets.elts == NULL) {
  647.         if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
  648.                            sizeof(ngx_http_xslt_sheet_t))
  649.             != NGX_OK)
  650.         {
  651.             return NGX_CONF_ERROR;
  652.         }
  653.     }

  654.     sheet = ngx_array_push(&xlcf->sheets);
  655.     if (sheet == NULL) {
  656.         return NGX_CONF_ERROR;
  657.     }

  658.     ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));

  659.     if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
  660.         return NGX_CONF_ERROR;
  661.     }

  662.     xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);

  663.     file = xmcf->sheet_files.elts;
  664.     for (i = 0; i < xmcf->sheet_files.nelts; i++) {
  665.         if (ngx_strcmp(file[i].name, value[1].data) == 0) {
  666.             sheet->stylesheet = file[i].data;
  667.             goto found;
  668.         }
  669.     }

  670.     cln = ngx_pool_cleanup_add(cf->pool, 0);
  671.     if (cln == NULL) {
  672.         return NGX_CONF_ERROR;
  673.     }

  674.     sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
  675.     if (sheet->stylesheet == NULL) {
  676.         ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
  677.                            "xsltParseStylesheetFile(\"%s\") failed",
  678.                            value[1].data);
  679.         return NGX_CONF_ERROR;
  680.     }

  681.     cln->handler = ngx_http_xslt_cleanup_stylesheet;
  682.     cln->data = sheet->stylesheet;

  683.     file = ngx_array_push(&xmcf->sheet_files);
  684.     if (file == NULL) {
  685.         return NGX_CONF_ERROR;
  686.     }

  687.     file->name = value[1].data;
  688.     file->data = sheet->stylesheet;

  689. found:

  690.     n = cf->args->nelts;

  691.     if (n == 2) {
  692.         return NGX_CONF_OK;
  693.     }

  694.     if (ngx_array_init(&sheet->params, cf->pool, n - 2,
  695.                        sizeof(ngx_http_xslt_param_t))
  696.         != NGX_OK)
  697.     {
  698.         return NGX_CONF_ERROR;
  699.     }

  700.     for (i = 2; i < n; i++) {

  701.         param = ngx_array_push(&sheet->params);
  702.         if (param == NULL) {
  703.             return NGX_CONF_ERROR;
  704.         }

  705.         ngx_memzero(param, sizeof(ngx_http_xslt_param_t));
  706.         ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  707.         ccv.cf = cf;
  708.         ccv.value = &value[i];
  709.         ccv.complex_value = &param->value;
  710.         ccv.zero = 1;

  711.         if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  712.             return NGX_CONF_ERROR;
  713.         }
  714.     }

  715.     return NGX_CONF_OK;
  716. }


  717. static char *
  718. ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  719. {
  720.     ngx_http_xslt_filter_loc_conf_t  *xlcf = conf;

  721.     ngx_http_xslt_param_t            *param;
  722.     ngx_http_compile_complex_value_t  ccv;
  723.     ngx_str_t                        *value;

  724.     value = cf->args->elts;

  725.     if (xlcf->params == NULL) {
  726.         xlcf->params = ngx_array_create(cf->pool, 2,
  727.                                         sizeof(ngx_http_xslt_param_t));
  728.         if (xlcf->params == NULL) {
  729.             return NGX_CONF_ERROR;
  730.         }
  731.     }

  732.     param = ngx_array_push(xlcf->params);
  733.     if (param == NULL) {
  734.         return NGX_CONF_ERROR;
  735.     }

  736.     param->name = value[1].data;
  737.     param->quote = (cmd->post == NULL) ? 0 : 1;

  738.     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  739.     ccv.cf = cf;
  740.     ccv.value = &value[2];
  741.     ccv.complex_value = &param->value;
  742.     ccv.zero = 1;

  743.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  744.         return NGX_CONF_ERROR;
  745.     }

  746.     return NGX_CONF_OK;
  747. }


  748. static void
  749. ngx_http_xslt_cleanup_dtd(void *data)
  750. {
  751.     xmlFreeDtd(data);
  752. }


  753. static void
  754. ngx_http_xslt_cleanup_stylesheet(void *data)
  755. {
  756.     xsltFreeStylesheet(data);
  757. }


  758. static void *
  759. ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)
  760. {
  761.     ngx_http_xslt_filter_main_conf_t  *conf;

  762.     conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
  763.     if (conf == NULL) {
  764.         return NULL;
  765.     }

  766.     if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
  767.                        sizeof(ngx_http_xslt_file_t))
  768.         != NGX_OK)
  769.     {
  770.         return NULL;
  771.     }

  772.     if (ngx_array_init(&conf->sheet_files, cf->pool, 1,
  773.                        sizeof(ngx_http_xslt_file_t))
  774.         != NGX_OK)
  775.     {
  776.         return NULL;
  777.     }

  778.     return conf;
  779. }


  780. static void *
  781. ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
  782. {
  783.     ngx_http_xslt_filter_loc_conf_t  *conf;

  784.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
  785.     if (conf == NULL) {
  786.         return NULL;
  787.     }

  788.     /*
  789.      * set by ngx_pcalloc():
  790.      *
  791.      *     conf->dtd = NULL;
  792.      *     conf->sheets = { NULL };
  793.      *     conf->types = { NULL };
  794.      *     conf->types_keys = NULL;
  795.      *     conf->params = NULL;
  796.      */

  797.     conf->last_modified = NGX_CONF_UNSET;

  798.     return conf;
  799. }


  800. static char *
  801. ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  802. {
  803.     ngx_http_xslt_filter_loc_conf_t *prev = parent;
  804.     ngx_http_xslt_filter_loc_conf_t *conf = child;

  805.     if (conf->dtd == NULL) {
  806.         conf->dtd = prev->dtd;
  807.     }

  808.     if (conf->sheets.nelts == 0) {
  809.         conf->sheets = prev->sheets;
  810.     }

  811.     if (conf->params == NULL) {
  812.         conf->params = prev->params;
  813.     }

  814.     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
  815.                              &prev->types_keys, &prev->types,
  816.                              ngx_http_xslt_default_types)
  817.         != NGX_OK)
  818.     {
  819.         return NGX_CONF_ERROR;
  820.     }

  821.     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);

  822.     return NGX_CONF_OK;
  823. }


  824. static ngx_int_t
  825. ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf)
  826. {
  827.     xmlInitParser();

  828. #if (NGX_HAVE_EXSLT)
  829.     exsltRegisterAll();
  830. #endif

  831.     return NGX_OK;
  832. }


  833. static ngx_int_t
  834. ngx_http_xslt_filter_init(ngx_conf_t *cf)
  835. {
  836.     ngx_http_next_header_filter = ngx_http_top_header_filter;
  837.     ngx_http_top_header_filter = ngx_http_xslt_header_filter;

  838.     ngx_http_next_body_filter = ngx_http_top_body_filter;
  839.     ngx_http_top_body_filter = ngx_http_xslt_body_filter;

  840.     return NGX_OK;
  841. }


  842. static void
  843. ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
  844. {
  845.     xsltCleanupGlobals();
  846.     xmlCleanupParser();
  847. }