~ubuntu-branches/ubuntu/trusty/nginx/trusty-proposed

« back to all changes in this revision

Viewing changes to src/http/modules/ngx_http_xslt_filter_module.c

  • Committer: Package Import Robot
  • Author(s): Kartik Mistry
  • Date: 2013-04-25 12:51:45 UTC
  • mfrom: (1.3.28)
  • mto: (1.3.29) (15.1.2 experimental)
  • mto: This revision was merged to the branch mainline in revision 64.
  • Revision ID: package-import@ubuntu.com-20130425125145-ugl0wor6bq0u5eae
Tags: upstream-1.4.0
ImportĀ upstreamĀ versionĀ 1.4.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
30
typedef struct {
 
31
    u_char                    *name;
 
32
    void                      *data;
 
33
} ngx_http_xslt_file_t;
 
34
 
 
35
 
 
36
typedef 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
 
 
42
typedef 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
 
 
49
typedef struct {
 
50
    xsltStylesheetPtr          stylesheet;
 
51
    ngx_array_t                params;       /* ngx_http_xslt_param_t */
 
52
} ngx_http_xslt_sheet_t;
 
53
 
 
54
 
 
55
typedef 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
 
 
64
typedef 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
 
 
75
static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
 
76
    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
 
77
static 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
 
 
81
static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
 
82
    const xmlChar *externalId, const xmlChar *systemId);
 
83
static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);
 
84
 
 
85
 
 
86
static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
 
87
    ngx_http_xslt_filter_ctx_t *ctx);
 
88
static 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);
 
90
static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s);
 
91
static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s);
 
92
static void ngx_http_xslt_cleanup(void *data);
 
93
 
 
94
static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,
 
95
    void *conf);
 
96
static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,
 
97
    void *conf);
 
98
static char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd,
 
99
    void *conf);
 
100
static void ngx_http_xslt_cleanup_dtd(void *data);
 
101
static void ngx_http_xslt_cleanup_stylesheet(void *data);
 
102
static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf);
 
103
static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
 
104
static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
 
105
    void *child);
 
106
static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
 
107
static void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle);
 
108
 
 
109
 
 
110
ngx_str_t  ngx_http_xslt_default_types[] = {
 
111
    ngx_string("text/xml"),
 
112
    ngx_null_string
 
113
};
 
114
 
 
115
 
 
116
static 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
 
 
157
static 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
 
 
172
ngx_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
 
 
188
static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
 
189
static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
 
190
 
 
191
 
 
192
static ngx_int_t
 
193
ngx_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
 
 
232
static ngx_int_t
 
233
ngx_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
 
 
299
static ngx_int_t
 
300
ngx_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
 
 
351
static ngx_int_t
 
352
ngx_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
 
 
394
static void
 
395
ngx_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
 
 
445
static void ngx_cdecl
 
446
ngx_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
 
 
470
static ngx_buf_t *
 
471
ngx_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
 
 
610
static ngx_int_t
 
611
ngx_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
 
 
742
static u_char *
 
743
ngx_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
 
 
764
static u_char *
 
765
ngx_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
 
 
786
static void
 
787
ngx_http_xslt_cleanup(void *data)
 
788
{
 
789
    ngx_free(data);
 
790
}
 
791
 
 
792
 
 
793
static char *
 
794
ngx_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
 
 
848
static char *
 
849
ngx_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
 
 
918
found:
 
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
 
 
957
static char *
 
958
ngx_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
 
 
999
static void
 
1000
ngx_http_xslt_cleanup_dtd(void *data)
 
1001
{
 
1002
    xmlFreeDtd(data);
 
1003
}
 
1004
 
 
1005
 
 
1006
static void
 
1007
ngx_http_xslt_cleanup_stylesheet(void *data)
 
1008
{
 
1009
    xsltFreeStylesheet(data);
 
1010
}
 
1011
 
 
1012
 
 
1013
static void *
 
1014
ngx_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
 
 
1041
static void *
 
1042
ngx_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
 
 
1065
static char *
 
1066
ngx_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
 
 
1095
static ngx_int_t
 
1096
ngx_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
 
 
1114
static void
 
1115
ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
 
1116
{
 
1117
    xsltCleanupGlobals();
 
1118
    xmlCleanupParser();
 
1119
}