--- /dev/null
+LD_OPTS += -lxml2 -lc_utils
+
+all: mod_xmltools.so
+
+mod_xmltools.so: apachetools.o xmltools.o
+ $(CC) -c $(CC_OPTS) mod_xmltools.c
+ $(CC) $(LD_OPTS) -shared -W1 apachetools.o xmltools.o mod_xmltools.o -o $@
+
+apachetools.o: apachetools.c apachetools.h
+ $(CC) -c $(CC_OPTS) apachetools.c -o $@
+
+xmltools.o: xmltools.c xmltools.h
+ $(CC) -c $(CC_OPTS) xmltools.c -o $@
+
+
+install:
+ $(APXS2) -i -a -n mod_xmltools mod_xmltools.so
+ @echo "-----------------------------------------------";
+ @echo -e "* Important * : Change httpd.conf from this: \n \
+ LoadModule mod_xmltools_module modules/mod_xmltools.so \n \
+ to this: \n \
+ LoadModule mod_xmltools modules/mod_xmltools.so"
+ @echo "-----------------------------------------------";
+ @sleep 3;
+
+clean:
+ /bin/rm -f *.o xmltools
--- /dev/null
+#include "apachetools.h"
+
+string_array* apacheParseParms(request_rec* r) {
+
+ if( r == NULL ) return NULL;
+
+ char* arg = r->args; /* url query string */
+ apr_pool_t *p = r->pool; /* memory pool */
+ string_array* sarray = init_string_array(12); /* method parameters */
+
+ growing_buffer* buffer = NULL; /* POST data */
+ growing_buffer* tmp_buf = NULL; /* temp buffer */
+
+ char* key = NULL; /* query item name */
+ char* val = NULL; /* query item value */
+
+
+ /* gather the post args and append them to the url query string */
+ if( !strcmp(r->method,"POST") ) {
+
+ ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
+
+ if(ap_should_client_block(r)) {
+
+ char body[1025];
+ memset(body,0,1025);
+ buffer = buffer_init(1025);
+
+ while(ap_get_client_block(r, body, 1024)) {
+ buffer_add( buffer, body );
+ memset(body,0,1025);
+ }
+
+ if(arg && arg[0]) {
+
+ tmp_buf = buffer_init(1024);
+ buffer_add(tmp_buf,arg);
+ buffer_add(tmp_buf,buffer->buf);
+ arg = (char*) apr_pstrdup(p, tmp_buf->buf);
+ buffer_free(tmp_buf);
+
+ } else {
+ arg = (char*) apr_pstrdup(p, buffer->buf);
+ }
+
+ buffer_free(buffer);
+ }
+ }
+
+
+ if( ! arg || !arg[0] ) { /* we received no request */
+ return NULL;
+ }
+
+
+ while( arg && (val = ap_getword(p, (const char**) &arg, '&'))) {
+
+ key = ap_getword(r->pool, (const char**) &val, '=');
+ if(!key || !key[0])
+ break;
+
+ ap_unescape_url((char*)key);
+ ap_unescape_url((char*)val);
+
+ string_array_add(sarray, key);
+ string_array_add(sarray, val);
+
+ }
+
+ return sarray;
+
+}
+
+
+
+string_array* apacheGetParamKeys(string_array* params) {
+ if(params == NULL) return NULL;
+ string_array* sarray = init_string_array(12);
+ int i;
+ for( i = 0; i < params->size; i++ )
+ string_array_add(sarray, string_array_get_string(params, i++));
+ return sarray;
+}
+
+string_array* apacheGetParamValues(string_array* params, char* key) {
+
+ if(params == NULL || key == NULL) return NULL;
+ string_array* sarray = init_string_array(12);
+
+ int i;
+ for( i = 0; i < params->size; i++ ) {
+ char* nkey = string_array_get_string(params, i++);
+ if(key && !strcmp(nkey, key))
+ string_array_add(sarray, string_array_get_string(params, i));
+ }
+ return sarray;
+}
+
+
+char* apacheGetFirstParamValue(string_array* params, char* key) {
+ if(params == NULL || key == NULL) return NULL;
+
+ int i;
+ for( i = 0; i < params->size; i++ ) {
+ char* nkey = string_array_get_string(params, i++);
+ if(key && !strcmp(nkey, key))
+ return strdup(string_array_get_string(params, i));
+ }
+
+ return NULL;
+}
+
+
--- /dev/null
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "apr_compat.h"
+#include "apr_strings.h"
+
+#include "string_array.h"
+#include "utils.h"
+
+#ifndef APACHE_TOOLS_H
+#define APACHE_TOOLS_H
+
+
+/* parses apache URL params (GET and POST).
+ Returns a string_array of the form [ key, val, key, val, ...]
+ Returns NULL if there are no params */
+string_array* apacheParseParms(request_rec* r);
+
+/* provide the params string array, and this will generate a
+ string of array of param keys
+ the returned string_array most be freed by the caller
+ */
+string_array* apacheGetParamKeys(string_array* params);
+
+/* provide the params string array and a key name, and
+ this will provide the value found for that key
+ the returned string_array most be freed by the caller
+ */
+string_array* apacheGetParamValues(string_array* params, char* key);
+
+/* returns the first value found for the given param.
+ char* must be freed by the caller */
+char* apacheGetFirstParamValue(string_array* params, char* key);
+
+
+#endif
--- /dev/null
+#include "apachetools.h"
+#include "xmltools.h"
+
+#define MODULE_NAME "mod_xmltools" /* our module name */
+#define PARAM_LOCALE "locale" /* the URL param for the local directory */
+#define LANG_DTD "lang.dtd" /* the DTD for the test entities */
+
+/* these should be config directives */
+#define LOCALE_DIR "/home/erickson/sandbox/apachemods/locale" /* The root directory where the local files are stored */
+#define DEFAULT_LOCALE "en-US" /* If no locale data is provided */
+
+
+/* Child Init */
+static void mod_xmltools_child_init(apr_pool_t *p, server_rec *s) {
+}
+
+/* allocates a char* to hold the name of the DTD language file
+ Prints to stderr and returns NULL if there was an error loading the file
+ */
+
+static char* get_dtd_lang_file(string_array* params) {
+
+ char* localedir = apacheGetFirstParamValue(params, PARAM_LOCALE);
+ if(!localedir) localedir = strdup(DEFAULT_LOCALE);
+
+ int len = strlen(LANG_DTD) + strlen(localedir) + strlen(LOCALE_DIR) + 1;
+ char dtdfile[len];
+ bzero(dtdfile, len);
+
+ if(localedir)
+ sprintf(dtdfile, "%s/%s/%s", LOCALE_DIR, localedir, LANG_DTD );
+
+ return strdup(dtdfile);
+}
+
+static int mod_xmltools_handler (request_rec* r) {
+
+ /* make sure we're needed first thing*/
+ if (strcmp(r->handler, MODULE_NAME ))
+ return DECLINED;
+
+ /* we accept get/post requests */
+ r->allowed |= (AP_METHOD_BIT << M_GET);
+ r->allowed |= (AP_METHOD_BIT << M_POST);
+
+ ap_set_content_type(r, "text/html");
+
+ string_array* params = apacheParseParms(r);
+
+ char* file = r->filename;
+ char* dtdfile = get_dtd_lang_file(params);
+
+ xmlDocPtr doc;
+
+ /* be explicit */
+ xmlSubstituteEntitiesDefault(0);
+
+ /* parse the doc */
+ if( (doc = xmlParseFile(file)) == NULL) {
+ fprintf(stderr, "\n ^-- Error parsing XML file %s\n", file);
+ fflush(stderr);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* process xincludes */
+ if( xmlXIncludeProcess(doc) < 0 ) {
+ fprintf(stderr, "\n ^-- Error processing XIncludes for file %s\n", file);
+ fflush(stderr);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+
+ /* replace the DTD */
+ if(xmlReplaceDtd(doc, dtdfile) < 0) {
+ fprintf(stderr, "Error replacing DTD file with file %s\n", dtdfile);
+ fflush(stderr);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+
+ /* force DTD entity replacement */
+ doc = xmlProcessDtdEntities(doc);
+
+ /* stringify */
+ char* xml = xmlDocToString(doc, 0);
+
+ /* print the doc */
+ ap_rputs(xml, r);
+
+ /* deallocate */
+ free(dtdfile);
+ free(xml);
+ xmlFreeDoc(doc);
+ xmlCleanupCharEncodingHandlers();
+ xmlCleanupParser();
+
+ return OK;
+
+}
+
+
+static void mod_xmltools_register_hooks (apr_pool_t *p) {
+ ap_hook_handler(mod_xmltools_handler, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init(mod_xmltools_child_init,NULL,NULL,APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA mod_xmltools = {
+ STANDARD20_MODULE_STUFF,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ mod_xmltools_register_hooks,
+};
+
--- /dev/null
+#include "xmltools.h"
+
+#define TEXT_DTD "test2.dtd"
+
+
+
+/*
+int main( int argc, char* argv[] ) {
+
+ char* file = argv[1];
+ char* localedir = argv[2];
+
+ int len = strlen(TEXT_DTD) + strlen(localedir) + 1;
+ char dtdfile[len];
+ bzero(dtdfile, len);
+
+ if(localedir)
+ sprintf(dtdfile, "%s/%s", localedir, TEXT_DTD );
+
+
+ if(access(dtdfile, R_OK)) {
+ fprintf(stderr, "Unable to open DTD file %s\n", dtdfile);
+ fflush(stderr);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+
+ xmlDocPtr doc;
+
+ xmlSubstituteEntitiesDefault(0);
+
+
+ if( (doc = xmlParseFile(file)) == NULL) {
+ fprintf(stderr, "\n ^-- Error parsing XML file %s\n", file);
+ fflush(stderr);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if( xmlXIncludeProcess(doc) < 0 ) {
+ fprintf(stderr, "\n ^-- Error processing XIncludes for file %s\n", file);
+ fflush(stderr);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ xmlReplaceDtd(doc, dtdfile);
+
+ doc = xmlProcessDtdEntities(doc);
+
+ char* xml = xmlDocToString(doc, 0);
+
+ printf("\n%s\n", xml);
+
+ free(xml);
+ xmlFreeDoc(doc);
+ xmlCleanupCharEncodingHandlers();
+ xmlCleanupParser();
+
+ return 0;
+
+}
+*/
+
+xmlDocPtr xmlProcessDtdEntities(xmlDocPtr doc) {
+ char* xml = xmlDocToString(doc, 1);
+ xmlFreeDoc(doc);
+ xmlSubstituteEntitiesDefault(1);
+ xmlDocPtr d = xmlParseMemory(xml, strlen(xml));
+ free(xml);
+ return d;
+}
+
+
+int xmlReplaceDtd(xmlDocPtr doc, char* dtdfile) {
+
+ if(!doc || !dtdfile) return 0;
+
+ /* remove the original DTD */
+ if(doc->children && doc->children->type == XML_DTD_NODE) {
+ xmlNodePtr p = doc->children;
+ xmlUnlinkNode(p);
+ xmlFreeNode(p);
+ }
+
+
+ xmlDtdPtr dtd = xmlParseDTD(NULL, dtdfile);
+
+ if(!dtd) {
+ fprintf(stderr, "Error parsing DTD file %s\n", dtdfile);
+ fflush(stderr);
+ return -1;
+ }
+
+ fprintf(stderr, "2\n");
+ fflush(stderr);
+
+ dtd->name = xmlStrdup((xmlChar*)"x");
+ doc->extSubset = dtd;
+ dtd->doc = doc;
+ dtd->parent = doc;
+ xmlNodePtr x = doc->children;
+ doc->children = (xmlNodePtr)dtd;
+ dtd->next = x;
+
+ return 1;
+}
+
+char* xmlDocToString(xmlDocPtr doc, int full) {
+
+ char* xml;
+
+ if(full) {
+
+ xmlChar* xmlbuf;
+ int size;
+ xmlDocDumpMemory(doc, &xmlbuf, &size);
+ xml = strdup((char*) (xmlbuf));
+ xmlFree(xmlbuf);
+ return xml;
+
+ } else {
+
+ xmlBufferPtr xmlbuf = xmlBufferCreate();
+ xmlNodeDump( xmlbuf, doc, xmlDocGetRootElement(doc), 0, 0);
+ xml = strdup((char*) (xmlBufferContent(xmlbuf)));
+ xmlBufferFree(xmlbuf);
+ return xml;
+
+ }
+
+}
--- /dev/null
+
+/* general headers */
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+/* libxml2 headers */
+#include <libxml/parser.h>
+#include <libxml/globals.h>
+#include <libxml/xinclude.h>
+#include <libxml/xmlwriter.h>
+#include <libxml/xmlreader.h>
+
+#include "apachetools.h"
+
+
+#ifndef XMLTOOLS_H
+#define XMLTOOLS_H
+
+
+/* turns a doc into a string. string must be deallocated.
+ if 'full', then the entire doc is stringified, otherwise
+ the root node (on down) is stringified */
+char* xmlDocToString(xmlDocPtr doc, int full);
+
+int xmlReplaceDtd(xmlDocPtr doc, char* dtdfile);
+
+/* Inline DTD Entity replacement.
+ creates a new doc with the entities replaced, frees the
+ doc provided and returns a new one.
+ Do this and you'll be OK:
+ doc = xmlProcessDtdEntities(doc);
+ */
+xmlDocPtr xmlProcessDtdEntities(xmlDocPtr doc);
+
+
+#endif
+
+