Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

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_pcalloc(r->pool, sizeof(ngx_buf_t));
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         p = string.data;
0690         last = string.data + string.len;
0691 
0692         while (p && *p) {
0693 
0694             value = p;
0695             p = (u_char *) ngx_strchr(p, '=');
0696             if (p == NULL) {
0697                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0698                                 "invalid libxslt parameter \"%s\"", value);
0699                 return NGX_ERROR;
0700             }
0701             *p++ = '\0';
0702 
0703             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0704                            "xslt filter param name: \"%s\"", value);
0705 
0706             s = ngx_array_push(&ctx->params);
0707             if (s == NULL) {
0708                 return NGX_ERROR;
0709             }
0710 
0711             *s = value;
0712 
0713             value = p;
0714             p = (u_char *) ngx_strchr(p, ':');
0715 
0716             if (p) {
0717                 len = p - value;
0718                 *p++ = '\0';
0719 
0720             } else {
0721                 len = last - value;
0722             }
0723 
0724             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0725                            "xslt filter param value: \"%s\"", value);
0726 
0727             dst = value;
0728             src = value;
0729 
0730             ngx_unescape_uri(&dst, &src, len, 0);
0731 
0732             *dst = '\0';
0733 
0734             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0735                            "xslt filter param unescaped: \"%s\"", value);
0736 
0737             s = ngx_array_push(&ctx->params);
0738             if (s == NULL) {
0739                 return NGX_ERROR;
0740             }
0741 
0742             *s = value;
0743         }
0744     }
0745 
0746     if (final) {
0747         s = ngx_array_push(&ctx->params);
0748         if (s == NULL) {
0749             return NGX_ERROR;
0750         }
0751 
0752         *s = NULL;
0753     }
0754 
0755     return NGX_OK;
0756 }
0757 
0758 
0759 static u_char *
0760 ngx_http_xslt_content_type(xsltStylesheetPtr s)
0761 {
0762     u_char  *type;
0763 
0764     if (s->mediaType) {
0765         return s->mediaType;
0766     }
0767 
0768     for (s = s->imports; s; s = s->next) {
0769 
0770         type = ngx_http_xslt_content_type(s);
0771 
0772         if (type) {
0773             return type;
0774         }
0775     }
0776 
0777     return NULL;
0778 }
0779 
0780 
0781 static u_char *
0782 ngx_http_xslt_encoding(xsltStylesheetPtr s)
0783 {
0784     u_char  *encoding;
0785 
0786     if (s->encoding) {
0787         return s->encoding;
0788     }
0789 
0790     for (s = s->imports; s; s = s->next) {
0791 
0792         encoding = ngx_http_xslt_encoding(s);
0793 
0794         if (encoding) {
0795             return encoding;
0796         }
0797     }
0798 
0799     return NULL;
0800 }
0801 
0802 
0803 static void
0804 ngx_http_xslt_cleanup(void *data)
0805 {
0806     ngx_free(data);
0807 }
0808 
0809 
0810 static char *
0811 ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0812 {
0813     ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
0814 
0815     ngx_str_t                         *value;
0816     ngx_uint_t                         i;
0817     ngx_pool_cleanup_t                *cln;
0818     ngx_http_xslt_file_t              *file;
0819     ngx_http_xslt_filter_main_conf_t  *xmcf;
0820 
0821     if (xlcf->dtd) {
0822         return "is duplicate";
0823     }
0824 
0825     value = cf->args->elts;
0826 
0827     xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
0828 
0829     file = xmcf->dtd_files.elts;
0830     for (i = 0; i < xmcf->dtd_files.nelts; i++) {
0831         if (ngx_strcmp(file[i].name, value[1].data) == 0) {
0832             xlcf->dtd = file[i].data;
0833             return NGX_CONF_OK;
0834         }
0835     }
0836 
0837     cln = ngx_pool_cleanup_add(cf->pool, 0);
0838     if (cln == NULL) {
0839         return NGX_CONF_ERROR;
0840     }
0841 
0842     xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
0843 
0844     if (xlcf->dtd == NULL) {
0845         ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
0846         return NGX_CONF_ERROR;
0847     }
0848 
0849     cln->handler = ngx_http_xslt_cleanup_dtd;
0850     cln->data = xlcf->dtd;
0851 
0852     file = ngx_array_push(&xmcf->dtd_files);
0853     if (file == NULL) {
0854         return NGX_CONF_ERROR;
0855     }
0856 
0857     file->name = value[1].data;
0858     file->data = xlcf->dtd;
0859 
0860     return NGX_CONF_OK;
0861 }
0862 
0863 
0864 
0865 static char *
0866 ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0867 {
0868     ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
0869 
0870     ngx_str_t                         *value;
0871     ngx_uint_t                         i, n;
0872     ngx_pool_cleanup_t                *cln;
0873     ngx_http_xslt_file_t              *file;
0874     ngx_http_xslt_sheet_t             *sheet;
0875     ngx_http_xslt_param_t             *param;
0876     ngx_http_compile_complex_value_t   ccv;
0877     ngx_http_xslt_filter_main_conf_t  *xmcf;
0878 
0879     value = cf->args->elts;
0880 
0881     if (xlcf->sheets.elts == NULL) {
0882         if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
0883                            sizeof(ngx_http_xslt_sheet_t))
0884             != NGX_OK)
0885         {
0886             return NGX_CONF_ERROR;
0887         }
0888     }
0889 
0890     sheet = ngx_array_push(&xlcf->sheets);
0891     if (sheet == NULL) {
0892         return NGX_CONF_ERROR;
0893     }
0894 
0895     ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
0896 
0897     if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
0898         return NGX_CONF_ERROR;
0899     }
0900 
0901     xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
0902 
0903     file = xmcf->sheet_files.elts;
0904     for (i = 0; i < xmcf->sheet_files.nelts; i++) {
0905         if (ngx_strcmp(file[i].name, value[1].data) == 0) {
0906             sheet->stylesheet = file[i].data;
0907             goto found;
0908         }
0909     }
0910 
0911     cln = ngx_pool_cleanup_add(cf->pool, 0);
0912     if (cln == NULL) {
0913         return NGX_CONF_ERROR;
0914     }
0915 
0916     sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
0917     if (sheet->stylesheet == NULL) {
0918         ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
0919                            "xsltParseStylesheetFile(\"%s\") failed",
0920                            value[1].data);
0921         return NGX_CONF_ERROR;
0922     }
0923 
0924     cln->handler = ngx_http_xslt_cleanup_stylesheet;
0925     cln->data = sheet->stylesheet;
0926 
0927     file = ngx_array_push(&xmcf->sheet_files);
0928     if (file == NULL) {
0929         return NGX_CONF_ERROR;
0930     }
0931 
0932     file->name = value[1].data;
0933     file->data = sheet->stylesheet;
0934 
0935 found:
0936 
0937     n = cf->args->nelts;
0938 
0939     if (n == 2) {
0940         return NGX_CONF_OK;
0941     }
0942 
0943     if (ngx_array_init(&sheet->params, cf->pool, n - 2,
0944                        sizeof(ngx_http_xslt_param_t))
0945         != NGX_OK)
0946     {
0947         return NGX_CONF_ERROR;
0948     }
0949 
0950     for (i = 2; i < n; i++) {
0951 
0952         param = ngx_array_push(&sheet->params);
0953         if (param == NULL) {
0954             return NGX_CONF_ERROR;
0955         }
0956 
0957         ngx_memzero(param, sizeof(ngx_http_xslt_param_t));
0958         ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0959 
0960         ccv.cf = cf;
0961         ccv.value = &value[i];
0962         ccv.complex_value = &param->value;
0963         ccv.zero = 1;
0964 
0965         if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0966             return NGX_CONF_ERROR;
0967         }
0968     }
0969 
0970     return NGX_CONF_OK;
0971 }
0972 
0973 
0974 static char *
0975 ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0976 {
0977     ngx_http_xslt_filter_loc_conf_t  *xlcf = conf;
0978 
0979     ngx_http_xslt_param_t            *param;
0980     ngx_http_compile_complex_value_t  ccv;
0981     ngx_str_t                        *value;
0982 
0983     value = cf->args->elts;
0984 
0985     if (xlcf->params == NULL) {
0986         xlcf->params = ngx_array_create(cf->pool, 2,
0987                                         sizeof(ngx_http_xslt_param_t));
0988         if (xlcf->params == NULL) {
0989             return NGX_CONF_ERROR;
0990         }
0991     }
0992 
0993     param = ngx_array_push(xlcf->params);
0994     if (param == NULL) {
0995         return NGX_CONF_ERROR;
0996     }
0997 
0998     param->name = value[1].data;
0999     param->quote = (cmd->post == NULL) ? 0 : 1;
1000 
1001     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1002 
1003     ccv.cf = cf;
1004     ccv.value = &value[2];
1005     ccv.complex_value = &param->value;
1006     ccv.zero = 1;
1007 
1008     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1009         return NGX_CONF_ERROR;
1010     }
1011 
1012     return NGX_CONF_OK;
1013 }
1014 
1015 
1016 static void
1017 ngx_http_xslt_cleanup_dtd(void *data)
1018 {
1019     xmlFreeDtd(data);
1020 }
1021 
1022 
1023 static void
1024 ngx_http_xslt_cleanup_stylesheet(void *data)
1025 {
1026     xsltFreeStylesheet(data);
1027 }
1028 
1029 
1030 static void *
1031 ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)
1032 {
1033     ngx_http_xslt_filter_main_conf_t  *conf;
1034 
1035     conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
1036     if (conf == NULL) {
1037         return NULL;
1038     }
1039 
1040     if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
1041                        sizeof(ngx_http_xslt_file_t))
1042         != NGX_OK)
1043     {
1044         return NULL;
1045     }
1046 
1047     if (ngx_array_init(&conf->sheet_files, cf->pool, 1,
1048                        sizeof(ngx_http_xslt_file_t))
1049         != NGX_OK)
1050     {
1051         return NULL;
1052     }
1053 
1054     return conf;
1055 }
1056 
1057 
1058 static void *
1059 ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
1060 {
1061     ngx_http_xslt_filter_loc_conf_t  *conf;
1062 
1063     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
1064     if (conf == NULL) {
1065         return NULL;
1066     }
1067 
1068     /*
1069      * set by ngx_pcalloc():
1070      *
1071      *     conf->dtd = NULL;
1072      *     conf->sheets = { NULL };
1073      *     conf->types = { NULL };
1074      *     conf->types_keys = NULL;
1075      *     conf->params = NULL;
1076      */
1077 
1078     conf->last_modified = NGX_CONF_UNSET;
1079 
1080     return conf;
1081 }
1082 
1083 
1084 static char *
1085 ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1086 {
1087     ngx_http_xslt_filter_loc_conf_t *prev = parent;
1088     ngx_http_xslt_filter_loc_conf_t *conf = child;
1089 
1090     if (conf->dtd == NULL) {
1091         conf->dtd = prev->dtd;
1092     }
1093 
1094     if (conf->sheets.nelts == 0) {
1095         conf->sheets = prev->sheets;
1096     }
1097 
1098     if (conf->params == NULL) {
1099         conf->params = prev->params;
1100     }
1101 
1102     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
1103                              &prev->types_keys, &prev->types,
1104                              ngx_http_xslt_default_types)
1105         != NGX_OK)
1106     {
1107         return NGX_CONF_ERROR;
1108     }
1109 
1110     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
1111 
1112     return NGX_CONF_OK;
1113 }
1114 
1115 
1116 static ngx_int_t
1117 ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf)
1118 {
1119     xmlInitParser();
1120 
1121 #if (NGX_HAVE_EXSLT)
1122     exsltRegisterAll();
1123 #endif
1124 
1125     return NGX_OK;
1126 }
1127 
1128 
1129 static ngx_int_t
1130 ngx_http_xslt_filter_init(ngx_conf_t *cf)
1131 {
1132     ngx_http_next_header_filter = ngx_http_top_header_filter;
1133     ngx_http_top_header_filter = ngx_http_xslt_header_filter;
1134 
1135     ngx_http_next_body_filter = ngx_http_top_body_filter;
1136     ngx_http_top_body_filter = ngx_http_xslt_body_filter;
1137 
1138     return NGX_OK;
1139 }
1140 
1141 
1142 static void
1143 ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
1144 {
1145     xsltCleanupGlobals();
1146     xmlCleanupParser();
1147 }