/* Copyright 2006 Joachim Zobel . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * This file implements a filter that passes SAX buckets through another filter * if a streaming xpath expression (named pattern by libxml2) matches. * These are from a very limited xpath subset as described here: * http://www.w3.org/TR/xmlschema-1/#Selector * */ #include #include #include #include #include #include #include #include #include #include module AP_MODULE_DECLARE_DATA xml2_module; //#include #include #include #include #include "frag_buffer.h" #include "buckets_sax.h" #include "sax_util.h" #include "sxpath.h" /***************************************************************************** * libxml2 streaming xpath *****************************************************************************/ /* * Context for the xml2_xpath_filter */ typedef struct { const xmlChar *pattern; xmlPatternPtr patternc; xmlStreamCtxtPtr patstream; ap_filter_t *condfilter; // if a match occured, this is the starts se_id se_id_t match; // The outgoing brigade apr_bucket_brigade *bb_out; } xml2_xpath_ctx; /* * Holds the condition filter */ typedef struct { const char *newname; const char *condfilter; const char *xpath; // ap_filter_rec_t *condfrec; } xml2_filter_cond; #define OBSCURE_ERROR_RTN (-500) /* * xml2_xpath_filter_init */ static int xml2_xpath_filter_init(ap_filter_t * f) { xml2_xpath_ctx *fctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(xml2_xpath_ctx)); // Get the config. xml2_cfg *cfg = ap_get_module_config(f->r->per_dir_config, &xml2_module); // Get the filter config by filter name xml2_filter_cond *fcond = apr_hash_get(cfg->fconds, f->frec->name, APR_HASH_KEY_STRING); // Create the condfilter ap_filter_t *condfilter = apr_palloc(f->r->pool, sizeof(ap_filter_t)); //fctx->condfilter = ap_add_output_filter_handle(fcond->condfrec, // NULL, f->r, f->c); // and initialize it condfilter->frec = ap_get_output_filter_handle(fcond->condfilter); condfilter->next = f->next; condfilter->ctx = NULL; condfilter->r = f->r; condfilter->c = f->c; fctx->condfilter = condfilter; // Set the pattern fctx->pattern = fcond->xpath; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "xml2_xpath_filter_init called for cond. pattern %s, filter %s.", fctx->pattern, fcond->condfilter); // compile the pattern passed in with fctx fctx->patternc = xmlPatterncompile(fctx->pattern, NULL, 0, NULL); if (fctx->patternc == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "Pattern %s failed to compile.", fctx->pattern); return OBSCURE_ERROR_RTN; } // Make shure the compiled pattern is cleaned up apr_pool_cleanup_register(f->r->pool, fctx->patternc, (void *) xmlFreePattern, apr_pool_cleanup_null); // create a stream context from the compiled pattern fctx->patstream = xmlPatternGetStreamCtxt(fctx->patternc); if (fctx->patstream == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "xmlPatternGetStreamCtxt failed for pattern %s.", fctx->pattern); return OBSCURE_ERROR_RTN; } // Make shure the stream context is cleaned up apr_pool_cleanup_register(f->r->pool, fctx->patstream, (void *) xmlFreeStreamCtxt, apr_pool_cleanup_null); // Create the outgoing brigade fctx->bb_out = apr_brigade_create(f->r->pool, f->c->bucket_alloc); return APR_SUCCESS; } /* * xml2_xpath_filter */ static int xml2_xpath_filter(ap_filter_t * f, apr_bucket_brigade * bb) { apr_bucket *b = NULL; xml2_xpath_ctx *fctx = f->ctx; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "xml2_xpath_filter called for pattern %s.", fctx->pattern); for (b = APR_BRIGADE_FIRST(bb); !APR_BRIGADE_EMPTY(bb); b = APR_BRIGADE_FIRST(bb)) { // Move the bucket to the outgoing brigade. APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(fctx->bb_out, b); if (!BUCKET_IS_SAX(b)) { // only sax buckets are processed continue; } bucket_sax *bs = b->data; int ret = 0; switch (sax_inspect_which(b)) { case START_ELT: { start_elt_t *se = bs->event; int match = 0; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "Processing start elt. %s, %s.", se->name.name, se->name.uri); ret = xmlStreamPush(fctx->patstream, se->name.name, se->name.uri); if (ret < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "xmlStreamPush() failed."); } else if (ret > 0) { match = 1; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "xmlStreamPush matched %s.", se->name.name); } attr_t *attr; for (attr = se->atts; (attr && attr->name.name); attr++) { ret = xmlStreamPushAttr(fctx->patstream, attr->name.name, attr->name.uri); if (ret < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "xmlStreamPushAttr() failed."); } else if (ret > 0) { match = 1; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "xmlStreamPushAttr matched %s.", attr->name.name); } } // Only the first match is of interest, // inner matches are ignored. if (match && !fctx->match) { fctx->match = se->se_id; // The current bucket is _not_ passed // with the unmatched buckets. APR_BUCKET_REMOVE(b); int rv = ap_pass_brigade(f->next, fctx->bb_out); APR_BRIGADE_INSERT_TAIL(fctx->bb_out, b); if (rv != APR_SUCCESS) { return rv; } } } break; case END_ELT: { /* * BAUSTELLE */ end_elt_t *ee = bs->event; if (fctx->match && fctx->match == -ee->se_id) { // match end bucket detected fctx->match = 0; int rv = ap_pass_brigade(fctx->condfilter, fctx->bb_out); if (rv != APR_SUCCESS) { return rv; } } ret = xmlStreamPop(fctx->patstream); if (ret < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "xmlStreamPop() failed."); } } break; default: break; } } if (fctx->match) { return ap_pass_brigade(fctx->condfilter, fctx->bb_out); } else { return ap_pass_brigade(f->next, fctx->bb_out); } } /* * xml2_filter_cond_create */ const char *xml2_filter_cond_create(cmd_parms * cmd, void *cfg, const char *newname, const char *condfilter, const char *xpath) { xml2_filter_cond *rec = apr_palloc(cmd->pool, sizeof(xml2_filter_cond)); rec->newname = newname; rec->condfilter = condfilter; rec->xpath = xpath; apr_hash_set(((xml2_cfg *) cfg)->fconds, newname, APR_HASH_KEY_STRING, rec); // Are multiple registrations for the same name harmful? ap_register_output_filter(newname, xml2_xpath_filter, xml2_xpath_filter_init, AP_FTYPE_RESOURCE); return NULL; }