source: nginx/src/http/modules/ngx_http_xslt_filter_module.c @ 4946:2570296374b4

Last change on this file since 4946:2570296374b4 was 4946:2570296374b4, checked in by Ruslan Ermilov <ru@…>, 5 years ago

Xslt: prevented infinite loop.

If XSLT transformation failed and error 500 was handled in the same
location, an infinite loop occured that exhausted the stack.

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