From 413954fd574d66aa529ed9ad30172e38640cd19f Mon Sep 17 00:00:00 2001 From: erickson Date: Mon, 20 Mar 2006 16:21:21 +0000 Subject: [PATCH 1/1] adding entity replacer to cvs git-svn-id: svn://svn.open-ils.org/ILS/trunk@3396 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/apachemods/mod_xmlent.c | 271 +++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 Open-ILS/src/apachemods/mod_xmlent.c diff --git a/Open-ILS/src/apachemods/mod_xmlent.c b/Open-ILS/src/apachemods/mod_xmlent.c new file mode 100644 index 0000000000..1e3fa015b5 --- /dev/null +++ b/Open-ILS/src/apachemods/mod_xmlent.c @@ -0,0 +1,271 @@ +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_request.h" +#include "apr_compat.h" +#include "apr_strings.h" +#include "apr_reslist.h" +#include "http_log.h" +#include "util_filter.h" +#include "opensrf/utils.h" + +#include +#include +#include + +#define MODULE_NAME "xmlent_module" + +/* Define the config defaults here */ +#define MODXMLENT_CONFIG_STRIP_COMMENTS "XMLEntStripComments" +#define MODXMLENT_CONFIG_STRIP_COMMENTS_DEFAULT "yes" +#define MODXMLENT_CONFIG_CONTENT_TYPE "XMLEntContentType" +#define MODXMLENT_CONFIG_CONTENT_TYPE_DEFAULT "text/html" +#define MODXMLENT_CONFIG_STRIP_PI "XMLEntStripPI" +#define MODXMLENT_CONFIG_STRIP_PI_DEFAULT "yes" + +module AP_MODULE_DECLARE_DATA xmlent_module; + +/* our context */ +typedef struct { + apr_bucket_brigade* brigade; /* the bucket brigade we buffer our data into */ + XML_Parser parser; /* our XML parser */ +} xmlEntContext; + +/* our config data */ +typedef struct { + int stripComments; /* should we strip comments on the way out? */ + int stripPI; /* should we strip processing instructions on the way out? */ + char* contentType; /* the content type used to server pages */ +} xmlEntConfig; + + +/* get the content type from the config */ +static const char* xmlEntSetContentType(cmd_parms *params, void *cfg, const char *arg) { + xmlEntConfig* config = (xmlEntConfig*) cfg; + config->contentType = (char*) arg; + return NULL; +} + +/* get the stip PI flag from the config */ +static const char* xmlEntSetStripPI(cmd_parms *params, void *cfg, const char *arg) { + xmlEntConfig* config = (xmlEntConfig*) cfg; + char* a = (char*) arg; + config->stripPI = (a && !strcasecmp(a, "yes")) ? 1 : 0; + return NULL; +} + +/* Get the strip comments flag from the config */ +static const char* xmlEntSetStripComments(cmd_parms *params, void *cfg, const char *arg) { + xmlEntConfig* config = (xmlEntConfig*) cfg; + char* a = (char*) arg; + config->stripComments = (a && !strcasecmp(a, "yes")) ? 1 : 0; + return NULL; +} + +/* Tell apache how to set our config variables */ +static const command_rec xmlEntCommands[] = { + AP_INIT_TAKE1( MODXMLENT_CONFIG_STRIP_COMMENTS, + xmlEntSetStripComments, NULL, ACCESS_CONF, "XMLENT Strip Comments"), + AP_INIT_TAKE1( MODXMLENT_CONFIG_CONTENT_TYPE, + xmlEntSetContentType, NULL, ACCESS_CONF, "XMLENT Content Type"), + AP_INIT_TAKE1( MODXMLENT_CONFIG_STRIP_PI, + xmlEntSetStripPI, NULL, ACCESS_CONF, "XMLENT Strip XML Processing Instructions"), + {NULL} +}; + +/* Creates a new config object */ +static void* xmlEntCreateDirConfig( apr_pool_t* p, char* dir ) { + xmlEntConfig* config = + (xmlEntConfig*) apr_palloc( p, sizeof(xmlEntConfig) ); + config->stripComments = + (MODXMLENT_CONFIG_STRIP_COMMENTS_DEFAULT && + !strcasecmp(MODXMLENT_CONFIG_STRIP_COMMENTS_DEFAULT, "yes")) ? 1 : 0; + config->stripPI = + (MODXMLENT_CONFIG_STRIP_PI_DEFAULT && + !strcasecmp(MODXMLENT_CONFIG_STRIP_PI_DEFAULT, "yes")) ? 1 : 0; + config->contentType = MODXMLENT_CONFIG_CONTENT_TYPE_DEFAULT; + return (void*) config; +} + +/* keep for a while in case we ever need it */ +/* +#define XMLENT_INHERIT(p, c, f) ((c->f) ? c->f : p->f); +static void* xmlEntMergeDirConfig(apr_pool_t *p, void *base, void *overrides) { + xmlEntConfig* parent = base; + xmlEntConfig* child = overrides; + xmlEntConfig* newConf = (xmlEntConfig*) apr_pcalloc(p, sizeof(xmlEntConfig)); + newConf->contentType = XMLENT_INHERIT(parent, child, contentType); + newConf->stripComments = XMLENT_INHERIT(parent, child, stripComments); + return newConf; +} +*/ + + +/* We need a global parser object because sub-requests, with different + * filter contexts, are parsing part of the same document. + * This means that this filter will only work in forked (non-threaded) environments. + * XXX Figure out how to share pointers/data accross filters */ +XML_Parser parser = NULL; + +/* utility function which passes data to the next filter */ +static void _fwrite( ap_filter_t* filter, char* data, ... ) { + if(!(filter && data)) return; + xmlEntContext* ctx = (xmlEntContext*) filter->ctx; + VA_LIST_TO_STRING(data); + ap_fwrite( filter->next, ctx->brigade, VA_BUF, strlen(VA_BUF)); +} + + +/* cycles through the attributes attached to an element */ +static void printAttr( ap_filter_t* filter, const char** atts ) { + if(!atts) return; + int i; + for( i = 0; atts[i] && atts[i+1]; i++ ) { + const char* name = atts[i]; + const char* value = atts[i+1]; + char* escaped = ap_escape_html(filter->r->pool, value); + _fwrite( filter, " %s='%s'", name, escaped ); + i++; + } +} + +/* Starts and XML element */ +static void XMLCALL startElement(void *userData, const char *name, const char **atts) { + ap_filter_t* filter = (ap_filter_t*) userData; + _fwrite(filter, "<%s", name ); + printAttr( filter, atts ); + _fwrite(filter, ">\n", name ); +} + +/* Handles the character data */ +static void XMLCALL charHandler( void* userData, const XML_Char* s, int len ) { + ap_filter_t* filter = (ap_filter_t*) userData; + char data[len+1]; + bzero(data, len+1); + memcpy( data, s, len ); + char* escaped = ap_escape_html(filter->r->pool, data); + _fwrite( filter, escaped ); +} + +static void XMLCALL handlePI( void* userData, const XML_Char* target, const XML_Char* data) { + /* + fprintf(stderr, "target=%s : data=%s\n", target, data); + fflush(stderr); + */ + ap_filter_t* filter = (ap_filter_t*) userData; + _fwrite(filter, "", target, data); +} + +/* Ends an XML element */ +static void XMLCALL endElement(void *userData, const char *name) { + ap_filter_t* filter = (ap_filter_t*) userData; + _fwrite( filter, "\n", name ); +} + + +/* The handler. Create a new parser and/or filter context where appropriate + * and parse the chunks of data received from the brigade + */ +static int xmlEntHandler( ap_filter_t *f, apr_bucket_brigade *brigade ) { + + xmlEntContext* ctx = f->ctx; + apr_bucket* currentBucket = NULL; + apr_pool_t* pool = f->r->pool; + const char* data; + apr_size_t len; + + /* load the per-dir/location config */ + xmlEntConfig* config = ap_get_module_config( + f->r->per_dir_config, &xmlent_module ); + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, + 0, f->r, "XMLENT Content Type = %s", config->contentType); + + /* set the content type based on the config */ + ap_set_content_type(f->r, config->contentType); + + /* create the XML parser */ + if( parser == NULL ) { + parser = XML_ParserCreate(NULL); + XML_SetUserData(parser, f); + XML_SetElementHandler(parser, startElement, endElement); + XML_SetCharacterDataHandler(parser, charHandler); + if(!config->stripPI) + XML_SetProcessingInstructionHandler(parser, handlePI); + } + + /* create the filter context */ + if( ctx == NULL ) { + f->ctx = ctx = apr_pcalloc( pool, sizeof(*ctx)); + ctx->brigade = apr_brigade_create( pool, f->c->bucket_alloc ); + ctx->parser = parser; + } + + /* cycle through the buckets in the brigade */ + while (!APR_BRIGADE_EMPTY(brigade)) { + + /* grab the next bucket */ + currentBucket = APR_BRIGADE_FIRST(brigade); + + /* clean up when we're done */ + if (APR_BUCKET_IS_EOS(currentBucket) || APR_BUCKET_IS_FLUSH(currentBucket)) { + APR_BUCKET_REMOVE(currentBucket); + APR_BRIGADE_INSERT_TAIL(ctx->brigade, currentBucket); + ap_pass_brigade(f->next, ctx->brigade); + XML_ParserFree(parser); + parser = NULL; + return APR_SUCCESS; + } + + /* read the incoming data */ + int s = apr_bucket_read(currentBucket, &data, &len, APR_NONBLOCK_READ); + if( s != APR_SUCCESS ) { + ap_log_rerror( APLOG_MARK, APLOG_ERR, 0, f->r, + "XMLENT error reading data from filter with status %d", s); + return s; + } + + if (len > 0) { + + ap_log_rerror( APLOG_MARK, APLOG_DEBUG, + 0, f->r, "XMLENT read %d bytes", (int)len); + + /* push data into the XML push parser */ + if ( XML_Parse(ctx->parser, data, len, 0) == XML_STATUS_ERROR ) { + + /* log and die on XML errors */ + ap_log_rerror( APLOG_MARK, APLOG_ERR, 0, f->r, "%s at line %d\n", + XML_ErrorString(XML_GetErrorCode(ctx->parser)), + XML_GetCurrentLineNumber(ctx->parser)); + + return HTTP_INTERNAL_SERVER_ERROR; + } + } + + /* so a subrequest doesn't re-read this bucket */ + apr_bucket_delete(currentBucket); + } + + apr_brigade_destroy(brigade); + return APR_SUCCESS; +} + + +/* Register the filter function as a filter for modifying the HTTP body (content) */ +static void xmlEntRegisterHook(apr_pool_t *pool) { + ap_register_output_filter("XMLENT", xmlEntHandler, NULL, AP_FTYPE_CONTENT_SET); +} + +/* Define the module data */ +module AP_MODULE_DECLARE_DATA xmlent_module = { + STANDARD20_MODULE_STUFF, + xmlEntCreateDirConfig, /* dir config creater */ + NULL, /*xmlEntMergeDirConfig,*/ /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + xmlEntCommands, /* command apr_table_t */ + xmlEntRegisterHook /* register hook */ +}; + + -- 2.43.2