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;
31 static const char* xmlBuilderSetLocaleParam(
32 cmd_parms* params, void* cfg, const char* arg ) {
33 xmlBuilderConfig* config = ap_get_module_config(
34 params->server->module_config, &xmlbuilder_module );
35 config->localeParam = (char*) arg;
40 static const char* xmlBuilderSetPostXSL(
41 cmd_parms* params, void* cfg, const char* arg ) {
42 xmlBuilderConfig* config = ap_get_module_config(
43 params->server->module_config, &xmlbuilder_module );
44 config->postXSL = xsltParseStylesheetFile((xmlChar*) arg);
45 if( config->postXSL == NULL )
46 apacheDebug("Unable to parse postXSL stylesheet: %s. No postXSL will be performed", arg);
50 static const command_rec xmlBuilderCommands[] = {
51 AP_INIT_TAKE1( MODXMLB_CONFIG_LOCALE,
52 xmlBuilderSetDefaultLocale, NULL, ACCESS_CONF, "Default Locale"),
53 AP_INIT_TAKE1( MODXMLB_CONFIG_BASE_DIR,
54 xmlBuilderSetBaseDir, NULL, ACCESS_CONF, "Base Directory"),
55 AP_INIT_TAKE1( MODXMLB_CONFIG_POST_XSL,
56 xmlBuilderSetPostXSL, NULL, ACCESS_CONF, "Post XSL"),
57 AP_INIT_TAKE1( MODXMLB_CONFIG_DEFAULT_DTD,
58 xmlBuilderSetDefaultDtd, NULL, ACCESS_CONF, "Default DTD"),
59 AP_INIT_TAKE1( MODXMLB_CONFIG_LOCALE_PARAM,
60 xmlBuilderSetLocaleParam, NULL, ACCESS_CONF, "Default DTD"),
64 static void* xmlBuilderCreateConfig( apr_pool_t* p, server_rec* s ) {
65 xmlBuilderConfig* config =
66 (xmlBuilderConfig*) apr_palloc( p, sizeof(xmlBuilderConfig) );
67 config->baseDir = MODXMLB_DEFAULT_BASE_DIR;
68 config->defaultLocale = MODXMLB_DEFAULT_LOCALE;
69 config->defaultDtd = NULL;
70 config->postXSL = NULL;
71 config->localeParam = MODXMLB_DEFAULT_LOCALE_PARAM;
72 return (void*) config;
76 /* Child Init handler ----------------------------------------------------------- */
77 static void xmlBuilderChildInit( apr_pool_t *p, server_rec *s ) {
80 static int xmlBuilderHandler( request_rec* r ) {
82 if( strcmp(r->handler, MODULE_NAME ) ) return DECLINED;
84 xmlBuilderConfig* config = ap_get_module_config(
85 r->server->module_config, &xmlbuilder_module );
87 r->allowed |= (AP_METHOD_BIT << M_GET);
88 r->allowed |= (AP_METHOD_BIT << M_POST);
89 ap_set_content_type(r, "text/html; charset=utf-8");
91 string_array* params = apacheParseParms(r);
92 char* locale = apacheGetFirstParamValue(params, config->localeParam);
93 if(locale) __xmlBuilderDynamicLocale = locale;
94 char* XMLFile = r->filename;
96 apacheDebug("Processing file %s", XMLFile);
97 xmlDocPtr doc = xmlBuilderProcessFile( XMLFile, config );
98 if(!doc) return apacheError( "Unable to parse XML file %s", XMLFile );
100 /* apply the post XSL */
101 if(config->postXSL) {
103 newdoc = xsltApplyStylesheet(config->postXSL, doc, NULL );
106 apacheDebug("Error applying postXSL... skipping.");
113 char* docXML = xmlDocToString( doc, 1 );
118 xmlCleanupCharEncodingHandlers();
125 /* frees the collected DTD's */
126 static void __xmlBuilderFreeDtdHash( char* key, void* item ) {
132 xmlDocPtr xmlBuilderProcessFile( char* filename, xmlBuilderConfig* config ) {
134 apacheError( "No XML file provided" ); return NULL; }
136 xmlBuilderContext context;
137 context.config = config;
138 context.doc = xmlNewDoc( BAD_CAST "1.0" );
139 context.dtdHash = osrfNewHash();
140 context.entHash = osrfNewHash();
141 context.nodeList = osrfNewList();
142 context.xmlError = 0;
143 context.xmlFile = filename;
144 context.dtdHash->freeItem = &__xmlBuilderFreeDtdHash;
146 /* pre-parse the default dtd if defined */
147 if( config->defaultDtd )
148 xmlBuilderAddDtd( config->defaultDtd, &context );
150 xmlParserCtxtPtr parserCtx;
152 parserCtx = xmlCreatePushParserCtxt(xmlBuilderSaxHandler, &context, "", 0, NULL);
153 xmlCtxtReadFile( parserCtx, filename, NULL, XML_PARSE_RECOVER );
155 xmlFreeParserCtxt( parserCtx );
156 osrfListFree(context.nodeList);
157 osrfHashFree(context.entHash);
158 osrfHashFree(context.dtdHash);
163 void xmlBuilderStartElement( void* context, const xmlChar *name, const xmlChar **atts ) {
164 apacheDebug( "Starting element: %s", name );
165 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
167 xmlNodePtr node = NULL;
169 /* process xincludes as a sub-doc */
170 if( !strcmp( name, "xi:include" ) ) {
172 char* href = strdup(xmlSaxAttr( atts, "href" ));
175 /* find the relative path for the xinclude */
177 int len = strlen(ctx->xmlFile) + strlen(href) + 1;
180 strcpy( buf, ctx->xmlFile );
182 for( i = strlen(buf); i != 0; i-- ) {
183 if( buf[i] == '/' ) break;
192 apacheDebug( "Processing xinclude %s", href );
193 xmlDocPtr subDoc = xmlBuilderProcessFile( href, ctx->config );
194 node = xmlDocGetRootElement( subDoc );
198 apacheError("Unable to parse xinclude: %s", href );
203 node = xmlNewNode(NULL, name);
204 xmlAddAttrs( node, atts );
208 xmlNodePtr parent = osrfListGetIndex(
209 ctx->nodeList, ctx->nodeList->size - 1 );
211 if( parent ) xmlAddChild( parent, node );
212 else xmlDocSetRootElement(ctx->doc, node);
214 osrfListPush( ctx->nodeList, node );
217 void xmlBuilderEndElement( void* context, const xmlChar* name ) {
218 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
219 osrfListPop( ctx->nodeList );
223 void xmlBuilderHandleCharacter(void* context, const xmlChar *ch, int len) {
224 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
225 xmlNodePtr node = osrfListGetIndex(
226 ctx->nodeList, ctx->nodeList->size - 1 );
229 xmlNodePtr txt = xmlNewTextLen(ch, len);
230 xmlAddChild( node, txt );
236 void xmlBuilderParseError( void* context, const char* msg, ... ) {
237 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
238 VA_LIST_TO_STRING(msg);
239 apacheDebug( "Parser Error Occurred: %s", VA_BUF);
244 xmlEntityPtr xmlBuilderGetEntity( void* context, const xmlChar* name ) {
245 xmlBuilderContext* ctx = (xmlBuilderContext*) context;
246 return osrfHashGet( ctx->entHash, name );
250 void xmlBuilderExtSubset( void* blob,
251 const xmlChar* name, const xmlChar* extId, const xmlChar* sysId ) {
253 xmlBuilderContext* context = (xmlBuilderContext*) blob;
254 if( context->config->defaultDtd ) return; /* only use the default if defined */
255 xmlBuilderAddDtd( sysId, context );
260 void xmlBuilderAddDtd( const char* sysId, xmlBuilderContext* context ) {
263 if( osrfHashGet( context->dtdHash, sysId ) ) return; /* already parsed this hash */
265 /* use the dynamic locale if defined... default locale instead */
267 if(__xmlBuilderDynamicLocale) locale = __xmlBuilderDynamicLocale;
268 else locale = context->config->defaultLocale;
270 /* determine the path to the DTD file and load it */
271 int len = strlen(context->config->baseDir) + strlen(locale) + strlen(sysId) + 4;
272 char buf[len]; bzero(buf,len);
273 snprintf( buf, len, "%s/%s/%s", context->config->baseDir, locale, sysId );
275 apacheDebug("Parsing DTD file %s", buf);
276 xmlDtdPtr dtd = xmlParseDTD(NULL, buf);
279 /* cycle through entities and push them into the entity hash */
280 xmlNodePtr node = dtd->children;
282 if( node->type == XML_ENTITY_DECL ) { /* shove the entities into the hash */
283 xmlEntityPtr ent = (xmlEntityPtr) node;
284 osrfHashSet( context->entHash, ent, (char*) ent->name );
289 /* cache the DTD so we can free it later */
290 osrfHashSet( context->dtdHash, dtd, sysId );
294 /* ------------------------------------------------------------------------ */
296 /* register callbacks */
297 static void xmlBuilderRegisterHooks (apr_pool_t *p) {
298 ap_hook_handler(xmlBuilderHandler, NULL, NULL, APR_HOOK_MIDDLE);
299 ap_hook_child_init(xmlBuilderChildInit,NULL,NULL,APR_HOOK_MIDDLE);
303 /* finally, flesh the module */
304 module AP_MODULE_DECLARE_DATA xmlbuilder_module = {
305 STANDARD20_MODULE_STUFF,
308 xmlBuilderCreateConfig,
311 xmlBuilderRegisterHooks,
320 char* get_dtd_lang_file(string_array* params, char* default_locale, char* locale_dir) {
322 char* locale = apacheGetFirstParamValue(params, PARAM_LOCALE);
323 if(!locale) locale = default_locale;
324 if(!locale) return NULL;
326 int len = strlen(LANG_DTD) + strlen(locale) + strlen(locale_dir) + 1;
331 sprintf(dtdfile, "%s/%s/%s", locale_dir, locale, LANG_DTD );
333 return strdup(dtdfile);