Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.19.2 ]​[ nginx-1.18.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     r->allow_ranges = 0;
0237 
0238     return NGX_OK;
0239 }
0240 
0241 
0242 static ngx_int_t
0243 ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0244 {
0245     int                          wellFormed;
0246     ngx_chain_t                 *cl;
0247     ngx_http_xslt_filter_ctx_t  *ctx;
0248 
0249     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0250                    "xslt filter body");
0251 
0252     if (in == NULL) {
0253         return ngx_http_next_body_filter(r, in);
0254     }
0255 
0256     ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
0257 
0258     if (ctx == NULL || ctx->done) {
0259         return ngx_http_next_body_filter(r, in);
0260     }
0261 
0262     for (cl = in; cl; cl = cl->next) {
0263 
0264         if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
0265 
0266             if (ctx->ctxt->myDoc) {
0267 
0268 #if (NGX_HTTP_XSLT_REUSE_DTD)
0269                 ctx->ctxt->myDoc->extSubset = NULL;
0270 #endif
0271                 xmlFreeDoc(ctx->ctxt->myDoc);
0272             }
0273 
0274             xmlFreeParserCtxt(ctx->ctxt);
0275 
0276             return ngx_http_xslt_send(r, ctx, NULL);
0277         }
0278 
0279         if (cl->buf->last_buf || cl->buf->last_in_chain) {
0280 
0281             ctx->doc = ctx->ctxt->myDoc;
0282 
0283 #if (NGX_HTTP_XSLT_REUSE_DTD)
0284             ctx->doc->extSubset = NULL;
0285 #endif
0286 
0287             wellFormed = ctx->ctxt->wellFormed;
0288 
0289             xmlFreeParserCtxt(ctx->ctxt);
0290 
0291             if (wellFormed) {
0292                 return ngx_http_xslt_send(r, ctx,
0293                                        ngx_http_xslt_apply_stylesheet(r, ctx));
0294             }
0295 
0296             xmlFreeDoc(ctx->doc);
0297 
0298             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0299                           "not well formed XML document");
0300 
0301             return ngx_http_xslt_send(r, ctx, NULL);
0302         }
0303     }
0304 
0305     return NGX_OK;
0306 }
0307 
0308 
0309 static ngx_int_t
0310 ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
0311     ngx_buf_t *b)
0312 {
0313     ngx_int_t                         rc;
0314     ngx_chain_t                       out;
0315     ngx_pool_cleanup_t               *cln;
0316     ngx_http_xslt_filter_loc_conf_t  *conf;
0317 
0318     ctx->done = 1;
0319 
0320     if (b == NULL) {
0321         return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
0322                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
0323     }
0324 
0325     cln = ngx_pool_cleanup_add(r->pool, 0);
0326 
0327     if (cln == NULL) {
0328         ngx_free(b->pos);
0329         return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
0330                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
0331     }
0332 
0333     if (r == r->main) {
0334         r->headers_out.content_length_n = b->last - b->pos;
0335 
0336         if (r->headers_out.content_length) {
0337             r->headers_out.content_length->hash = 0;
0338             r->headers_out.content_length = NULL;
0339         }
0340 
0341         conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
0342 
0343         if (!conf->last_modified) {
0344             ngx_http_clear_last_modified(r);
0345             ngx_http_clear_etag(r);
0346 
0347         } else {
0348             ngx_http_weak_etag(r);
0349         }
0350     }
0351 
0352     rc = ngx_http_next_header_filter(r);
0353 
0354     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
0355         ngx_free(b->pos);
0356         return rc;
0357     }
0358 
0359     cln->handler = ngx_http_xslt_cleanup;
0360     cln->data = b->pos;
0361 
0362     out.buf = b;
0363     out.next = NULL;
0364 
0365     return ngx_http_next_body_filter(r, &out);
0366 }
0367 
0368 
0369 static ngx_int_t
0370 ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
0371     ngx_buf_t *b)
0372 {
0373     int               err;
0374     xmlParserCtxtPtr  ctxt;
0375 
0376     if (ctx->ctxt == NULL) {
0377 
0378         ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
0379         if (ctxt == NULL) {
0380             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0381                           "xmlCreatePushParserCtxt() failed");
0382             return NGX_ERROR;
0383         }
0384         xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD
0385                                                |XML_PARSE_NOWARNING);
0386 
0387         ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset;
0388         ctxt->sax->setDocumentLocator = NULL;
0389         ctxt->sax->error = ngx_http_xslt_sax_error;
0390         ctxt->sax->fatalError = ngx_http_xslt_sax_error;
0391         ctxt->sax->_private = ctx;
0392 
0393         ctx->ctxt = ctxt;
0394         ctx->request = r;
0395     }
0396 
0397     err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),
0398                         (b->last_buf) || (b->last_in_chain));
0399 
0400     if (err == 0) {
0401         b->pos = b->last;
0402         return NGX_OK;
0403     }
0404 
0405     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0406                   "xmlParseChunk() failed, error:%d", err);
0407 
0408     return NGX_ERROR;
0409 }
0410 
0411 
0412 static void
0413 ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
0414     const xmlChar *externalId, const xmlChar *systemId)
0415 {
0416     xmlParserCtxtPtr ctxt = data;
0417 
0418     xmlDocPtr                         doc;
0419     xmlDtdPtr                         dtd;
0420     ngx_http_request_t               *r;
0421     ngx_http_xslt_filter_ctx_t       *ctx;
0422     ngx_http_xslt_filter_loc_conf_t  *conf;
0423 
0424     ctx = ctxt->sax->_private;
0425     r = ctx->request;
0426 
0427     conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
0428 
0429     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0430                    "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
0431                    name ? name : (xmlChar *) "",
0432                    externalId ? externalId : (xmlChar *) "",
0433                    systemId ? systemId : (xmlChar *) "");
0434 
0435     doc = ctxt->myDoc;
0436 
0437 #if (NGX_HTTP_XSLT_REUSE_DTD)
0438 
0439     dtd = conf->dtd;
0440 
0441 #else
0442 
0443     dtd = xmlCopyDtd(conf->dtd);
0444     if (dtd == NULL) {
0445         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0446                       "xmlCopyDtd() failed");
0447         return;
0448     }
0449 
0450     if (doc->children == NULL) {
0451         xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
0452 
0453     } else {
0454         xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
0455     }
0456 
0457 #endif
0458 
0459     doc->extSubset = dtd;
0460 }
0461 
0462 
0463 static void ngx_cdecl
0464 ngx_http_xslt_sax_error(void *data, const char *msg, ...)
0465 {
0466     xmlParserCtxtPtr ctxt = data;
0467 
0468     size_t                       n;
0469     va_list                      args;
0470     ngx_http_xslt_filter_ctx_t  *ctx;
0471     u_char                       buf[NGX_MAX_ERROR_STR];
0472 
0473     ctx = ctxt->sax->_private;
0474 
0475     buf[0] = '\0';
0476 
0477     va_start(args, msg);
0478     n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
0479     va_end(args);
0480 
0481     while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
0482 
0483     ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
0484                   "libxml2 error: \"%*s\"", n + 1, buf);
0485 }
0486 
0487 
0488 static ngx_buf_t *
0489 ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
0490     ngx_http_xslt_filter_ctx_t *ctx)
0491 {
0492     int                               len, rc, doc_type;
0493     u_char                           *type, *encoding;
0494     ngx_buf_t                        *b;
0495     ngx_uint_t                        i;
0496     xmlChar                          *buf;
0497     xmlDocPtr                         doc, res;
0498     ngx_http_xslt_sheet_t            *sheet;
0499     ngx_http_xslt_filter_loc_conf_t  *conf;
0500 
0501     conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
0502     sheet = conf->sheets.elts;
0503     doc = ctx->doc;
0504 
0505     /* preallocate array for 4 params */
0506 
0507     if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
0508         != NGX_OK)
0509     {
0510         xmlFreeDoc(doc);
0511         return NULL;
0512     }
0513 
0514     for (i = 0; i < conf->sheets.nelts; i++) {
0515 
0516         ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);
0517         if (ctx->transform == NULL) {
0518             xmlFreeDoc(doc);
0519             return NULL;
0520         }
0521 
0522         if (conf->params
0523             && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)
0524         {
0525             xsltFreeTransformContext(ctx->transform);
0526             xmlFreeDoc(doc);
0527             return NULL;
0528         }
0529 
0530         if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {
0531             xsltFreeTransformContext(ctx->transform);
0532             xmlFreeDoc(doc);
0533             return NULL;
0534         }
0535 
0536         res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,
0537                                       ctx->params.elts, NULL, NULL,
0538                                       ctx->transform);
0539 
0540         xsltFreeTransformContext(ctx->transform);
0541         xmlFreeDoc(doc);
0542 
0543         if (res == NULL) {
0544             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0545                           "xsltApplyStylesheet() failed");
0546             return NULL;
0547         }
0548 
0549         doc = res;
0550 
0551         /* reset array elements */
0552         ctx->params.nelts = 0;
0553     }
0554 
0555     /* there must be at least one stylesheet */
0556 
0557     if (r == r->main) {
0558         type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);
0559 
0560     } else {
0561         type = NULL;
0562     }
0563 
0564     encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);
0565     doc_type = doc->type;
0566 
0567     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0568                    "xslt filter type: %d t:%s e:%s",
0569                    doc_type, type ? type : (u_char *) "(null)",
0570                    encoding ? encoding : (u_char *) "(null)");
0571 
0572     rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
0573 
0574     xmlFreeDoc(doc);
0575 
0576     if (rc != 0) {
0577         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0578                       "xsltSaveResultToString() failed");
0579         return NULL;
0580     }
0581 
0582     if (len == 0) {
0583         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0584                       "xsltSaveResultToString() returned zero-length result");
0585         return NULL;
0586     }
0587 
0588     b = ngx_calloc_buf(r->pool);
0589     if (b == NULL) {
0590         ngx_free(buf);
0591         return NULL;
0592     }
0593 
0594     b->pos = buf;
0595     b->last = buf + len;
0596     b->memory = 1;
0597 
0598     if (encoding) {
0599         r->headers_out.charset.len = ngx_strlen(encoding);
0600         r->headers_out.charset.data = encoding;
0601     }
0602 
0603     if (r != r->main) {
0604         return b;
0605     }
0606 
0607     b->last_buf = 1;
0608 
0609     if (type) {
0610         len = ngx_strlen(type);
0611 
0612         r->headers_out.content_type_len = len;
0613         r->headers_out.content_type.len = len;
0614         r->headers_out.content_type.data = type;
0615 
0616     } else if (doc_type == XML_HTML_DOCUMENT_NODE) {
0617 
0618         r->headers_out.content_type_len = sizeof("text/html") - 1;
0619         ngx_str_set(&r->headers_out.content_type, "text/html");
0620     }
0621 
0622     r->headers_out.content_type_lowcase = NULL;
0623 
0624     return b;
0625 }
0626 
0627 
0628 static ngx_int_t
0629 ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
0630     ngx_array_t *params, ngx_uint_t final)
0631 {
0632     u_char                 *p, *value, *dst, *src, **s;
0633     size_t                  len;
0634     ngx_uint_t              i;
0635     ngx_str_t               string;
0636     ngx_http_xslt_param_t  *param;
0637 
0638     param = params->elts;
0639 
0640     for (i = 0; i < params->nelts; i++) {
0641 
0642         if (ngx_http_complex_value(r, &param[i].value, &string) != NGX_OK) {
0643             return NGX_ERROR;
0644         }
0645 
0646         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0647                        "xslt filter param: \"%s\"", string.data);
0648 
0649         if (param[i].name) {
0650 
0651             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0652                            "xslt filter param name: \"%s\"", param[i].name);
0653 
0654             if (param[i].quote) {
0655                 if (xsltQuoteOneUserParam(ctx->transform, param[i].name,
0656                                           string.data)
0657                     != 0)
0658                 {
0659                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0660                                 "xsltQuoteOneUserParam(\"%s\", \"%s\") failed",
0661                                 param[i].name, string.data);
0662                     return NGX_ERROR;
0663                 }
0664 
0665                 continue;
0666             }
0667 
0668             s = ngx_array_push(&ctx->params);
0669             if (s == NULL) {
0670                 return NGX_ERROR;
0671             }
0672 
0673             *s = param[i].name;
0674 
0675             s = ngx_array_push(&ctx->params);
0676             if (s == NULL) {
0677                 return NGX_ERROR;
0678             }
0679 
0680             *s = string.data;
0681 
0682             continue;
0683         }
0684 
0685         /*
0686          * parse param1=value1:param2=value2 syntax as used by parameters
0687          * specified in xslt_stylesheet directives
0688          */
0689 
0690         if (param[i].value.lengths) {
0691             p = string.data;
0692 
0693         } else {
0694             p = ngx_pnalloc(r->pool, string.len + 1);
0695             if (p == NULL) {
0696                 return NGX_ERROR;
0697             }
0698 
0699             ngx_memcpy(p, string.data, string.len + 1);
0700         }
0701 
0702         while (p && *p) {
0703 
0704             value = p;
0705             p = (u_char *) ngx_strchr(p, '=');
0706             if (p == NULL) {
0707                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0708                                 "invalid libxslt parameter \"%s\"", value);
0709                 return NGX_ERROR;
0710             }
0711             *p++ = '\0';
0712 
0713             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0714                            "xslt filter param name: \"%s\"", value);
0715 
0716             s = ngx_array_push(&ctx->params);
0717             if (s == NULL) {
0718                 return NGX_ERROR;
0719             }
0720 
0721             *s = value;
0722 
0723             value = p;
0724             p = (u_char *) ngx_strchr(p, ':');
0725 
0726             if (p) {
0727                 len = p - value;
0728                 *p++ = '\0';
0729 
0730             } else {
0731                 len = ngx_strlen(value);
0732             }
0733 
0734             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0735                            "xslt filter param value: \"%s\"", value);
0736 
0737             dst = value;
0738             src = value;
0739 
0740             ngx_unescape_uri(&dst, &src, len, 0);
0741 
0742             *dst = '\0';
0743 
0744             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0745                            "xslt filter param unescaped: \"%s\"", value);
0746 
0747             s = ngx_array_push(&ctx->params);
0748             if (s == NULL) {
0749                 return NGX_ERROR;
0750             }
0751 
0752             *s = value;
0753         }
0754     }
0755 
0756     if (final) {
0757         s = ngx_array_push(&ctx->params);
0758         if (s == NULL) {
0759             return NGX_ERROR;
0760         }
0761 
0762         *s = NULL;
0763     }
0764 
0765     return NGX_OK;
0766 }
0767 
0768 
0769 static u_char *
0770 ngx_http_xslt_content_type(xsltStylesheetPtr s)
0771 {
0772     u_char  *type;
0773 
0774     if (s->mediaType) {
0775         return s->mediaType;
0776     }
0777 
0778     for (s = s->imports; s; s = s->next) {
0779 
0780         type = ngx_http_xslt_content_type(s);
0781 
0782         if (type) {
0783             return type;
0784         }
0785     }
0786 
0787     return NULL;
0788 }
0789 
0790 
0791 static u_char *
0792 ngx_http_xslt_encoding(xsltStylesheetPtr s)
0793 {
0794     u_char  *encoding;
0795 
0796     if (s->encoding) {
0797         return s->encoding;
0798     }
0799 
0800     for (s = s->imports; s; s = s->next) {
0801 
0802         encoding = ngx_http_xslt_encoding(s);
0803 
0804         if (encoding) {
0805             return encoding;
0806         }
0807     }
0808 
0809     return NULL;
0810 }
0811 
0812 
0813 static void
0814 ngx_http_xslt_cleanup(void *data)
0815 {
0816     ngx_free(data);
0817 }
0818 
0819 
0820 static char *
0821 ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0822 {
0823     ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
0824 
0825     ngx_str_t                         *value;
0826     ngx_uint_t                         i;
0827     ngx_pool_cleanup_t                *cln;
0828     ngx_http_xslt_file_t              *file;
0829     ngx_http_xslt_filter_main_conf_t  *xmcf;
0830 
0831     if (xlcf->dtd) {
0832         return "is duplicate";
0833     }
0834 
0835     value = cf->args->elts;
0836 
0837     xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
0838 
0839     file = xmcf->dtd_files.elts;
0840     for (i = 0; i < xmcf->dtd_files.nelts; i++) {
0841         if (ngx_strcmp(file[i].name, value[1].data) == 0) {
0842             xlcf->dtd = file[i].data;
0843             return NGX_CONF_OK;
0844         }
0845     }
0846 
0847     cln = ngx_pool_cleanup_add(cf->pool, 0);
0848     if (cln == NULL) {
0849         return NGX_CONF_ERROR;
0850     }
0851 
0852     xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
0853 
0854     if (xlcf->dtd == NULL) {
0855         ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
0856         return NGX_CONF_ERROR;
0857     }
0858 
0859     cln->handler = ngx_http_xslt_cleanup_dtd;
0860     cln->data = xlcf->dtd;
0861 
0862     file = ngx_array_push(&xmcf->dtd_files);
0863     if (file == NULL) {
0864         return NGX_CONF_ERROR;
0865     }
0866 
0867     file->name = value[1].data;
0868     file->data = xlcf->dtd;
0869 
0870     return NGX_CONF_OK;
0871 }
0872 
0873 
0874 
0875 static char *
0876 ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0877 {
0878     ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
0879 
0880     ngx_str_t                         *value;
0881     ngx_uint_t                         i, n;
0882     ngx_pool_cleanup_t                *cln;
0883     ngx_http_xslt_file_t              *file;
0884     ngx_http_xslt_sheet_t             *sheet;
0885     ngx_http_xslt_param_t             *param;
0886     ngx_http_compile_complex_value_t   ccv;
0887     ngx_http_xslt_filter_main_conf_t  *xmcf;
0888 
0889     value = cf->args->elts;
0890 
0891     if (xlcf->sheets.elts == NULL) {
0892         if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
0893                            sizeof(ngx_http_xslt_sheet_t))
0894             != NGX_OK)
0895         {
0896             return NGX_CONF_ERROR;
0897         }
0898     }
0899 
0900     sheet = ngx_array_push(&xlcf->sheets);
0901     if (sheet == NULL) {
0902         return NGX_CONF_ERROR;
0903     }
0904 
0905     ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
0906 
0907     if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
0908         return NGX_CONF_ERROR;
0909     }
0910 
0911     xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
0912 
0913     file = xmcf->sheet_files.elts;
0914     for (i = 0; i < xmcf->sheet_files.nelts; i++) {
0915         if (ngx_strcmp(file[i].name, value[1].data) == 0) {
0916             sheet->stylesheet = file[i].data;
0917             goto found;
0918         }
0919     }
0920 
0921     cln = ngx_pool_cleanup_add(cf->pool, 0);
0922     if (cln == NULL) {
0923         return NGX_CONF_ERROR;
0924     }
0925 
0926     sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
0927     if (sheet->stylesheet == NULL) {
0928         ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
0929                            "xsltParseStylesheetFile(\"%s\") failed",
0930                            value[1].data);
0931         return NGX_CONF_ERROR;
0932     }
0933 
0934     cln->handler = ngx_http_xslt_cleanup_stylesheet;
0935     cln->data = sheet->stylesheet;
0936 
0937     file = ngx_array_push(&xmcf->sheet_files);
0938     if (file == NULL) {
0939         return NGX_CONF_ERROR;
0940     }
0941 
0942     file->name = value[1].data;
0943     file->data = sheet->stylesheet;
0944 
0945 found:
0946 
0947     n = cf->args->nelts;
0948 
0949     if (n == 2) {
0950         return NGX_CONF_OK;
0951     }
0952 
0953     if (ngx_array_init(&sheet->params, cf->pool, n - 2,
0954                        sizeof(ngx_http_xslt_param_t))
0955         != NGX_OK)
0956     {
0957         return NGX_CONF_ERROR;
0958     }
0959 
0960     for (i = 2; i < n; i++) {
0961 
0962         param = ngx_array_push(&sheet->params);
0963         if (param == NULL) {
0964             return NGX_CONF_ERROR;
0965         }
0966 
0967         ngx_memzero(param, sizeof(ngx_http_xslt_param_t));
0968         ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0969 
0970         ccv.cf = cf;
0971         ccv.value = &value[i];
0972         ccv.complex_value = &param->value;
0973         ccv.zero = 1;
0974 
0975         if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0976             return NGX_CONF_ERROR;
0977         }
0978     }
0979 
0980     return NGX_CONF_OK;
0981 }
0982 
0983 
0984 static char *
0985 ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0986 {
0987     ngx_http_xslt_filter_loc_conf_t  *xlcf = conf;
0988 
0989     ngx_http_xslt_param_t            *param;
0990     ngx_http_compile_complex_value_t  ccv;
0991     ngx_str_t                        *value;
0992 
0993     value = cf->args->elts;
0994 
0995     if (xlcf->params == NULL) {
0996         xlcf->params = ngx_array_create(cf->pool, 2,
0997                                         sizeof(ngx_http_xslt_param_t));
0998         if (xlcf->params == NULL) {
0999             return NGX_CONF_ERROR;
1000         }
1001     }
1002 
1003     param = ngx_array_push(xlcf->params);
1004     if (param == NULL) {
1005         return NGX_CONF_ERROR;
1006     }
1007 
1008     param->name = value[1].data;
1009     param->quote = (cmd->post == NULL) ? 0 : 1;
1010 
1011     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1012 
1013     ccv.cf = cf;
1014     ccv.value = &value[2];
1015     ccv.complex_value = &param->value;
1016     ccv.zero = 1;
1017 
1018     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1019         return NGX_CONF_ERROR;
1020     }
1021 
1022     return NGX_CONF_OK;
1023 }
1024 
1025 
1026 static void
1027 ngx_http_xslt_cleanup_dtd(void *data)
1028 {
1029     xmlFreeDtd(data);
1030 }
1031 
1032 
1033 static void
1034 ngx_http_xslt_cleanup_stylesheet(void *data)
1035 {
1036     xsltFreeStylesheet(data);
1037 }
1038 
1039 
1040 static void *
1041 ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)
1042 {
1043     ngx_http_xslt_filter_main_conf_t  *conf;
1044 
1045     conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
1046     if (conf == NULL) {
1047         return NULL;
1048     }
1049 
1050     if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
1051                        sizeof(ngx_http_xslt_file_t))
1052         != NGX_OK)
1053     {
1054         return NULL;
1055     }
1056 
1057     if (ngx_array_init(&conf->sheet_files, cf->pool, 1,
1058                        sizeof(ngx_http_xslt_file_t))
1059         != NGX_OK)
1060     {
1061         return NULL;
1062     }
1063 
1064     return conf;
1065 }
1066 
1067 
1068 static void *
1069 ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
1070 {
1071     ngx_http_xslt_filter_loc_conf_t  *conf;
1072 
1073     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
1074     if (conf == NULL) {
1075         return NULL;
1076     }
1077 
1078     /*
1079      * set by ngx_pcalloc():
1080      *
1081      *     conf->dtd = NULL;
1082      *     conf->sheets = { NULL };
1083      *     conf->types = { NULL };
1084      *     conf->types_keys = NULL;
1085      *     conf->params = NULL;
1086      */
1087 
1088     conf->last_modified = NGX_CONF_UNSET;
1089 
1090     return conf;
1091 }
1092 
1093 
1094 static char *
1095 ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1096 {
1097     ngx_http_xslt_filter_loc_conf_t *prev = parent;
1098     ngx_http_xslt_filter_loc_conf_t *conf = child;
1099 
1100     if (conf->dtd == NULL) {
1101         conf->dtd = prev->dtd;
1102     }
1103 
1104     if (conf->sheets.nelts == 0) {
1105         conf->sheets = prev->sheets;
1106     }
1107 
1108     if (conf->params == NULL) {
1109         conf->params = prev->params;
1110     }
1111 
1112     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
1113                              &prev->types_keys, &prev->types,
1114                              ngx_http_xslt_default_types)
1115         != NGX_OK)
1116     {
1117         return NGX_CONF_ERROR;
1118     }
1119 
1120     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
1121 
1122     return NGX_CONF_OK;
1123 }
1124 
1125 
1126 static ngx_int_t
1127 ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf)
1128 {
1129     xmlInitParser();
1130 
1131 #if (NGX_HAVE_EXSLT)
1132     exsltRegisterAll();
1133 #endif
1134 
1135     return NGX_OK;
1136 }
1137 
1138 
1139 static ngx_int_t
1140 ngx_http_xslt_filter_init(ngx_conf_t *cf)
1141 {
1142     ngx_http_next_header_filter = ngx_http_top_header_filter;
1143     ngx_http_top_header_filter = ngx_http_xslt_header_filter;
1144 
1145     ngx_http_next_body_filter = ngx_http_top_body_filter;
1146     ngx_http_top_body_filter = ngx_http_xslt_body_filter;
1147 
1148     return NGX_OK;
1149 }
1150 
1151 
1152 static void
1153 ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
1154 {
1155     xsltCleanupGlobals();
1156     xmlCleanupParser();
1157 }