]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/apachemods/mod_xmlbuilder.c
ccf7882805c7ec3e9a2b30ab401a3cd78fbc5f05
[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         if( config->defaultDtd ) {
28                 if(!strcmp(config->defaultDtd,"NULL"))
29                         config->defaultDtd = NULL;
30         }
31         return NULL;
32 }
33
34
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;
40         return NULL;
41 }
42
43
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);      
51         return NULL;
52 }
53
54 static const char* xmlBuilderSetContentType(
55                                          cmd_parms* params, void* cfg, const char* arg ) {
56         xmlBuilderConfig* config = ap_get_module_config(
57                 params->server->module_config, &xmlbuilder_module );
58         config->contentType = (char*) arg;
59         return NULL;
60 }
61
62 static const command_rec xmlBuilderCommands[] = {
63         AP_INIT_TAKE1( MODXMLB_CONFIG_LOCALE, 
64                         xmlBuilderSetDefaultLocale, NULL, ACCESS_CONF, "Default Locale"),
65         AP_INIT_TAKE1( MODXMLB_CONFIG_BASE_DIR, 
66                         xmlBuilderSetBaseDir, NULL, ACCESS_CONF, "Base Directory"),
67         AP_INIT_TAKE1( MODXMLB_CONFIG_POST_XSL, 
68                         xmlBuilderSetPostXSL, NULL, ACCESS_CONF, "Post XSL"),
69         AP_INIT_TAKE1( MODXMLB_CONFIG_DEFAULT_DTD, 
70                         xmlBuilderSetDefaultDtd, NULL, ACCESS_CONF, "Default DTD"),
71         AP_INIT_TAKE1( MODXMLB_CONFIG_LOCALE_PARAM,
72                         xmlBuilderSetLocaleParam, NULL, ACCESS_CONF, "Default DTD"),
73         AP_INIT_TAKE1( MODXMLB_CONFIG_CONTENT_TYPE,
74                         xmlBuilderSetContentType, NULL, ACCESS_CONF, "Content Type"),
75         {NULL}
76 };
77
78 static void* xmlBuilderCreateConfig( apr_pool_t* p, server_rec* s ) {
79         xmlBuilderConfig* config = 
80                 (xmlBuilderConfig*) apr_palloc( p, sizeof(xmlBuilderConfig) );
81         config->baseDir                 = MODXMLB_DEFAULT_BASE_DIR;
82         config->defaultLocale   = MODXMLB_DEFAULT_LOCALE;
83         config->defaultDtd              = NULL;
84         config->postXSL                 = NULL;
85         config->contentType             = NULL;
86         config->localeParam             = MODXMLB_DEFAULT_LOCALE_PARAM;
87         return (void*) config;
88 }
89
90
91 /* Child Init handler  ----------------------------------------------------------- */
92 static void xmlBuilderChildInit( apr_pool_t *p, server_rec *s ) {
93 }
94
95 static int xmlBuilderHandler( request_rec* r ) {
96
97         if( strcmp(r->handler, MODULE_NAME ) ) return DECLINED;
98
99         xmlBuilderConfig* config = ap_get_module_config( 
100                         r->server->module_config, &xmlbuilder_module );
101         
102         r->allowed |= (AP_METHOD_BIT << M_GET);
103         r->allowed |= (AP_METHOD_BIT << M_POST);
104         char* ct = config->contentType;
105         if(!config->contentType) ct = "text/html; charset=utf-8";
106         ap_set_content_type(r, ct);
107
108         //ap_table_set(r->headers_out, "Cache-Control", "max-age=15552000");
109
110         char* dates = apr_pcalloc(r->pool, MAX_STRING_LEN);
111         apr_rfc822_date(dates, apr_time_now() + 604800000000); /* cache for one week */
112         ap_table_set(r->headers_out, "Expires", dates);
113
114         apr_rfc822_date(dates, apr_time_now()); /* cache for one week */
115         ap_table_set(r->headers_out, "Date", dates);
116
117
118         /*
119         char expire_hdr[APR_RFC822_DATE_LEN];
120          apr_rfc822_date(expire_hdr, (apr_time_t)(time(NULL) + 15552000));
121         ap_table_set(r->headers_out, "Expires", expire_hdr);
122          */
123
124         string_array* params = apacheParseParms(r);
125         char* locale = apacheGetFirstParamValue(params, config->localeParam);
126         if(locale) __xmlBuilderDynamicLocale = locale;
127         char* XMLFile = r->filename;
128
129         xmlDocPtr doc = xmlBuilderProcessFile( XMLFile, config );
130         if(!doc) return apacheError( "Unable to parse XML file %s", XMLFile );
131
132         /* apply the post XSL */
133         if(config->postXSL) {
134                 xmlDocPtr newdoc;
135                 newdoc = xsltApplyStylesheet(config->postXSL, doc, NULL );
136
137                 if(newdoc == NULL) {
138                         apacheDebug("Error applying postXSL... skipping.");
139                 } else {
140                         xmlFreeDoc(doc);
141                         doc = newdoc;
142                 }
143         }
144
145         char* docXML = xmlDocToString( doc, 1 );
146         ap_rputs(docXML, r);
147         free(docXML);
148         xmlFreeDoc( doc );
149         doc = NULL;
150         xmlCleanupCharEncodingHandlers();
151         xmlCleanupParser();
152
153         return OK;
154 }
155
156
157 /* frees the collected DTD's */
158 static void __xmlBuilderFreeDtdHash( char* key, void* item ) {
159         if(!item) return;
160         xmlFreeDtd( item );
161 }
162
163
164 xmlDocPtr xmlBuilderProcessFile( char* filename, xmlBuilderConfig* config ) {
165         if(!filename) { 
166                 apacheError( "No XML file provided" ); return NULL; }
167
168         xmlBuilderContext context;
169         context.config          = config;
170         context.doc                     = xmlNewDoc( BAD_CAST "1.0" );
171         context.dtdHash = osrfNewHash();
172         context.entHash = osrfNewHash();
173         context.nodeList        = osrfNewList();
174         context.xmlError        = 0;
175         context.xmlFile = filename;
176         context.dtdHash->freeItem = &__xmlBuilderFreeDtdHash;
177
178         /* pre-parse the default dtd if defined */
179         if( config->defaultDtd ) 
180                 xmlBuilderAddDtd( config->defaultDtd, &context );
181
182         xmlParserCtxtPtr parserCtx;
183
184         parserCtx = xmlCreatePushParserCtxt(xmlBuilderSaxHandler, &context, "", 0, NULL);
185         xmlCtxtReadFile( parserCtx, filename, NULL, XML_PARSE_RECOVER );
186
187         xmlFreeParserCtxt( parserCtx );
188         osrfListFree(context.nodeList);
189         osrfHashFree(context.entHash);
190         osrfHashFree(context.dtdHash);
191         return context.doc;
192 }
193
194
195 void xmlBuilderStartElement( void* context, const xmlChar *name, const xmlChar **atts ) {
196         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
197
198         xmlNodePtr node = NULL;
199
200         /* process xincludes as a sub-doc */
201         if( !strcmp( name, "xi:include" ) ) { 
202
203                 char* href = strdup(xmlSaxAttr( atts, "href" ));
204                 if(href) {
205
206                         /* find the relative path for the xinclude */
207                         if(href[0] != '/') {
208                                 int len = strlen(ctx->xmlFile) + strlen(href) + 1;
209                                 char buf[len];
210                                 bzero(buf, len);
211                                 strcpy( buf, ctx->xmlFile );
212                                 int i;
213                                 for( i = strlen(buf); i != 0; i-- ) {
214                                         if( buf[i] == '/' ) break;
215                                         buf[i] = '\0';
216                                 }
217                                 strcat( buf, href );
218                                 free(href);
219                                 href = strdup(buf);
220                         }
221
222
223                         xmlDocPtr subDoc = xmlBuilderProcessFile( href, ctx->config );
224                         node = xmlDocGetRootElement( subDoc );
225                 }
226
227                 if(!node) {
228                         apacheError("Unable to parse xinclude: %s", href );
229                         free(href);
230                         return;
231                 }
232                 free(href);
233
234         } else {
235                 node = xmlNewNode(NULL, name);
236                 xmlBuilderAddAtts( ctx, node, atts );
237         }
238
239
240         xmlNodePtr parent = osrfListGetIndex( 
241                         ctx->nodeList, ctx->nodeList->size - 1 );
242
243         if( parent ) xmlAddChild( parent, node );
244         else xmlDocSetRootElement(ctx->doc, node);
245         
246         osrfListPush( ctx->nodeList, node );
247 }
248
249
250 void xmlBuilderAddAtts( xmlBuilderContext* ctx, xmlNodePtr node, const xmlChar** atts ) {
251         if(!(ctx && node && atts)) return;
252         int i;
253         for(i = 0; (atts[i] != NULL); i++) {
254                 if(atts[i+1]) {
255                         const xmlChar* name = atts[i];
256                         const xmlChar* prop = atts[i+1];
257                         int nl = strlen(prop);
258                         char* _prop = NULL;
259
260                         if( prop[0] == '&' && prop[nl-1] == ';' ) { /* replace the entity if we are one */
261                                 char buf[nl+1];
262                                 bzero(buf, nl+1);
263                                 strncat(buf, prop + 1, nl - 2);
264                                 xmlEntityPtr ent = osrfHashGet( ctx->entHash, buf );
265                                 if(ent && ent->content) _prop = strdup(ent->content);
266                         } else { _prop = strdup(prop); }
267
268                         xmlSetProp( node, name, _prop );
269                         i++;
270                         free(_prop);
271                 }
272         }
273 }
274
275 void xmlBuilderEndElement( void* context, const xmlChar* name ) {
276         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
277         osrfListPop( ctx->nodeList );
278 }
279
280
281 void xmlBuilderHandleCharacter(void* context, const xmlChar *ch, int len) {
282         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
283         xmlNodePtr node = osrfListGetIndex( 
284                         ctx->nodeList, ctx->nodeList->size - 1 );
285
286         if(node) {
287                 xmlNodePtr txt = xmlNewTextLen(ch, len);
288                 xmlAddChild( node, txt );
289         }
290
291 }
292
293
294 void xmlBuilderParseError( void* context, const char* msg, ... ) {
295         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
296         VA_LIST_TO_STRING(msg);
297         apacheDebug( "Parser Error Occurred: %s", VA_BUF);
298         ctx->xmlError = 1;
299 }
300
301
302 xmlEntityPtr xmlBuilderGetEntity( void* context, const xmlChar* name ) {
303         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
304         xmlEntityPtr ent = osrfHashGet( ctx->entHash, name );
305         return ent;
306 }
307
308
309 void xmlBuilderExtSubset( void* blob, 
310                 const xmlChar* name, const xmlChar* extId, const xmlChar* sysId ) {
311
312         xmlBuilderContext* context = (xmlBuilderContext*) blob;
313         if( context->config->defaultDtd ) {
314                 apacheDebug("Ignoring DTD [%s] because default DTD is set...", sysId);
315                 return; 
316         }
317
318         xmlBuilderAddDtd( sysId, context );
319 }
320
321
322 void xmlBuilderProcInstruction( 
323                         void* blob, const xmlChar* name, const xmlChar* data ) {
324         xmlBuilderContext* ctx = (xmlBuilderContext*) blob;
325         //xmlNodePtr node = xmlNewDocPI( ctx->doc, name, data );
326         xmlNodePtr pi = xmlNewDocPI( ctx->doc, name, data );
327         xmlAddChild( (xmlNodePtr) ctx->doc, pi );
328 }
329
330
331
332 void xmlBuilderAddDtd( const char* sysId, xmlBuilderContext* context ) {
333
334         if(!sysId) return;
335         if( osrfHashGet( context->dtdHash, sysId ) ) return; /* already parsed this hash */
336
337         //apacheDebug("Adding new DTD file to the entity hash: %s", sysId);
338
339         /* use the dynamic locale if defined... default locale instead */
340         char* locale;
341         if(__xmlBuilderDynamicLocale) locale = __xmlBuilderDynamicLocale;
342         else locale = context->config->defaultLocale;
343
344         /* determine the path to the DTD file and load it */
345         int len = strlen(context->config->baseDir) + strlen(locale) + strlen(sysId) + 4;
346         char buf[len]; bzero(buf,len);
347         snprintf( buf, len, "%s/%s/%s", context->config->baseDir, locale, sysId );
348
349         xmlDtdPtr dtd = xmlParseDTD(NULL, buf);
350         if(!dtd) return;
351
352         /* cycle through entities and push them into the entity hash */
353         xmlNodePtr node = dtd->children;
354         while( node ) {
355                 if( node->type == XML_ENTITY_DECL ) { /* shove the entities into the hash */
356                         xmlEntityPtr ent = (xmlEntityPtr) node;
357                         osrfHashSet( context->entHash, ent, (char*) ent->name );
358                 }
359                 node = node->next;
360         }
361
362         /* cache the DTD so we can free it later */
363         osrfHashSet( context->dtdHash, dtd, sysId );
364 }
365
366
367 /* ------------------------------------------------------------------------ */
368
369 /* register callbacks */
370 static void xmlBuilderRegisterHooks (apr_pool_t *p) {
371         ap_hook_handler(xmlBuilderHandler, NULL, NULL, APR_HOOK_MIDDLE);
372         ap_hook_child_init(xmlBuilderChildInit,NULL,NULL,APR_HOOK_MIDDLE);
373 }
374
375
376 /* finally, flesh the module */
377 module AP_MODULE_DECLARE_DATA xmlbuilder_module = {
378         STANDARD20_MODULE_STUFF,
379         NULL,
380         NULL,
381         xmlBuilderCreateConfig,
382         NULL,
383         xmlBuilderCommands,
384         xmlBuilderRegisterHooks,
385 };
386
387
388
389
390