caught memory error
[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                         free(href);
200                         return;
201                 }
202                 free(href);
203
204         } else {
205                 node = xmlNewNode(NULL, name);
206                 xmlAddAttrs( node, atts );
207         }
208
209
210         xmlNodePtr parent = osrfListGetIndex( 
211                         ctx->nodeList, ctx->nodeList->size - 1 );
212
213         if( parent ) xmlAddChild( parent, node );
214         else xmlDocSetRootElement(ctx->doc, node);
215         
216         osrfListPush( ctx->nodeList, node );
217 }
218
219 void xmlBuilderEndElement( void* context, const xmlChar* name ) {
220         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
221         osrfListPop( ctx->nodeList );
222 }
223
224
225 void xmlBuilderHandleCharacter(void* context, const xmlChar *ch, int len) {
226         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
227         xmlNodePtr node = osrfListGetIndex( 
228                         ctx->nodeList, ctx->nodeList->size - 1 );
229
230         if(node) {
231                 xmlNodePtr txt = xmlNewTextLen(ch, len);
232                 xmlAddChild( node, txt );
233         }
234
235 }
236
237
238 void xmlBuilderParseError( void* context, const char* msg, ... ) {
239         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
240         VA_LIST_TO_STRING(msg);
241         apacheDebug( "Parser Error Occurred: %s", VA_BUF);
242         ctx->xmlError = 1;
243 }
244
245
246 xmlEntityPtr xmlBuilderGetEntity( void* context, const xmlChar* name ) {
247         xmlBuilderContext* ctx = (xmlBuilderContext*) context;
248         return osrfHashGet( ctx->entHash, name );
249 }
250
251
252 void xmlBuilderExtSubset( void* blob, 
253                 const xmlChar* name, const xmlChar* extId, const xmlChar* sysId ) {
254
255         xmlBuilderContext* context = (xmlBuilderContext*) blob;
256         if( context->config->defaultDtd ) return; /* only use the default if defined */
257         xmlBuilderAddDtd( sysId, context );
258 }
259
260
261
262 void xmlBuilderAddDtd( const char* sysId, xmlBuilderContext* context ) {
263
264         if(!sysId) return;
265         if( osrfHashGet( context->dtdHash, sysId ) ) return; /* already parsed this hash */
266
267         /* use the dynamic locale if defined... default locale instead */
268         char* locale;
269         if(__xmlBuilderDynamicLocale) locale = __xmlBuilderDynamicLocale;
270         else locale = context->config->defaultLocale;
271
272         /* determine the path to the DTD file and load it */
273         int len = strlen(context->config->baseDir) + strlen(locale) + strlen(sysId) + 4;
274         char buf[len]; bzero(buf,len);
275         snprintf( buf, len, "%s/%s/%s", context->config->baseDir, locale, sysId );
276
277         apacheDebug("Parsing DTD file %s", buf);
278         xmlDtdPtr dtd = xmlParseDTD(NULL, buf);
279
280
281         /* cycle through entities and push them into the entity hash */
282         xmlNodePtr node = dtd->children;
283         while( node ) {
284                 if( node->type == XML_ENTITY_DECL ) { /* shove the entities into the hash */
285                         xmlEntityPtr ent = (xmlEntityPtr) node;
286                         osrfHashSet( context->entHash, ent, (char*) ent->name );
287                 }
288                 node = node->next;
289         }
290
291         /* cache the DTD so we can free it later */
292         osrfHashSet( context->dtdHash, dtd, sysId );
293 }
294
295
296 /* ------------------------------------------------------------------------ */
297
298 /* register callbacks */
299 static void xmlBuilderRegisterHooks (apr_pool_t *p) {
300         ap_hook_handler(xmlBuilderHandler, NULL, NULL, APR_HOOK_MIDDLE);
301         ap_hook_child_init(xmlBuilderChildInit,NULL,NULL,APR_HOOK_MIDDLE);
302 }
303
304
305 /* finally, flesh the module */
306 module AP_MODULE_DECLARE_DATA xmlbuilder_module = {
307         STANDARD20_MODULE_STUFF,
308         NULL,
309         NULL,
310         xmlBuilderCreateConfig,
311         NULL,
312         xmlBuilderCommands,
313         xmlBuilderRegisterHooks,
314 };
315
316
317
318
319
320
321 /*
322 char* get_dtd_lang_file(string_array* params, char* default_locale, char* locale_dir) {
323
324         char* locale = apacheGetFirstParamValue(params, PARAM_LOCALE);
325         if(!locale) locale = default_locale;
326         if(!locale) return NULL;
327
328         int len = strlen(LANG_DTD) + strlen(locale) + strlen(locale_dir) + 1;
329         char dtdfile[len];
330         bzero(dtdfile, len);
331
332         if(locale)
333                 sprintf(dtdfile, "%s/%s/%s",  locale_dir, locale, LANG_DTD );
334
335         return strdup(dtdfile);
336 }
337 */
338
339