1 #include "mod_xmlbuilder.h"
3 char* __xmlBuilderDynamicLocale = NULL;
4 request_rec* currentRec = NULL;
7 /* set the base DTD directory */
8 static const char* xmlBuilderSetBaseDir(cmd_parms *params, void *cfg, const char *arg) {
9 xmlBuilderConfig* config = ap_get_module_config(
10 params->server->module_config, &xmlbuilder_module );
11 config->baseDir = (char*) arg;
15 static const char* xmlBuilderSetDefaultLocale(
16 cmd_parms* params, void* cfg, const char* arg ) {
17 xmlBuilderConfig* config = ap_get_module_config(
18 params->server->module_config, &xmlbuilder_module );
19 config->defaultLocale = (char*) arg;
23 static const char* xmlBuilderSetDefaultDtd(
24 cmd_parms* params, void* cfg, const char* arg ) {
25 xmlBuilderConfig* config = ap_get_module_config(
26 params->server->module_config, &xmlbuilder_module );
27 config->defaultDtd = (char*) arg;
28 if( config->defaultDtd ) {
29 if(!strcmp(config->defaultDtd,"NULL"))
30 config->defaultDtd = NULL;
36 static const char* xmlBuilderSetLocaleParam(
37 cmd_parms* params, void* cfg, const char* arg ) {
38 xmlBuilderConfig* config = ap_get_module_config(
39 params->server->module_config, &xmlbuilder_module );
40 config->localeParam = (char*) arg;
45 static const char* xmlBuilderSetPostXSL(
46 cmd_parms* params, void* cfg, const char* arg ) {
47 xmlBuilderConfig* config = ap_get_module_config(
48 params->server->module_config, &xmlbuilder_module );
49 config->postXSL = xsltParseStylesheetFile((xmlChar*) arg);
50 if( config->postXSL == NULL )
51 apacheDebug("Unable to parse postXSL stylesheet: %s. No postXSL will be performed", arg);
55 static const char* xmlBuilderSetContentType(
56 cmd_parms* params, void* cfg, const char* arg ) {
57 xmlBuilderConfig* config = ap_get_module_config(
58 params->server->module_config, &xmlbuilder_module );
59 config->contentType = (char*) arg;
63 // ACCESS_CONF - OR_ALL ?
64 static const command_rec xmlBuilderCommands[] = {
65 AP_INIT_TAKE1( MODXMLB_CONFIG_LOCALE,
66 xmlBuilderSetDefaultLocale, NULL, RSRC_CONF, "Default Locale"),
67 AP_INIT_TAKE1( MODXMLB_CONFIG_BASE_DIR,
68 xmlBuilderSetBaseDir, NULL, RSRC_CONF, "Base Directory"),
69 AP_INIT_TAKE1( MODXMLB_CONFIG_POST_XSL,
70 xmlBuilderSetPostXSL, NULL, RSRC_CONF, "Post XSL"),
71 AP_INIT_TAKE1( MODXMLB_CONFIG_DEFAULT_DTD,
72 xmlBuilderSetDefaultDtd, NULL, RSRC_CONF, "Default DTD"),
73 AP_INIT_TAKE1( MODXMLB_CONFIG_LOCALE_PARAM,
74 xmlBuilderSetLocaleParam, NULL, RSRC_CONF, "Locale URL param name"),
75 AP_INIT_TAKE1( MODXMLB_CONFIG_CONTENT_TYPE,
76 xmlBuilderSetContentType, NULL, RSRC_CONF, "Content Type"),
80 static void* xmlBuilderCreateConfig( apr_pool_t* p, server_rec* s ) {
81 xmlBuilderConfig* config =
82 (xmlBuilderConfig*) apr_palloc( p, sizeof(xmlBuilderConfig) );
83 config->baseDir = MODXMLB_DEFAULT_BASE_DIR;
84 config->defaultLocale = MODXMLB_DEFAULT_LOCALE;
85 config->defaultDtd = NULL;
86 config->postXSL = NULL;
87 config->contentType = NULL;
88 config->localeParam = MODXMLB_DEFAULT_LOCALE_PARAM;
89 return (void*) config;
93 /* Child Init handler ----------------------------------------------------------- */
94 static void xmlBuilderChildInit( apr_pool_t *p, server_rec *s ) {
97 static int xmlBuilderHandler( request_rec* r ) {
99 if( strcmp(r->handler, MODULE_NAME ) ) return DECLINED;
102 xmlBuilderConfig* config = ap_get_module_config(
103 r->server->module_config, &xmlbuilder_module );
106 xmlBuilderConfig* config = ap_get_module_config(
107 r->per_dir_config, &xmlbuilder_module );
111 ap_log_rerror( APLOG_MARK, APLOG_ERR, 0, currentRec,
112 "config is nulll...");
113 return HTTP_INTERNAL_SERVER_ERROR;
116 r->allowed |= (AP_METHOD_BIT << M_GET);
117 r->allowed |= (AP_METHOD_BIT << M_POST);
118 char* ct = config->contentType;
119 if(!config->contentType) ct = "text/html; charset=utf-8";
120 ap_set_content_type(r, ct);
122 //ap_table_set(r->headers_out, "Cache-Control", "max-age=15552000");
124 char* dates = apr_pcalloc(r->pool, MAX_STRING_LEN);
125 apr_rfc822_date(dates, apr_time_now() + 604800000000); /* cache for one week */
126 ap_table_set(r->headers_out, "Expires", dates);
128 apr_rfc822_date(dates, apr_time_now()); /* cache for one week */
129 ap_table_set(r->headers_out, "Date", dates);
133 char expire_hdr[APR_RFC822_DATE_LEN];
134 apr_rfc822_date(expire_hdr, (apr_time_t)(time(NULL) + 15552000));
135 ap_table_set(r->headers_out, "Expires", expire_hdr);
138 string_array* params = apacheParseParms(r);
139 char* locale = apacheGetFirstParamValue(params, config->localeParam);
140 if(locale) __xmlBuilderDynamicLocale = locale;
141 char* XMLFile = r->filename;
143 xmlDocPtr doc = xmlBuilderProcessFile( XMLFile, config );
144 if(!doc) return apacheError( "Unable to parse XML file %s", XMLFile );
146 /* apply the post XSL */
147 if(config->postXSL) {
149 newdoc = xsltApplyStylesheet(config->postXSL, doc, NULL );
152 apacheDebug("Error applying postXSL... skipping.");
159 char* docXML = xmlDocToString( doc, 1 );
160 //apacheDebug("DOC:\n%s\n%s", docXML);
165 //xmlCleanupCharEncodingHandlers();
166 //xmlCleanupParser();
172 /* frees the collected DTD's */
173 static void __xmlBuilderFreeDtdHash( char* key, void* item ) {
179 xmlDocPtr xmlBuilderProcessFile( char* filename, xmlBuilderConfig* config ) {
181 apacheError( "No XML file provided" ); return NULL; }
183 xmlBuilderContext context;
184 context.config = config;
185 context.doc = xmlNewDoc( BAD_CAST "1.0" );
186 context.dtdHash = osrfNewHash();
187 context.entHash = osrfNewHash();
188 context.nodeList = osrfNewList();
189 context.xmlError = 0;
190 context.xmlFile = filename;
191 context.dtdHash->freeItem = &__xmlBuilderFreeDtdHash;
195 ap_log_rerror( APLOG_MARK, APLOG_DEBUG, 0, currentRec,
196 "xmlBuilderProcessFile() Options: "
197 "XMLBuilderDefaultLocale : %s | XMLBuilderBaseDir : %s | "
198 "XMLBuilderDefaultDTD : %s | XMLBuilderLocaleParam : %s",
199 config->defaultLocale, config->baseDir, config->defaultDtd, config->localeParam );
202 /* pre-parse the default dtd if defined */
203 if( config->defaultDtd )
204 xmlBuilderAddDtd( config->defaultDtd, &context );
206 xmlParserCtxtPtr parserCtx;
208 parserCtx = xmlCreatePushParserCtxt(xmlBuilderSaxHandler, &context, "", 0, NULL);
209 xmlCtxtReadFile( parserCtx, filename, NULL, XML_PARSE_RECOVER );
211 xmlFreeParserCtxt( parserCtx );
212 osrfListFree(context.nodeList);
213 osrfHashFree(context.entHash);
214 osrfHashFree(context.dtdHash);
219 void xmlBuilderStartElement( void* context, const xmlChar *name, const xmlChar **atts ) {
220 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
222 xmlNodePtr node = NULL;
224 /* process xincludes as a sub-doc */
225 if( !strcmp( name, "xi:include" ) ) {
227 char* href = strdup(xmlSaxAttr( atts, "href" ));
230 /* find the relative path for the xinclude */
232 int len = strlen(ctx->xmlFile) + strlen(href) + 1;
235 strcpy( buf, ctx->xmlFile );
237 for( i = strlen(buf); i != 0; i-- ) {
238 if( buf[i] == '/' ) break;
247 xmlDocPtr subDoc = xmlBuilderProcessFile( href, ctx->config );
248 node = xmlDocGetRootElement( subDoc );
252 apacheError("Unable to parse xinclude: %s", href );
259 node = xmlNewNode(NULL, name);
260 xmlBuilderAddAtts( ctx, node, atts );
264 xmlNodePtr parent = osrfListGetIndex(
265 ctx->nodeList, ctx->nodeList->size - 1 );
267 if( parent ) xmlAddChild( parent, node );
268 else xmlDocSetRootElement(ctx->doc, node);
270 osrfListPush( ctx->nodeList, node );
274 void xmlBuilderAddAtts( xmlBuilderContext* ctx, xmlNodePtr node, const xmlChar** atts ) {
275 if(!(ctx && node && atts)) return;
278 for(i = 0; (atts[i] != NULL); i++) {
282 const xmlChar* name = atts[i];
283 const xmlChar* prop = atts[i+1];
284 int nl = strlen(prop);
287 if( prop[0] == '&' && prop[nl-1] == ';' ) { /* replace the entity if we are one */
290 strncat(buf, prop + 1, nl - 2);
291 xmlEntityPtr ent = osrfHashGet( ctx->entHash, buf );
292 if(ent && ent->content) _prop = ent->content;
293 } else { _prop = (char*) prop; }
295 xmlSetProp( node, name, _prop );
301 void xmlBuilderEndElement( void* context, const xmlChar* name ) {
302 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
303 osrfListPop( ctx->nodeList );
307 void xmlBuilderHandleCharacter(void* context, const xmlChar *ch, int len) {
308 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
309 xmlNodePtr node = osrfListGetIndex(
310 ctx->nodeList, ctx->nodeList->size - 1 );
313 xmlNodePtr txt = xmlNewTextLen(ch, len);
314 xmlAddChild( node, txt );
320 void xmlBuilderParseError( void* context, const char* msg, ... ) {
321 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
322 VA_LIST_TO_STRING(msg);
323 apacheDebug( "Parser Error Occurred: %s", VA_BUF);
328 xmlEntityPtr xmlBuilderGetEntity( void* context, const xmlChar* name ) {
329 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
330 xmlEntityPtr ent = osrfHashGet( ctx->entHash, name );
335 void xmlBuilderExtSubset( void* blob,
336 const xmlChar* name, const xmlChar* extId, const xmlChar* sysId ) {
338 xmlBuilderContext* context = (xmlBuilderContext*) blob;
339 if( context->config->defaultDtd ) {
340 apacheDebug("Ignoring DTD [%s] because default DTD is set...", sysId);
344 xmlBuilderAddDtd( sysId, context );
348 void xmlBuilderProcInstruction(
349 void* blob, const xmlChar* name, const xmlChar* data ) {
350 xmlBuilderContext* ctx = (xmlBuilderContext*) blob;
351 //xmlNodePtr node = xmlNewDocPI( ctx->doc, name, data );
352 xmlNodePtr pi = xmlNewDocPI( ctx->doc, name, data );
353 xmlAddChild( (xmlNodePtr) ctx->doc, pi );
358 void xmlBuilderAddDtd( const char* sysId, xmlBuilderContext* context ) {
361 if( osrfHashGet( context->dtdHash, sysId ) ) return; /* already parsed this hash */
363 //apacheDebug("Adding new DTD file to the entity hash: %s", sysId);
365 /* use the dynamic locale if defined... default locale instead */
367 if(__xmlBuilderDynamicLocale) locale = __xmlBuilderDynamicLocale;
368 else locale = context->config->defaultLocale;
370 /* determine the path to the DTD file and load it */
371 int len = strlen(context->config->baseDir) + strlen(locale) + strlen(sysId) + 4;
372 char buf[len]; bzero(buf,len);
373 snprintf( buf, len, "%s/%s/%s", context->config->baseDir, locale, sysId );
375 xmlDtdPtr dtd = xmlParseDTD(NULL, buf);
378 /* cycle through entities and push them into the entity hash */
379 xmlNodePtr node = dtd->children;
381 if( node->type == XML_ENTITY_DECL ) { /* shove the entities into the hash */
382 xmlEntityPtr ent = (xmlEntityPtr) node;
383 osrfHashSet( context->entHash, ent, (char*) ent->name );
388 /* cache the DTD so we can free it later */
389 osrfHashSet( context->dtdHash, dtd, sysId );
393 /* ------------------------------------------------------------------------ */
395 /* register callbacks */
396 static void xmlBuilderRegisterHooks (apr_pool_t *p) {
397 ap_hook_handler(xmlBuilderHandler, NULL, NULL, APR_HOOK_MIDDLE);
398 ap_hook_child_init(xmlBuilderChildInit,NULL,NULL,APR_HOOK_MIDDLE);
402 /* finally, flesh the module */
403 module AP_MODULE_DECLARE_DATA xmlbuilder_module = {
404 STANDARD20_MODULE_STUFF,
407 xmlBuilderCreateConfig,
410 xmlBuilderRegisterHooks,