Annotation of embedaddon/nginx/src/http/modules/ngx_http_xslt_filter_module.c, revision 1.1.1.1

1.1       misho       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: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>