Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 #include <libxml/parser.h>
0013 #include <libxml/tree.h>
0014 #include <libxslt/xslt.h>
0015 #include <libxslt/xsltInternals.h>
0016 #include <libxslt/transform.h>
0017 #include <libxslt/variables.h>
0018 #include <libxslt/xsltutils.h>
0019 
0020 #if (NGX_HAVE_EXSLT)
0021 #include <libexslt/exslt.h>
0022 #endif
0023 
0024 
0025 #ifndef NGX_HTTP_XSLT_REUSE_DTD
0026 #define NGX_HTTP_XSLT_REUSE_DTD  1
0027 #endif
0028 
0029 
0030 typedef struct {
0031     u_char                    *name;
0032     void                      *data;
0033 } ngx_http_xslt_file_t;
0034 
0035 
0036 typedef struct {
0037     ngx_array_t                dtd_files;    /* ngx_http_xslt_file_t */
0038     ngx_array_t                sheet_files;  /* ngx_http_xslt_file_t */
0039 } ngx_http_xslt_filter_main_conf_t;
0040 
0041 
0042 typedef struct {
0043     u_char                    *name;
0044     ngx_http_complex_value_t   value;
0045     ngx_uint_t                 quote;        /* unsigned  quote:1; */
0046 } ngx_http_xslt_param_t;
0047 
0048 
0049 typedef struct {
0050     xsltStylesheetPtr          stylesheet;
0051     ngx_array_t                params;       /* ngx_http_xslt_param_t */
0052 } ngx_http_xslt_sheet_t;
0053 
0054 
0055 typedef struct {
0056     xmlDtdPtr                  dtd;
0057     ngx_array_t                sheets;       /* ngx_http_xslt_sheet_t */
0058     ngx_hash_t                 types;
0059     ngx_array_t               *types_keys;
0060     ngx_array_t               *params;       /* ngx_http_xslt_param_t */
0061     ngx_flag_t                 last_modified;
0062 } ngx_http_xslt_filter_loc_conf_t;
0063 
0064 
0065 typedef struct {
0066     xmlDocPtr                  doc;
0067     xmlParserCtxtPtr           ctxt;
0068     xsltTransformContextPtr    transform;
0069     ngx_http_request_t        *request;
0070     ngx_array_t                params;
0071 
0072     ngx_uint_t                 done;         /* unsigned  done:1; */
0073 } ngx_http_xslt_filter_ctx_t;
0074 
0075 
0076 static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
0077     ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
0078 static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
0079     ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
0080 
0081 
0082 static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
0083     const xmlChar *externalId, const xmlChar *systemId);
0084 static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);
0085 
0086 
0087 static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
0088     ngx_http_xslt_filter_ctx_t *ctx);
0089 static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,
0090     ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params, ngx_uint_t final);
0091 static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s);
0092 static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s);
0093 static void ngx_http_xslt_cleanup(void *data);
0094 
0095 static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,
0096     void *conf);
0097 static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,
0098     void *conf);
0099 static char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd,
0100     void *conf);
0101 static void ngx_http_xslt_cleanup_dtd(void *data);
0102 static void ngx_http_xslt_cleanup_stylesheet(void *data);
0103 static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf);
0104 static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
0105 static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
0106     void *child);
0107 static ngx_int_t ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf);
0108 static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
0109 static void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle);
0110 
0111 
0112 static ngx_str_t  ngx_http_xslt_default_types[] = {
0113     ngx_string("text/xml"),
0114     ngx_null_string
0115 };
0116 
0117 
0118 static ngx_command_t  ngx_http_xslt_filter_commands[] = {
0119 
0120     { ngx_string("xml_entities"),
0121       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0122       ngx_http_xslt_entities,
0123       NGX_HTTP_LOC_CONF_OFFSET,
0124       0,
0125       NULL },
0126 
0127     { ngx_string("xslt_stylesheet"),
0128       NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0129       ngx_http_xslt_stylesheet,
0130       NGX_HTTP_LOC_CONF_OFFSET,
0131       0,
0132       NULL },
0133 
0134     { ngx_string("xslt_param"),
0135       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
0136       ngx_http_xslt_param,
0137       NGX_HTTP_LOC_CONF_OFFSET,
0138       0,
0139       NULL },
0140 
0141     { ngx_string("xslt_string_param"),
0142       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
0143       ngx_http_xslt_param,
0144       NGX_HTTP_LOC_CONF_OFFSET,
0145       0,
0146       (void *) 1 },
0147 
0148     { ngx_string("xslt_types"),
0149       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0150       ngx_http_types_slot,
0151       NGX_HTTP_LOC_CONF_OFFSET,
0152       offsetof(ngx_http_xslt_filter_loc_conf_t, types_keys),
0153       &ngx_http_xslt_default_types[0] },
0154 
0155     { ngx_string("xslt_last_modified"),
0156       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0157       ngx_conf_set_flag_slot,
0158       NGX_HTTP_LOC_CONF_OFFSET,
0159       offsetof(ngx_http_xslt_filter_loc_conf_t, last_modified),
0160       NULL },
0161 
0162       ngx_null_command
0163 };
0164 
0165 
0166 static ngx_http_module_t  ngx_http_xslt_filter_module_ctx = {
0167     ngx_http_xslt_filter_preconfiguration, /* preconfiguration */
0168     ngx_http_xslt_filter_init,             /* postconfiguration */
0169 
0170     ngx_http_xslt_filter_create_main_conf, /* create main configuration */
0171     NULL,                                  /* init main configuration */
0172 
0173     NULL,                                  /* create server configuration */
0174     NULL,                                  /* merge server configuration */
0175 
0176     ngx_http_xslt_filter_create_conf,      /* create location configuration */
0177     ngx_http_xslt_filter_merge_conf        /* merge location configuration */
0178 };
0179 
0180 
0181 ngx_module_t  ngx_http_xslt_filter_module = {
0182     NGX_MODULE_V1,
0183     &ngx_http_xslt_filter_module_ctx,      /* module context */
0184     ngx_http_xslt_filter_commands,         /* module directives */
0185     NGX_HTTP_MODULE,                       /* module type */
0186     NULL,                                  /* init master */
0187     NULL,                                  /* init module */
0188     NULL,                                  /* init process */
0189     NULL,                                  /* init thread */
0190     NULL,                                  /* exit thread */
0191     ngx_http_xslt_filter_exit,             /* exit process */
0192     ngx_http_xslt_filter_exit,             /* exit master */
0193     NGX_MODULE_V1_PADDING
0194 };
0195 
0196 
0197 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0198 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
0199 
0200 
0201 static ngx_int_t
0202 ngx_http_xslt_header_filter(ngx_http_request_t *r)
0203 {
0204     ngx_http_xslt_filter_ctx_t       *ctx;
0205     ngx_http_xslt_filter_loc_conf_t  *conf;
0206 
0207     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0208                    "xslt filter header");
0209 
0210     if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
0211         return ngx_http_next_header_filter(r);
0212     }
0213 
0214     conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
0215 
0216     if (conf->sheets.nelts == 0
0217         || ngx_http_test_content_type(r, &conf->types) == NULL)
0218     {
0219         return ngx_http_next_header_filter(r);
0220     }
0221 
0222     ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
0223 
0224     if (ctx) {
0225         return ngx_http_next_header_filter(r);
0226     }
0227 
0228     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
0229     if (ctx == NULL) {
0230         return NGX_ERROR;
0231     }
0232 
0233     ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
0234 
0235     r->main_filter_need_in_memory = 1;
0236 
0237     return NGX_OK;
0238 }
0239 
0240 
0241 static ngx_int_t
0242 ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0243 {
0244     int                          wellFormed;
0245     ngx_chain_t                 *cl;
0246     ngx_http_xslt_filter_ctx_t  *ctx;
0247 
0248     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0249                    "xslt filter body");
0250 
0251     if (in == NULL) {
0252         return ngx_http_next_body_filter(r, in);
0253     }
0254 
0255     ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
0256 
0257     if (ctx == NULL || ctx->done) {
0258         return ngx_http_next_body_filter(r, in);
0259     }
0260 
0261     for (cl = in; cl; cl = cl->next) {
0262 
0263         if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
0264 
0265             if (ctx->ctxt->myDoc) {
0266 
0267 #if (NGX_HTTP_XSLT_REUSE_DTD)
0268                 ctx->ctxt->myDoc->extSubset = NULL;
0269 #endif
0270                 xmlFreeDoc(ctx->ctxt->myDoc);
0271             }
0272 
0273             xmlFreeParserCtxt(ctx->ctxt);
0274 
0275             return ngx_http_xslt_send(r, ctx, NULL);
0276         }
0277 
0278         if (cl->buf->last_buf || cl->buf->last_in_chain) {
0279 
0280             ctx->doc = ctx->ctxt->myDoc;
0281 
0282 #if (NGX_HTTP_XSLT_REUSE_DTD)
0283             ctx->doc->extSubset = NULL;
0284 #endif
0285 
0286             wellFormed = ctx->ctxt->wellFormed;
0287 
0288             xmlFreeParserCtxt(ctx->ctxt);
0289 
0290             if (wellFormed) {
0291                 return ngx_http_xslt_send(r, ctx,
0292                                        ngx_http_xslt_apply_stylesheet(r, ctx));
0293             }
0294 
0295             xmlFreeDoc(ctx->doc);
0296 
0297             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0298                           "not well formed XML document");
0299 
0300             return ngx_http_xslt_send(r, ctx, NULL);
0301         }
0302     }
0303 
0304     return NGX_OK;
0305 }
0306 
0307 
0308 static ngx_int_t
0309 ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
0310     ngx_buf_t *b)
0311 {
0312     ngx_int_t                         rc;
0313     ngx_chain_t                       out;
0314     ngx_pool_cleanup_t               *cln;
0315     ngx_http_xslt_filter_loc_conf_t  *conf;
0316 
0317     ctx->done = 1;
0318 
0319     if (b == NULL) {
0320         return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
0321                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
0322     }
0323 
0324     cln = ngx_pool_cleanup_add(r->pool, 0);
0325 
0326     if (cln == NULL) {
0327         ngx_free(b->pos);
0328         return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
0329                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
0330     }
0331 
0332     if (r == r->main) {
0333         r->headers_out.content_length_n = b->last - b->pos;
0334 
0335         if (r->headers_out.content_length) {
0336             r->headers_out.content_length->hash = 0;
0337             r->headers_out.content_length = NULL;
0338         }
0339 
0340         conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
0341 
0342         if (!conf->last_modified) {
0343             ngx_http_clear_last_modified(r);
0344             ngx_http_clear_etag(r);
0345 
0346         } else {
0347             ngx_http_weak_etag(r);
0348         }
0349     }
0350 
0351     rc = ngx_http_next_header_filter(r);
0352 
0353     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
0354         ngx_free(b->pos);
0355         return rc;
0356     }
0357 
0358     cln->handler = ngx_http_xslt_cleanup;
0359     cln->data = b->pos;
0360 
0361     out.buf = b;
0362     out.next = NULL;
0363 
0364     return ngx_http_next_body_filter(r, &out);
0365 }
0366 
0367 
0368 static ngx_int_t
0369 ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
0370     ngx_buf_t *b)
0371 {
0372     int               err;
0373     xmlParserCtxtPtr  ctxt;
0374 
0375     if (ctx->ctxt == NULL) {
0376 
0377         ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
0378         if (ctxt == NULL) {
0379             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0380                           "xmlCreatePushParserCtxt() failed");
0381             return NGX_ERROR;
0382         }
0383         xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD
0384                                                |XML_PARSE_NOWARNING);
0385 
0386         ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset;
0387         ctxt->sax->setDocumentLocator = NULL;
0388         ctxt->sax->error = ngx_http_xslt_sax_error;
0389         ctxt->sax->fatalError = ngx_http_xslt_sax_error;
0390         ctxt->sax->_private = ctx;
0391 
0392         ctx->ctxt = ctxt;
0393         ctx->request = r;
0394     }
0395 
0396     err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),
0397                         (b->last_buf) || (b->last_in_chain));
0398 
0399     if (err == 0) {
0400         b->pos = b->last;
0401         return NGX_OK;
0402     }
0403 
0404     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0405                   "xmlParseChunk() failed, error:%d", err);
0406 
0407     return NGX_ERROR;
0408 }
0409 
0410 
0411 static void
0412 ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
0413     const xmlChar *externalId, const xmlChar *systemId)
0414 {
0415     xmlParserCtxtPtr ctxt = data;
0416 
0417     xmlDocPtr                         doc;
0418     xmlDtdPtr                         dtd;
0419     ngx_http_request_t               *r;
0420     ngx_http_xslt_filter_ctx_t       *ctx;
0421     ngx_http_xslt_filter_loc_conf_t  *conf;
0422 
0423     ctx = ctxt->sax->_private;
0424     r = ctx->request;
0425 
0426     conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
0427 
0428     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0429                    "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
0430                    name ? name : (xmlChar *) "",
0431                    externalId ? externalId : (xmlChar *) "",
0432                    systemId ? systemId : (xmlChar *) "");
0433 
0434     doc = ctxt->myDoc;
0435 
0436 #if (NGX_HTTP_XSLT_REUSE_DTD)
0437 
0438     dtd = conf->dtd;
0439 
0440 #else
0441 
0442     dtd = xmlCopyDtd(conf->dtd);
0443     if (dtd == NULL) {
0444         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0445                       "xmlCopyDtd() failed");
0446         return;
0447     }
0448 
0449     if (doc->children == NULL) {
0450         xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
0451 
0452     } else {
0453         xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
0454     }
0455 
0456 #endif
0457 
0458     doc->extSubset = dtd;
0459 }
0460 
0461 
0462 static void ngx_cdecl
0463 ngx_http_xslt_sax_error(void *data, const char *msg, ...)
0464 {
0465     xmlParserCtxtPtr ctxt = data;
0466 
0467     size_t                       n;
0468     va_list                      args;
0469     ngx_http_xslt_filter_ctx_t  *ctx;
0470     u_char                       buf[NGX_MAX_ERROR_STR];
0471 
0472     ctx = ctxt->sax->_private;
0473 
0474     buf[0] = '\0';
0475 
0476     va_start(args, msg);
0477     n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
0478     va_end(args);
0479 
0480     while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
0481 
0482     ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
0483                   "libxml2 error: \"%*s\"", n + 1, buf);
0484 }
0485 
0486 
0487 static ngx_buf_t *
0488 ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
0489     ngx_http_xslt_filter_ctx_t *ctx)
0490 {
0491     int                               len, rc, doc_type;
0492     u_char                           *type, *encoding;
0493     ngx_buf_t                        *b;
0494     ngx_uint_t                        i;
0495     xmlChar                          *buf;
0496     xmlDocPtr                         doc, res;
0497     ngx_http_xslt_sheet_t            *sheet;
0498     ngx_http_xslt_filter_loc_conf_t  *conf;
0499 
0500     conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
0501     sheet = conf->sheets.elts;
0502     doc = ctx->doc;
0503 
0504     /* preallocate array for 4 params */
0505 
0506     if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
0507         != NGX_OK)
0508     {
0509         xmlFreeDoc(doc);
0510         return NULL;
0511     }
0512 
0513     for (i = 0; i < conf->sheets.nelts; i++) {
0514 
0515         ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);
0516         if (ctx->transform == NULL) {
0517             xmlFreeDoc(doc);
0518             return NULL;
0519         }
0520 
0521         if (conf->params
0522             && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)
0523         {
0524             xsltFreeTransformContext(ctx->transform);
0525             xmlFreeDoc(doc);
0526             return NULL;
0527         }
0528 
0529         if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {
0530             xsltFreeTransformContext(ctx->transform);
0531             xmlFreeDoc(doc);
0532             return NULL;
0533         }
0534 
0535         res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,
0536                                       ctx->params.elts, NULL, NULL,
0537                                       ctx->transform);
0538 
0539         xsltFreeTransformContext(ctx->transform);
0540         xmlFreeDoc(doc);
0541 
0542         if (res == NULL) {
0543             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0544                           "xsltApplyStylesheet() failed");
0545             return NULL;
0546         }
0547 
0548         doc = res;
0549 
0550         /* reset array elements */
0551         ctx->params.nelts = 0;
0552     }
0553 
0554     /* there must be at least one stylesheet */
0555 
0556     if (r == r->main) {
0557         type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);
0558 
0559     } else {
0560         type = NULL;
0561     }
0562 
0563     encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);
0564     doc_type = doc->type;
0565 
0566     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0567                    "xslt filter type: %d t:%s e:%s",
0568                    doc_type, type ? type : (u_char *) "(null)",
0569                    encoding ? encoding : (u_char *) "(null)");
0570 
0571     rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
0572 
0573     xmlFreeDoc(doc);
0574 
0575     if (rc != 0) {
0576         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0577                       "xsltSaveResultToString() failed");
0578         return NULL;
0579     }
0580 
0581     if (len == 0) {
0582         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0583                       "xsltSaveResultToString() returned zero-length result");
0584         return NULL;
0585     }
0586 
0587     b = ngx_calloc_buf(r->pool);
0588     if (b == NULL) {
0589         ngx_free(buf);
0590         return NULL;
0591     }
0592 
0593     b->pos = buf;
0594     b->last = buf + len;
0595     b->memory = 1;
0596 
0597     if (encoding) {
0598         r->headers_out.charset.len = ngx_strlen(encoding);
0599         r->headers_out.charset.data = encoding;
0600     }
0601 
0602     if (r != r->main) {
0603         return b;
0604     }
0605 
0606     b->last_buf = 1;
0607 
0608     if (type) {
0609         len = ngx_strlen(type);
0610 
0611         r->headers_out.content_type_len = len;
0612         r->headers_out.content_type.len = len;
0613         r->headers_out.content_type.data = type;
0614 
0615     } else if (doc_type == XML_HTML_DOCUMENT_NODE) {
0616 
0617         r->headers_out.content_type_len = sizeof("text/html") - 1;
0618         ngx_str_set(&r->headers_out.content_type, "text/html");
0619     }
0620 
0621     r->headers_out.content_type_lowcase = NULL;
0622 
0623     return b;
0624 }
0625 
0626 
0627 static ngx_int_t
0628 ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
0629     ngx_array_t *params, ngx_uint_t final)
0630 {
0631     u_char                 *p, *last, *value, *dst, *src, **s;
0632     size_t                  len;
0633     ngx_uint_t              i;
0634     ngx_str_t               string;
0635     ngx_http_xslt_param_t  *param;
0636 
0637     param = params->elts;
0638 
0639     for (i = 0; i < params->nelts; i++) {
0640 
0641         if (ngx_http_complex_value(r, &param[i].value, &string) != NGX_OK) {
0642             return NGX_ERROR;
0643         }
0644 
0645         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0646                        "xslt filter param: \"%s\"", string.data);
0647 
0648         if (param[i].name) {
0649 
0650             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0651                            "xslt filter param name: \"%s\"", param[i].name);
0652 
0653             if (param[i].quote) {
0654                 if (xsltQuoteOneUserParam(ctx->transform, param[i].name,
0655                                           string.data)
0656                     != 0)
0657                 {
0658                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0659                                 "xsltQuoteOneUserParam(\"%s\", \"%s\") failed",
0660                                 param[i].name, string.data);
0661                     return NGX_ERROR;
0662                 }
0663 
0664                 continue;
0665             }
0666 
0667             s = ngx_array_push(&ctx->params);
0668             if (s == NULL) {
0669                 return NGX_ERROR;
0670             }
0671 
0672             *s = param[i].name;
0673 
0674             s = ngx_array_push(&ctx->params);
0675             if (s == NULL) {
0676                 return NGX_ERROR;
0677             }
0678 
0679             *s = string.data;
0680 
0681             continue;
0682         }
0683 
0684         /*
0685          * parse param1=value1:param2=value2 syntax as used by parameters
0686          * specified in xslt_stylesheet directives
0687          */
0688 
0689         if (param[i].value.lengths) {
0690             p = string.data;
0691 
0692         } else {
0693             p = ngx_pnalloc(r->pool, string.len + 1);
0694             if (p == NULL) {
0695                 return NGX_ERROR;
0696             }
0697 
0698             ngx_memcpy(p, string.data, string.len + 1);
0699         }
0700 
0701         last = p + string.len;
0702 
0703         while (p && *p) {
0704 
0705             value = p;
0706             p = (u_char *) ngx_strchr(p, '=');
0707             if (p == NULL) {
0708                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0709                                 "invalid libxslt parameter \"%s\"", value);
0710                 return NGX_ERROR;
0711             }
0712             *p++ = '\0';
0713 
0714             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0715                            "xslt filter param name: \"%s\"", value);
0716 
0717             s = ngx_array_push(&ctx->params);
0718             if (s == NULL) {
0719                 return NGX_ERROR;
0720             }
0721 
0722             *s = value;
0723 
0724             value = p;
0725             p = (u_char *) ngx_strchr(p, ':');
0726 
0727             if (p) {
0728                 len = p - value;
0729                 *p++ = '\0';
0730 
0731             } else {
0732                 len = last - value;
0733             }
0734 
0735             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0736                            "xslt filter param value: \"%s\"", value);
0737 
0738             dst = value;
0739             src = value;
0740 
0741             ngx_unescape_uri(&dst, &src, len, 0);
0742 
0743             *dst = '\0';
0744 
0745             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0746                            "xslt filter param unescaped: \"%s\"", value);
0747 
0748             s = ngx_array_push(&ctx->params);
0749             if (s == NULL) {
0750                 return NGX_ERROR;
0751             }
0752 
0753             *s = value;
0754         }
0755     }
0756 
0757     if (final) {
0758         s = ngx_array_push(&ctx->params);
0759         if (s == NULL) {
0760             return NGX_ERROR;
0761         }
0762 
0763         *s = NULL;
0764     }
0765 
0766     return NGX_OK;
0767 }
0768 
0769 
0770 static u_char *
0771 ngx_http_xslt_content_type(xsltStylesheetPtr s)
0772 {
0773     u_char  *type;
0774 
0775     if (s->mediaType) {
0776         return s->mediaType;
0777     }
0778 
0779     for (s = s->imports; s; s = s->next) {
0780 
0781         type = ngx_http_xslt_content_type(s);
0782 
0783         if (type) {
0784             return type;
0785         }
0786     }
0787 
0788     return NULL;
0789 }
0790 
0791 
0792 static u_char *
0793 ngx_http_xslt_encoding(xsltStylesheetPtr s)
0794 {
0795     u_char  *encoding;
0796 
0797     if (s->encoding) {
0798         return s->encoding;
0799     }
0800 
0801     for (s = s->imports; s; s = s->next) {
0802 
0803         encoding = ngx_http_xslt_encoding(s);
0804 
0805         if (encoding) {
0806             return encoding;
0807         }
0808     }
0809 
0810     return NULL;
0811 }
0812 
0813 
0814 static void
0815 ngx_http_xslt_cleanup(void *data)
0816 {
0817     ngx_free(data);
0818 }
0819 
0820 
0821 static char *
0822 ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0823 {
0824     ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
0825 
0826     ngx_str_t                         *value;
0827     ngx_uint_t                         i;
0828     ngx_pool_cleanup_t                *cln;
0829     ngx_http_xslt_file_t              *file;
0830     ngx_http_xslt_filter_main_conf_t  *xmcf;
0831 
0832     if (xlcf->dtd) {
0833         return "is duplicate";
0834     }
0835 
0836     value = cf->args->elts;
0837 
0838     xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
0839 
0840     file = xmcf->dtd_files.elts;
0841     for (i = 0; i < xmcf->dtd_files.nelts; i++) {
0842         if (ngx_strcmp(file[i].name, value[1].data) == 0) {
0843             xlcf->dtd = file[i].data;
0844             return NGX_CONF_OK;
0845         }
0846     }
0847 
0848     cln = ngx_pool_cleanup_add(cf->pool, 0);
0849     if (cln == NULL) {
0850         return NGX_CONF_ERROR;
0851     }
0852 
0853     xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
0854 
0855     if (xlcf->dtd == NULL) {
0856         ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
0857         return NGX_CONF_ERROR;
0858     }
0859 
0860     cln->handler = ngx_http_xslt_cleanup_dtd;
0861     cln->data = xlcf->dtd;
0862 
0863     file = ngx_array_push(&xmcf->dtd_files);
0864     if (file == NULL) {
0865         return NGX_CONF_ERROR;
0866     }
0867 
0868     file->name = value[1].data;
0869     file->data = xlcf->dtd;
0870 
0871     return NGX_CONF_OK;
0872 }
0873 
0874 
0875 
0876 static char *
0877 ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0878 {
0879     ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
0880 
0881     ngx_str_t                         *value;
0882     ngx_uint_t                         i, n;
0883     ngx_pool_cleanup_t                *cln;
0884     ngx_http_xslt_file_t              *file;
0885     ngx_http_xslt_sheet_t             *sheet;
0886     ngx_http_xslt_param_t             *param;
0887     ngx_http_compile_complex_value_t   ccv;
0888     ngx_http_xslt_filter_main_conf_t  *xmcf;
0889 
0890     value = cf->args->elts;
0891 
0892     if (xlcf->sheets.elts == NULL) {
0893         if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
0894                            sizeof(ngx_http_xslt_sheet_t))
0895             != NGX_OK)
0896         {
0897             return NGX_CONF_ERROR;
0898         }
0899     }
0900 
0901     sheet = ngx_array_push(&xlcf->sheets);
0902     if (sheet == NULL) {
0903         return NGX_CONF_ERROR;
0904     }
0905 
0906     ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
0907 
0908     if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
0909         return NGX_CONF_ERROR;
0910     }
0911 
0912     xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
0913 
0914     file = xmcf->sheet_files.elts;
0915     for (i = 0; i < xmcf->sheet_files.nelts; i++) {
0916         if (ngx_strcmp(file[i].name, value[1].data) == 0) {
0917             sheet->stylesheet = file[i].data;
0918             goto found;
0919         }
0920     }
0921 
0922     cln = ngx_pool_cleanup_add(cf->pool, 0);
0923     if (cln == NULL) {
0924         return NGX_CONF_ERROR;
0925     }
0926 
0927     sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
0928     if (sheet->stylesheet == NULL) {
0929         ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
0930                            "xsltParseStylesheetFile(\"%s\") failed",
0931                            value[1].data);
0932         return NGX_CONF_ERROR;
0933     }
0934 
0935     cln->handler = ngx_http_xslt_cleanup_stylesheet;
0936     cln->data = sheet->stylesheet;
0937 
0938     file = ngx_array_push(&xmcf->sheet_files);
0939     if (file == NULL) {
0940         return NGX_CONF_ERROR;
0941     }
0942 
0943     file->name = value[1].data;
0944     file->data = sheet->stylesheet;
0945 
0946 found:
0947 
0948     n = cf->args->nelts;
0949 
0950     if (n == 2) {
0951         return NGX_CONF_OK;
0952     }
0953 
0954     if (ngx_array_init(&sheet->params, cf->pool, n - 2,
0955                        sizeof(ngx_http_xslt_param_t))
0956         != NGX_OK)
0957     {
0958         return NGX_CONF_ERROR;
0959     }
0960 
0961     for (i = 2; i < n; i++) {
0962 
0963         param = ngx_array_push(&sheet->params);
0964         if (param == NULL) {
0965             return NGX_CONF_ERROR;
0966         }
0967 
0968         ngx_memzero(param, sizeof(ngx_http_xslt_param_t));
0969         ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0970 
0971         ccv.cf = cf;
0972         ccv.value = &value[i];
0973         ccv.complex_value = &param->value;
0974         ccv.zero = 1;
0975 
0976         if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0977             return NGX_CONF_ERROR;
0978         }
0979     }
0980 
0981     return NGX_CONF_OK;
0982 }
0983 
0984 
0985 static char *
0986 ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0987 {
0988     ngx_http_xslt_filter_loc_conf_t  *xlcf = conf;
0989 
0990     ngx_http_xslt_param_t            *param;
0991     ngx_http_compile_complex_value_t  ccv;
0992     ngx_str_t                        *value;
0993 
0994     value = cf->args->elts;
0995 
0996     if (xlcf->params == NULL) {
0997         xlcf->params = ngx_array_create(cf->pool, 2,
0998                                         sizeof(ngx_http_xslt_param_t));
0999         if (xlcf->params == NULL) {
1000             return NGX_CONF_ERROR;
1001         }
1002     }
1003 
1004     param = ngx_array_push(xlcf->params);
1005     if (param == NULL) {
1006         return NGX_CONF_ERROR;
1007     }
1008 
1009     param->name = value[1].data;
1010     param->quote = (cmd->post == NULL) ? 0 : 1;
1011 
1012     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1013 
1014     ccv.cf = cf;
1015     ccv.value = &value[2];
1016     ccv.complex_value = &param->value;
1017     ccv.zero = 1;
1018 
1019     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1020         return NGX_CONF_ERROR;
1021     }
1022 
1023     return NGX_CONF_OK;
1024 }
1025 
1026 
1027 static void
1028 ngx_http_xslt_cleanup_dtd(void *data)
1029 {
1030     xmlFreeDtd(data);
1031 }
1032 
1033 
1034 static void
1035 ngx_http_xslt_cleanup_stylesheet(void *data)
1036 {
1037     xsltFreeStylesheet(data);
1038 }
1039 
1040 
1041 static void *
1042 ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)
1043 {
1044     ngx_http_xslt_filter_main_conf_t  *conf;
1045 
1046     conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
1047     if (conf == NULL) {
1048         return NULL;
1049     }
1050 
1051     if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
1052                        sizeof(ngx_http_xslt_file_t))
1053         != NGX_OK)
1054     {
1055         return NULL;
1056     }
1057 
1058     if (ngx_array_init(&conf->sheet_files, cf->pool, 1,
1059                        sizeof(ngx_http_xslt_file_t))
1060         != NGX_OK)
1061     {
1062         return NULL;
1063     }
1064 
1065     return conf;
1066 }
1067 
1068 
1069 static void *
1070 ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
1071 {
1072     ngx_http_xslt_filter_loc_conf_t  *conf;
1073 
1074     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
1075     if (conf == NULL) {
1076         return NULL;
1077     }
1078 
1079     /*
1080      * set by ngx_pcalloc():
1081      *
1082      *     conf->dtd = NULL;
1083      *     conf->sheets = { NULL };
1084      *     conf->types = { NULL };
1085      *     conf->types_keys = NULL;
1086      *     conf->params = NULL;
1087      */
1088 
1089     conf->last_modified = NGX_CONF_UNSET;
1090 
1091     return conf;
1092 }
1093 
1094 
1095 static char *
1096 ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1097 {
1098     ngx_http_xslt_filter_loc_conf_t *prev = parent;
1099     ngx_http_xslt_filter_loc_conf_t *conf = child;
1100 
1101     if (conf->dtd == NULL) {
1102         conf->dtd = prev->dtd;
1103     }
1104 
1105     if (conf->sheets.nelts == 0) {
1106         conf->sheets = prev->sheets;
1107     }
1108 
1109     if (conf->params == NULL) {
1110         conf->params = prev->params;
1111     }
1112 
1113     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
1114                              &prev->types_keys, &prev->types,
1115                              ngx_http_xslt_default_types)
1116         != NGX_OK)
1117     {
1118         return NGX_CONF_ERROR;
1119     }
1120 
1121     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
1122 
1123     return NGX_CONF_OK;
1124 }
1125 
1126 
1127 static ngx_int_t
1128 ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf)
1129 {
1130     xmlInitParser();
1131 
1132 #if (NGX_HAVE_EXSLT)
1133     exsltRegisterAll();
1134 #endif
1135 
1136     return NGX_OK;
1137 }
1138 
1139 
1140 static ngx_int_t
1141 ngx_http_xslt_filter_init(ngx_conf_t *cf)
1142 {
1143     ngx_http_next_header_filter = ngx_http_top_header_filter;
1144     ngx_http_top_header_filter = ngx_http_xslt_header_filter;
1145 
1146     ngx_http_next_body_filter = ngx_http_top_body_filter;
1147     ngx_http_top_body_filter = ngx_http_xslt_body_filter;
1148 
1149     return NGX_OK;
1150 }
1151 
1152 
1153 static void
1154 ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
1155 {
1156     xsltCleanupGlobals();
1157     xmlCleanupParser();
1158 }