25be733479f6049f35cca6ce8769d76202bf666c
[Evergreen.git] / Open-ILS / src / apachemods / mod_xmlbuilder.c
1 #include "mod_xmlbuilder.h"
2
3 char* __xmlBuilderDynamicLocale = NULL;
4
5
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;
11         return NULL;
12 }
13
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;
19         return NULL;
20 }
21
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         return NULL;
28 }
29
30
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;
36         return NULL;
37 }
38
39
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);      
47         return NULL;
48 }
49
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"),
61         {NULL}
62 };
63
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;
73 }
74
75
76 /* Child Init handler  ----------------------------------------------------------- */
77 static void xmlBuilderChildInit( apr_pool_t *p, server_rec *s ) {
78 }
79
80 static int xmlBuilderHandler( request_rec* r ) {
81
82         if( strcmp(r->handler, MODULE_NAME ) ) return DECLINED;
83
84         xmlBuilderConfig* config = ap_get_module_config( 
85                         r->server->module_config, &xmlbuilder_module );
86         
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");
90
91         string_array* params = apacheParseParms(r);
92         char* locale = apacheGetFirstParamValue(params, config->localeParam);
93         if(locale) __xmlBuilderDynamicLocale = locale;
94         char* XMLFile = r->filename;
95
96         apacheDebug("Processing file %s", XMLFile);
97         xmlDocPtr doc = xmlBuilderProcessFile( XMLFile, config );
98         if(!doc) return apacheError( "Unable to parse XML file %s", XMLFile );
99
100         /* apply the post XSL */
101         if(config->postXSL) {
102                 xmlDocPtr newdoc;
103                 newdoc = xsltApplyStylesheet(config->postXSL, doc, NULL );
104
105                 if(newdoc == NULL) {
106                         apacheDebug("Error applying postXSL... skipping.");
107                 } else {
108                         xmlFreeDoc(doc);
109                         doc = newdoc;
110                 }
111         }
112
113         char* docXML = xmlDocToString( doc, 1 );
114         ap_rputs(docXML, r);
115         free(docXML);
116         xmlFreeDoc( doc );
117         doc = NULL;
118         xmlCleanupCharEncodingHandlers();
119         xmlCleanupParser();
120
121         return OK;
122 }
123
124
125 /* frees the collected DTD's */
126 static void __xmlBuilderFreeDtdHash( char* key, void* item ) {
127         if(!item) return;
128         xmlFreeDtd( item );
129 }
130
131
132 xmlDocPtr xmlBuilderProcessFile( char* filename, xmlBuilderConfig* config ) {
133         if(!filename) { 
134                 apacheError( "No XML file provided" ); return NULL; }
135
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;
145
146         /* pre-parse the default dtd if defined */
147         if( config->defaultDtd ) 
148                 xmlBuilderAddDtd( config->defaultDtd, &context );
149
150         xmlParserCtxtPtr parserCtx;
151
152         parserCtx = xmlCreatePushParserCtxt(xmlBuilderSaxHandler, &context, "", 0, NULL);
153         xmlCtxtReadFile( parserCtx, filename, NULL, XML_PARSE_RECOVER );
154
155         xmlFreeParserCtxt( parserCtx );
156         osrfListFree(context.nodeList);
157         osrfHashFree(context.entHash);
158         osrfHashFree(context.dtdHash);
159         return context.doc;
160 }
161
162
163 void xmlBuilderStartElement( void* context, const xmlChar *name, const xmlChar **atts ) {
164         apacheDebug( "Starting element: %s", name );
165         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
166
167         xmlNodePtr node = NULL;
168
169         /* process xincludes as a sub-doc */
170         if( !strcmp( name, "xi:include" ) ) { 
171
172                 char* href = strdup(xmlSaxAttr( atts, "href" ));
173                 if(href) {
174
175                         /* find the relative path for the xinclude */
176                         if(href[0] != '/') {
177                                 int len = strlen(ctx->xmlFile) + strlen(href) + 1;
178                                 char buf[len];
179                                 bzero(buf, len);
180                                 strcpy( buf, ctx->xmlFile );
181                                 int i;
182                                 for( i = strlen(buf); i != 0; i-- ) {
183                                         if( buf[i] == '/' ) break;
184                                         buf[i] = '\0';
185                                 }
186                                 strcat( buf, href );
187                                 free(href);
188                                 href = strdup(buf);
189                         }
190
191
192                         apacheDebug( "Processing xinclude %s", href );
193                         xmlDocPtr subDoc = xmlBuilderProcessFile( href, ctx->config );
194                         node = xmlDocGetRootElement( subDoc );
195                 }
196
197                 if(!node) {
198                         apacheError("Unable to parse xinclude: %s", href );
199                         return;
200                 }
201
202         } else {
203                 node = xmlNewNode(NULL, name);
204                 xmlAddAttrs( node, atts );
205         }
206
207
208         xmlNodePtr parent = osrfListGetIndex( 
209                         ctx->nodeList, ctx->nodeList->size - 1 );
210
211         if( parent ) xmlAddChild( parent, node );
212         else xmlDocSetRootElement(ctx->doc, node);
213         
214         osrfListPush( ctx->nodeList, node );
215 }
216
217 void xmlBuilderEndElement( void* context, const xmlChar* name ) {
218         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
219         osrfListPop( ctx->nodeList );
220 }
221
222
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 );
227
228         if(node) {
229                 xmlNodePtr txt = xmlNewTextLen(ch, len);
230                 xmlAddChild( node, txt );
231         }
232
233 }
234
235
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);
240         ctx->xmlError = 1;
241 }
242
243
244 xmlEntityPtr xmlBuilderGetEntity( void* context, const xmlChar* name ) {
245         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
246         return osrfHashGet( ctx->entHash, name );
247 }
248
249
250 void xmlBuilderExtSubset( void* blob, 
251                 const xmlChar* name, const xmlChar* extId, const xmlChar* sysId ) {
252
253         xmlBuilderContext* context = (xmlBuilderContext*) blob;
254         if( context->config->defaultDtd ) return; /* only use the default if defined */
255         xmlBuilderAddDtd( sysId, context );
256 }
257
258
259
260 void xmlBuilderAddDtd( const char* sysId, xmlBuilderContext* context ) {
261
262         if(!sysId) return;
263         if( osrfHashGet( context->dtdHash, sysId ) ) return; /* already parsed this hash */
264
265         /* use the dynamic locale if defined... default locale instead */
266         char* locale;
267         if(__xmlBuilderDynamicLocale) locale = __xmlBuilderDynamicLocale;
268         else locale = context->config->defaultLocale;
269
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 );
274
275         apacheDebug("Parsing DTD file %s", buf);
276         xmlDtdPtr dtd = xmlParseDTD(NULL, buf);
277
278
279         /* cycle through entities and push them into the entity hash */
280         xmlNodePtr node = dtd->children;
281         while( node ) {
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 );
285                 }
286                 node = node->next;
287         }
288
289         /* cache the DTD so we can free it later */
290         osrfHashSet( context->dtdHash, dtd, sysId );
291 }
292
293
294 /* ------------------------------------------------------------------------ */
295
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);
300 }
301
302
303 /* finally, flesh the module */
304 module AP_MODULE_DECLARE_DATA xmlbuilder_module = {
305         STANDARD20_MODULE_STUFF,
306         NULL,
307         NULL,
308         xmlBuilderCreateConfig,
309         NULL,
310         xmlBuilderCommands,
311         xmlBuilderRegisterHooks,
312 };
313
314
315
316
317
318
319 /*
320 char* get_dtd_lang_file(string_array* params, char* default_locale, char* locale_dir) {
321
322         char* locale = apacheGetFirstParamValue(params, PARAM_LOCALE);
323         if(!locale) locale = default_locale;
324         if(!locale) return NULL;
325
326         int len = strlen(LANG_DTD) + strlen(locale) + strlen(locale_dir) + 1;
327         char dtdfile[len];
328         bzero(dtdfile, len);
329
330         if(locale)
331                 sprintf(dtdfile, "%s/%s/%s",  locale_dir, locale, LANG_DTD );
332
333         return strdup(dtdfile);
334 }
335 */
336
337