Hush a warning about XML_Size not being an int
[Evergreen.git] / Open-ILS / src / apachemods / mod_xmlent.c
index 1e3fa01..2df9eb6 100644 (file)
@@ -3,7 +3,7 @@
 #include "http_core.h"
 #include "http_protocol.h"
 #include "http_request.h"
-#include "apr_compat.h"
+//#include "apr_compat.h"
 #include "apr_strings.h"
 #include "apr_reslist.h"
 #include "http_log.h"
 
 /* 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" 
+#define MODXMLENT_CONFIG_DOCTYPE "XMLEntDoctype"
+#define MODXMLENT_CONFIG_STRIP_DOCTYPE "XMLEntStripDoctype"
+#define MODXMLENT_CONFIG_ESCAPE_SCRIPT "XMLEntEscapeScript"
 
 module AP_MODULE_DECLARE_DATA xmlent_module;
 
+int xmlEntInScript = 0; /* are we in the middle of a <script> tag */
+
 /* our context */
 typedef struct {
        apr_bucket_brigade* brigade; /* the bucket brigade we buffer our data into */
@@ -34,9 +37,12 @@ typedef struct {
 
 /* our config data */
 typedef struct {
-       int stripComments;  /* should we strip comments on the way out? */
+       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 */
+       int stripDoctype;
+       int escapeScript;               /* if true, we html-escape anything text inside a <scritp> tag */
+       char* contentType;      /* the content type used to server pages */
+       char* doctype;                  /* the doctype header to send before any other data */
 } xmlEntConfig;
 
 
@@ -47,6 +53,7 @@ static const char* xmlEntSetContentType(cmd_parms *params, void *cfg, const char
        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;
@@ -63,6 +70,28 @@ static const char* xmlEntSetStripComments(cmd_parms *params, void *cfg, const ch
        return NULL;
 }
 
+static const char* xmlEntSetEscapeScript(cmd_parms *params, void *cfg, const char *arg) {
+       xmlEntConfig* config = (xmlEntConfig*) cfg;
+       char* a = (char*) arg;
+       config->escapeScript = (a && !strcasecmp(a, "yes")) ? 1 : 0;
+       return NULL;
+}
+
+static const char* xmlEntSetStripDoctype(cmd_parms *params, void *cfg, const char *arg) {
+       xmlEntConfig* config = (xmlEntConfig*) cfg;
+       char* a = (char*) arg;
+       config->stripDoctype = (a && !strcasecmp(a, "yes")) ? 1 : 0;
+       return NULL;
+}
+
+
+/* Get the user defined doctype from the config */
+static const char* xmlEntSetDoctype(cmd_parms *params, void *cfg, const char *arg) {
+       xmlEntConfig* config = (xmlEntConfig*) cfg;
+       config->doctype = (char*) arg;
+       return NULL;
+}
+
 /* Tell apache how to set our config variables */
 static const command_rec xmlEntCommands[] = {
        AP_INIT_TAKE1( MODXMLENT_CONFIG_STRIP_COMMENTS, 
@@ -71,6 +100,12 @@ static const command_rec xmlEntCommands[] = {
                        xmlEntSetContentType, NULL, ACCESS_CONF, "XMLENT Content Type"),
        AP_INIT_TAKE1( MODXMLENT_CONFIG_STRIP_PI,
                        xmlEntSetStripPI, NULL, ACCESS_CONF, "XMLENT Strip XML Processing Instructions"),
+       AP_INIT_TAKE1( MODXMLENT_CONFIG_DOCTYPE,
+                       xmlEntSetDoctype, NULL, ACCESS_CONF, "XMLENT Doctype Declaration"),
+       AP_INIT_TAKE1( MODXMLENT_CONFIG_STRIP_DOCTYPE,
+                       xmlEntSetStripDoctype, NULL, ACCESS_CONF, "XMLENT Strip Doctype Declaration"),
+       AP_INIT_TAKE1( MODXMLENT_CONFIG_ESCAPE_SCRIPT,
+                       xmlEntSetEscapeScript, NULL, ACCESS_CONF, "XMLENT Escape data in script tags"),
        {NULL}
 };
 
@@ -78,13 +113,14 @@ static const command_rec xmlEntCommands[] = {
 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;
+
+       config->stripComments = 0;
+       config->stripPI       = 0;
+       config->stripDoctype  = 0;
+       config->escapeScript     = 1;
+       config->contentType      = MODXMLENT_CONFIG_CONTENT_TYPE_DEFAULT;
+       config->doctype       = NULL;
+
        return (void*) config;
 }
 
@@ -117,6 +153,17 @@ static void _fwrite( ap_filter_t* filter, char* data, ... ) {
 }
 
 
+/** XXX move me to  opensrf/utils.h */
+#define OSRF_UTILS_REPLACE_CHAR(str, o, n)\
+       do {\
+               int i = 0;\
+               while(str[i] != '\0') {\
+                       if(str[i] == o)\
+                               str[i] = n;\
+                       i++;\
+               }\
+       } while(0)
+
 /* cycles through the attributes attached to an element */
 static void printAttr( ap_filter_t* filter, const char** atts ) {
        if(!atts) return;
@@ -125,17 +172,30 @@ static void printAttr( ap_filter_t* filter, const char** atts ) {
                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 );
+
+               /* we make a big assumption here that if the string contains a ', 
+                * then the original attribute was wrapped in "s - so recreate that */
+               if( strchr( escaped, '\'' ) ) {
+                       OSRF_UTILS_REPLACE_CHAR(escaped,'"','\'');
+                       _fwrite( filter, " %s=\"%s\"", name, escaped );
+
+               } else {
+                       OSRF_UTILS_REPLACE_CHAR(escaped,'\'','"');
+                       _fwrite( filter, " %s='%s'", name, escaped );
+               }
+
                i++;
        }
 }
 
-/* Starts and XML element */
+/* Starts an 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 );
+       _fwrite(filter, ">", name );
+       if(!strcmp(name, "script")) 
+               xmlEntInScript = 1;
 }
 
 /* Handles the character data */
@@ -144,23 +204,44 @@ static void XMLCALL charHandler( void* userData, const XML_Char* s, int len ) {
        char data[len+1];
        bzero(data, len+1);
        memcpy( data, s, len );
-       char* escaped = ap_escape_html(filter->r->pool, data);
-       _fwrite( filter, escaped );
+
+       xmlEntConfig* config = ap_get_module_config( 
+                       filter->r->per_dir_config, &xmlent_module );
+
+       if( xmlEntInScript && ! config->escapeScript ) {
+               _fwrite( filter, "%s", data );
+
+       } else {
+               char* escaped = ap_escape_html(filter->r->pool, data);
+               _fwrite( filter, "%s", 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, "<?%s %s?>", target, data);
 }
 
+static void XMLCALL handleComment( void* userData, const XML_Char* comment ) {
+       ap_filter_t* filter = (ap_filter_t*) userData;
+       _fwrite(filter, "<!-- %s -->", comment);
+}
+
 /* Ends an XML element */
 static void XMLCALL endElement(void *userData, const char *name) {
        ap_filter_t* filter = (ap_filter_t*) userData;
-       _fwrite( filter, "</%s>\n", name );
+       _fwrite( filter, "</%s>", name );
+       if(!strcmp(name, "script")) 
+               xmlEntInScript = 1;
+}
+
+static void XMLCALL doctypeHandler( void* userData, 
+       const char* name, const char* sysid, const char* pubid, int hasinternal ) {
+
+       ap_filter_t* filter = (ap_filter_t*) userData;
+       char* s = (sysid) ? (char*) sysid : "";
+       char* p = (pubid) ? (char*) pubid : "";
+       _fwrite( filter, "<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", name, p, s );
 }
 
 
@@ -180,19 +261,31 @@ static int xmlEntHandler( ap_filter_t *f, apr_bucket_brigade *brigade ) {
                        f->r->per_dir_config, &xmlent_module );
 
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 
-                       0, f->r, "XMLENT Content Type = %s", config->contentType);
+                       0, f->r, "XMLENT Config:\nContent Type = %s, "
+                       "Strip PI = %s, Strip Comments = %s, Doctype = %s", 
+                       config->contentType, 
+                       (config->stripPI) ? "yes" : "no", 
+                       (config->stripComments) ? "yes" : "no",
+                       config->doctype);
 
        /* set the content type based on the config */
        ap_set_content_type(f->r, config->contentType);
 
+
        /* create the XML parser */
+       int firstrun = 0;
        if( parser == NULL ) {
-               parser = XML_ParserCreate(NULL);
+               firstrun = 1;
+               parser = XML_ParserCreate("UTF-8");
                XML_SetUserData(parser, f);
                XML_SetElementHandler(parser, startElement, endElement);
                XML_SetCharacterDataHandler(parser, charHandler);
+               if(!config->stripDoctype)
+                       XML_SetStartDoctypeDeclHandler( parser, doctypeHandler );
                if(!config->stripPI)
                        XML_SetProcessingInstructionHandler(parser, handlePI);
+               if(!config->stripComments)
+                       XML_SetCommentHandler(parser, handleComment);
        }
 
        /* create the filter context */
@@ -202,6 +295,18 @@ static int xmlEntHandler( ap_filter_t *f, apr_bucket_brigade *brigade ) {
                ctx->parser = parser;
        }
 
+
+       if(firstrun) { /* we haven't started writing the data to the stream yet */
+
+               /* go ahead and write the doctype out if we have one defined */
+               if(config->doctype) {
+                       ap_log_rerror( APLOG_MARK, APLOG_DEBUG, 
+                                       0, f->r, "XMLENT DOCTYPE => %s", config->doctype);
+                       _fwrite(f, "%s\n", config->doctype);
+               }
+       }
+
+
        /* cycle through the buckets in the brigade */
        while (!APR_BRIGADE_EMPTY(brigade)) {
 
@@ -235,10 +340,12 @@ static int xmlEntHandler( ap_filter_t *f, apr_bucket_brigade *brigade ) {
                        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",
+                               ap_log_rerror( APLOG_MARK, APLOG_ERR, 0, f->r, "XMLENT XML Parse Error: %s at line %d\n",
                                        XML_ErrorString(XML_GetErrorCode(ctx->parser)), 
-                                       XML_GetCurrentLineNumber(ctx->parser));
+                                       (int) XML_GetCurrentLineNumber(ctx->parser));
 
+                               XML_ParserFree(parser);
+                               parser = NULL;
                                return HTTP_INTERNAL_SERVER_ERROR; 
                        }
        }
@@ -260,12 +367,12 @@ static void xmlEntRegisterHook(apr_pool_t *pool) {
 /* 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 */
+  xmlEntCreateDirConfig,       /* dir config creater */
+  NULL,                                                        /* dir merger --- default is to override */
+  NULL,                                              /* server config */
+  NULL,                    /* merge server config */
+  xmlEntCommands,          /* command apr_table_t */
+  xmlEntRegisterHook                   /* register hook */
 };