]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/apachemods/mod_xmlbuilder.c
61a7cf5b810e6aea924094ec26e04caea9ec9078
[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 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"),
65         {NULL}
66 };
67
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;
77 }
78
79
80 /* Child Init handler  ----------------------------------------------------------- */
81 static void xmlBuilderChildInit( apr_pool_t *p, server_rec *s ) {
82 }
83
84 static int xmlBuilderHandler( request_rec* r ) {
85
86         if( strcmp(r->handler, MODULE_NAME ) ) return DECLINED;
87
88         xmlBuilderConfig* config = ap_get_module_config( 
89                         r->server->module_config, &xmlbuilder_module );
90         
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");
95
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);
99
100         apr_rfc822_date(dates, apr_time_now()); /* cache for one week */
101         ap_table_set(r->headers_out, "Date", dates);
102
103
104         /*
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);
108          */
109
110         string_array* params = apacheParseParms(r);
111         char* locale = apacheGetFirstParamValue(params, config->localeParam);
112         if(locale) __xmlBuilderDynamicLocale = locale;
113         char* XMLFile = r->filename;
114
115         xmlDocPtr doc = xmlBuilderProcessFile( XMLFile, config );
116         if(!doc) return apacheError( "Unable to parse XML file %s", XMLFile );
117
118         /* apply the post XSL */
119         if(config->postXSL) {
120                 xmlDocPtr newdoc;
121                 newdoc = xsltApplyStylesheet(config->postXSL, doc, NULL );
122
123                 if(newdoc == NULL) {
124                         apacheDebug("Error applying postXSL... skipping.");
125                 } else {
126                         xmlFreeDoc(doc);
127                         doc = newdoc;
128                 }
129         }
130
131         char* docXML = xmlDocToString( doc, 1 );
132         ap_rputs(docXML, r);
133         free(docXML);
134         xmlFreeDoc( doc );
135         doc = NULL;
136         xmlCleanupCharEncodingHandlers();
137         xmlCleanupParser();
138
139         return OK;
140 }
141
142
143 /* frees the collected DTD's */
144 static void __xmlBuilderFreeDtdHash( char* key, void* item ) {
145         if(!item) return;
146         xmlFreeDtd( item );
147 }
148
149
150 xmlDocPtr xmlBuilderProcessFile( char* filename, xmlBuilderConfig* config ) {
151         if(!filename) { 
152                 apacheError( "No XML file provided" ); return NULL; }
153
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;
163
164         /* pre-parse the default dtd if defined */
165         if( config->defaultDtd ) 
166                 xmlBuilderAddDtd( config->defaultDtd, &context );
167
168         xmlParserCtxtPtr parserCtx;
169
170         parserCtx = xmlCreatePushParserCtxt(xmlBuilderSaxHandler, &context, "", 0, NULL);
171         xmlCtxtReadFile( parserCtx, filename, NULL, XML_PARSE_RECOVER );
172
173         xmlFreeParserCtxt( parserCtx );
174         osrfListFree(context.nodeList);
175         osrfHashFree(context.entHash);
176         osrfHashFree(context.dtdHash);
177         return context.doc;
178 }
179
180
181 void xmlBuilderStartElement( void* context, const xmlChar *name, const xmlChar **atts ) {
182         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
183
184         xmlNodePtr node = NULL;
185
186         /* process xincludes as a sub-doc */
187         if( !strcmp( name, "xi:include" ) ) { 
188
189                 char* href = strdup(xmlSaxAttr( atts, "href" ));
190                 if(href) {
191
192                         /* find the relative path for the xinclude */
193                         if(href[0] != '/') {
194                                 int len = strlen(ctx->xmlFile) + strlen(href) + 1;
195                                 char buf[len];
196                                 bzero(buf, len);
197                                 strcpy( buf, ctx->xmlFile );
198                                 int i;
199                                 for( i = strlen(buf); i != 0; i-- ) {
200                                         if( buf[i] == '/' ) break;
201                                         buf[i] = '\0';
202                                 }
203                                 strcat( buf, href );
204                                 free(href);
205                                 href = strdup(buf);
206                         }
207
208
209                         xmlDocPtr subDoc = xmlBuilderProcessFile( href, ctx->config );
210                         node = xmlDocGetRootElement( subDoc );
211                 }
212
213                 if(!node) {
214                         apacheError("Unable to parse xinclude: %s", href );
215                         free(href);
216                         return;
217                 }
218                 free(href);
219
220         } else {
221                 node = xmlNewNode(NULL, name);
222                 xmlAddAttrs( node, atts );
223         }
224
225
226         xmlNodePtr parent = osrfListGetIndex( 
227                         ctx->nodeList, ctx->nodeList->size - 1 );
228
229         if( parent ) xmlAddChild( parent, node );
230         else xmlDocSetRootElement(ctx->doc, node);
231         
232         osrfListPush( ctx->nodeList, node );
233 }
234
235 void xmlBuilderEndElement( void* context, const xmlChar* name ) {
236         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
237         osrfListPop( ctx->nodeList );
238 }
239
240
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 );
245
246         if(node) {
247                 xmlNodePtr txt = xmlNewTextLen(ch, len);
248                 xmlAddChild( node, txt );
249         }
250
251 }
252
253
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);
258         ctx->xmlError = 1;
259 }
260
261
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 );
266 }
267
268
269 void xmlBuilderExtSubset( void* blob, 
270                 const xmlChar* name, const xmlChar* extId, const xmlChar* sysId ) {
271
272         xmlBuilderContext* context = (xmlBuilderContext*) blob;
273         if( context->config->defaultDtd ) {
274                 apacheDebug("Ignoring DTD [%s] because default DTD is set...", sysId);
275                 return; 
276         }
277
278         xmlBuilderAddDtd( sysId, context );
279 }
280
281
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 );
288 }
289
290
291
292 void xmlBuilderAddDtd( const char* sysId, xmlBuilderContext* context ) {
293
294         if(!sysId) return;
295         if( osrfHashGet( context->dtdHash, sysId ) ) return; /* already parsed this hash */
296
297         //apacheDebug("Adding new DTD file to the entity hash: %s", sysId);
298
299         /* use the dynamic locale if defined... default locale instead */
300         char* locale;
301         if(__xmlBuilderDynamicLocale) locale = __xmlBuilderDynamicLocale;
302         else locale = context->config->defaultLocale;
303
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 );
308
309         xmlDtdPtr dtd = xmlParseDTD(NULL, buf);
310         if(!dtd) return;
311
312         /* cycle through entities and push them into the entity hash */
313         xmlNodePtr node = dtd->children;
314         while( node ) {
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 );
318                 }
319                 node = node->next;
320         }
321
322         /* cache the DTD so we can free it later */
323         osrfHashSet( context->dtdHash, dtd, sysId );
324 }
325
326
327 /* ------------------------------------------------------------------------ */
328
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);
333 }
334
335
336 /* finally, flesh the module */
337 module AP_MODULE_DECLARE_DATA xmlbuilder_module = {
338         STANDARD20_MODULE_STUFF,
339         NULL,
340         NULL,
341         xmlBuilderCreateConfig,
342         NULL,
343         xmlBuilderCommands,
344         xmlBuilderRegisterHooks,
345 };
346
347
348
349
350