1 #include "mod_xmlbuilder.h"
3 char* __xmlBuilderDynamicLocale = NULL;
6 /* set the base DTD directory */
7 static const char* xmlBuilderSetBaseDir(cmd_parms *params, void *cfg, const char *arg) {
8 xmlBuilderConfig* config = ap_get_module_config(
9 params->server->module_config, &xmlbuilder_module );
10 config->baseDir = (char*) arg;
14 static const char* xmlBuilderSetDefaultLocale(
15 cmd_parms* params, void* cfg, const char* arg ) {
16 xmlBuilderConfig* config = ap_get_module_config(
17 params->server->module_config, &xmlbuilder_module );
18 config->defaultLocale = (char*) arg;
22 static const char* xmlBuilderSetDefaultDtd(
23 cmd_parms* params, void* cfg, const char* arg ) {
24 xmlBuilderConfig* config = ap_get_module_config(
25 params->server->module_config, &xmlbuilder_module );
26 config->defaultDtd = (char*) arg;
27 if( config->defaultDtd ) {
28 if(!strcmp(config->defaultDtd,"NULL"))
29 config->defaultDtd = NULL;
35 static const char* xmlBuilderSetLocaleParam(
36 cmd_parms* params, void* cfg, const char* arg ) {
37 xmlBuilderConfig* config = ap_get_module_config(
38 params->server->module_config, &xmlbuilder_module );
39 config->localeParam = (char*) arg;
44 static const char* xmlBuilderSetPostXSL(
45 cmd_parms* params, void* cfg, const char* arg ) {
46 xmlBuilderConfig* config = ap_get_module_config(
47 params->server->module_config, &xmlbuilder_module );
48 config->postXSL = xsltParseStylesheetFile((xmlChar*) arg);
49 if( config->postXSL == NULL )
50 apacheDebug("Unable to parse postXSL stylesheet: %s. No postXSL will be performed", arg);
54 static const command_rec xmlBuilderCommands[] = {
55 AP_INIT_TAKE1( MODXMLB_CONFIG_LOCALE,
56 xmlBuilderSetDefaultLocale, NULL, ACCESS_CONF, "Default Locale"),
57 AP_INIT_TAKE1( MODXMLB_CONFIG_BASE_DIR,
58 xmlBuilderSetBaseDir, NULL, ACCESS_CONF, "Base Directory"),
59 AP_INIT_TAKE1( MODXMLB_CONFIG_POST_XSL,
60 xmlBuilderSetPostXSL, NULL, ACCESS_CONF, "Post XSL"),
61 AP_INIT_TAKE1( MODXMLB_CONFIG_DEFAULT_DTD,
62 xmlBuilderSetDefaultDtd, NULL, ACCESS_CONF, "Default DTD"),
63 AP_INIT_TAKE1( MODXMLB_CONFIG_LOCALE_PARAM,
64 xmlBuilderSetLocaleParam, NULL, ACCESS_CONF, "Default DTD"),
68 static void* xmlBuilderCreateConfig( apr_pool_t* p, server_rec* s ) {
69 xmlBuilderConfig* config =
70 (xmlBuilderConfig*) apr_palloc( p, sizeof(xmlBuilderConfig) );
71 config->baseDir = MODXMLB_DEFAULT_BASE_DIR;
72 config->defaultLocale = MODXMLB_DEFAULT_LOCALE;
73 config->defaultDtd = NULL;
74 config->postXSL = NULL;
75 config->localeParam = MODXMLB_DEFAULT_LOCALE_PARAM;
76 return (void*) config;
80 /* Child Init handler ----------------------------------------------------------- */
81 static void xmlBuilderChildInit( apr_pool_t *p, server_rec *s ) {
84 static int xmlBuilderHandler( request_rec* r ) {
86 if( strcmp(r->handler, MODULE_NAME ) ) return DECLINED;
88 xmlBuilderConfig* config = ap_get_module_config(
89 r->server->module_config, &xmlbuilder_module );
91 r->allowed |= (AP_METHOD_BIT << M_GET);
92 r->allowed |= (AP_METHOD_BIT << M_POST);
93 ap_set_content_type(r, "text/html; charset=utf-8");
94 //ap_table_set(r->headers_out, "Cache-Control", "max-age=15552000");
96 char* dates = apr_pcalloc(r->pool, MAX_STRING_LEN);
97 apr_rfc822_date(dates, apr_time_now() + 604800000000); /* cache for one week */
98 ap_table_set(r->headers_out, "Expires", dates);
100 apr_rfc822_date(dates, apr_time_now()); /* cache for one week */
101 ap_table_set(r->headers_out, "Date", dates);
105 char expire_hdr[APR_RFC822_DATE_LEN];
106 apr_rfc822_date(expire_hdr, (apr_time_t)(time(NULL) + 15552000));
107 ap_table_set(r->headers_out, "Expires", expire_hdr);
110 string_array* params = apacheParseParms(r);
111 char* locale = apacheGetFirstParamValue(params, config->localeParam);
112 if(locale) __xmlBuilderDynamicLocale = locale;
113 char* XMLFile = r->filename;
115 xmlDocPtr doc = xmlBuilderProcessFile( XMLFile, config );
116 if(!doc) return apacheError( "Unable to parse XML file %s", XMLFile );
118 /* apply the post XSL */
119 if(config->postXSL) {
121 newdoc = xsltApplyStylesheet(config->postXSL, doc, NULL );
124 apacheDebug("Error applying postXSL... skipping.");
131 char* docXML = xmlDocToString( doc, 1 );
136 xmlCleanupCharEncodingHandlers();
143 /* frees the collected DTD's */
144 static void __xmlBuilderFreeDtdHash( char* key, void* item ) {
150 xmlDocPtr xmlBuilderProcessFile( char* filename, xmlBuilderConfig* config ) {
152 apacheError( "No XML file provided" ); return NULL; }
154 xmlBuilderContext context;
155 context.config = config;
156 context.doc = xmlNewDoc( BAD_CAST "1.0" );
157 context.dtdHash = osrfNewHash();
158 context.entHash = osrfNewHash();
159 context.nodeList = osrfNewList();
160 context.xmlError = 0;
161 context.xmlFile = filename;
162 context.dtdHash->freeItem = &__xmlBuilderFreeDtdHash;
164 /* pre-parse the default dtd if defined */
165 if( config->defaultDtd )
166 xmlBuilderAddDtd( config->defaultDtd, &context );
168 xmlParserCtxtPtr parserCtx;
170 parserCtx = xmlCreatePushParserCtxt(xmlBuilderSaxHandler, &context, "", 0, NULL);
171 xmlCtxtReadFile( parserCtx, filename, NULL, XML_PARSE_RECOVER );
173 xmlFreeParserCtxt( parserCtx );
174 osrfListFree(context.nodeList);
175 osrfHashFree(context.entHash);
176 osrfHashFree(context.dtdHash);
181 void xmlBuilderStartElement( void* context, const xmlChar *name, const xmlChar **atts ) {
182 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
184 xmlNodePtr node = NULL;
186 /* process xincludes as a sub-doc */
187 if( !strcmp( name, "xi:include" ) ) {
189 char* href = strdup(xmlSaxAttr( atts, "href" ));
192 /* find the relative path for the xinclude */
194 int len = strlen(ctx->xmlFile) + strlen(href) + 1;
197 strcpy( buf, ctx->xmlFile );
199 for( i = strlen(buf); i != 0; i-- ) {
200 if( buf[i] == '/' ) break;
209 xmlDocPtr subDoc = xmlBuilderProcessFile( href, ctx->config );
210 node = xmlDocGetRootElement( subDoc );
214 apacheError("Unable to parse xinclude: %s", href );
221 node = xmlNewNode(NULL, name);
222 xmlAddAttrs( node, atts );
226 xmlNodePtr parent = osrfListGetIndex(
227 ctx->nodeList, ctx->nodeList->size - 1 );
229 if( parent ) xmlAddChild( parent, node );
230 else xmlDocSetRootElement(ctx->doc, node);
232 osrfListPush( ctx->nodeList, node );
235 void xmlBuilderEndElement( void* context, const xmlChar* name ) {
236 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
237 osrfListPop( ctx->nodeList );
241 void xmlBuilderHandleCharacter(void* context, const xmlChar *ch, int len) {
242 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
243 xmlNodePtr node = osrfListGetIndex(
244 ctx->nodeList, ctx->nodeList->size - 1 );
247 xmlNodePtr txt = xmlNewTextLen(ch, len);
248 xmlAddChild( node, txt );
254 void xmlBuilderParseError( void* context, const char* msg, ... ) {
255 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
256 VA_LIST_TO_STRING(msg);
257 apacheDebug( "Parser Error Occurred: %s", VA_BUF);
262 xmlEntityPtr xmlBuilderGetEntity( void* context, const xmlChar* name ) {
263 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
264 //apacheDebug("Replacing entity: %s", name);
265 return osrfHashGet( ctx->entHash, name );
269 void xmlBuilderExtSubset( void* blob,
270 const xmlChar* name, const xmlChar* extId, const xmlChar* sysId ) {
272 xmlBuilderContext* context = (xmlBuilderContext*) blob;
273 if( context->config->defaultDtd ) {
274 apacheDebug("Ignoring DTD [%s] because default DTD is set...", sysId);
278 xmlBuilderAddDtd( sysId, context );
282 void xmlBuilderProcInstruction(
283 void* blob, const xmlChar* name, const xmlChar* data ) {
284 xmlBuilderContext* ctx = (xmlBuilderContext*) blob;
285 //xmlNodePtr node = xmlNewDocPI( ctx->doc, name, data );
286 xmlNodePtr pi = xmlNewDocPI( ctx->doc, name, data );
287 xmlAddChild( (xmlNodePtr) ctx->doc, pi );
292 void xmlBuilderAddDtd( const char* sysId, xmlBuilderContext* context ) {
295 if( osrfHashGet( context->dtdHash, sysId ) ) return; /* already parsed this hash */
297 //apacheDebug("Adding new DTD file to the entity hash: %s", sysId);
299 /* use the dynamic locale if defined... default locale instead */
301 if(__xmlBuilderDynamicLocale) locale = __xmlBuilderDynamicLocale;
302 else locale = context->config->defaultLocale;
304 /* determine the path to the DTD file and load it */
305 int len = strlen(context->config->baseDir) + strlen(locale) + strlen(sysId) + 4;
306 char buf[len]; bzero(buf,len);
307 snprintf( buf, len, "%s/%s/%s", context->config->baseDir, locale, sysId );
309 xmlDtdPtr dtd = xmlParseDTD(NULL, buf);
312 /* cycle through entities and push them into the entity hash */
313 xmlNodePtr node = dtd->children;
315 if( node->type == XML_ENTITY_DECL ) { /* shove the entities into the hash */
316 xmlEntityPtr ent = (xmlEntityPtr) node;
317 osrfHashSet( context->entHash, ent, (char*) ent->name );
322 /* cache the DTD so we can free it later */
323 osrfHashSet( context->dtdHash, dtd, sysId );
327 /* ------------------------------------------------------------------------ */
329 /* register callbacks */
330 static void xmlBuilderRegisterHooks (apr_pool_t *p) {
331 ap_hook_handler(xmlBuilderHandler, NULL, NULL, APR_HOOK_MIDDLE);
332 ap_hook_child_init(xmlBuilderChildInit,NULL,NULL,APR_HOOK_MIDDLE);
336 /* finally, flesh the module */
337 module AP_MODULE_DECLARE_DATA xmlbuilder_module = {
338 STANDARD20_MODULE_STUFF,
341 xmlBuilderCreateConfig,
344 xmlBuilderRegisterHooks,