remember to free memory, otherwise leaking
[pazpar2-moved-to-github.git] / src / config.c
1 /* $Id: config.c,v 1.24 2007-04-11 11:01:45 marc Exp $
2    Copyright (c) 2006-2007, Index Data.
3
4 This file is part of Pazpar2.
5
6 Pazpar2 is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Pazpar2; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 /* $Id: config.c,v 1.24 2007-04-11 11:01:45 marc Exp $ */
23
24 #include <string.h>
25
26 #include <libxml/parser.h>
27 #include <libxml/tree.h>
28 #include <libxslt/xslt.h>
29 #include <libxslt/transform.h>
30 #include <libxslt/xsltutils.h>
31
32 #if HAVE_CONFIG_H
33 #include <cconfig.h>
34 #endif
35
36 #include <yaz/yaz-util.h>
37 #include <yaz/nmem.h>
38
39 #define CONFIG_NOEXTERNS
40 #include "config.h"
41
42 static NMEM nmem = 0;
43 static char confdir[256] = ".";
44
45 struct conf_config *config = 0;
46
47 /* Code to parse configuration file */
48 /* ==================================================== */
49
50 static struct conf_service *parse_service(xmlNode *node)
51 {
52     xmlNode *n;
53     struct conf_service *r = nmem_malloc(nmem, sizeof(struct conf_service));
54     int md_node = 0;
55     int sk_node = 0;
56
57     r->num_sortkeys = r->num_metadata = 0;
58     // Allocate array of conf metadata and sortkey tructs, if necessary
59     for (n = node->children; n; n = n->next)
60         if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)
61                                                    n->name, "metadata"))
62         {
63             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
64             r->num_metadata++;
65             if (sortkey && strcmp((const char *) sortkey, "no"))
66                 r->num_sortkeys++;
67             xmlFree(sortkey);
68         }
69     if (r->num_metadata)
70         r->metadata = nmem_malloc(nmem, sizeof(struct conf_metadata) * r->num_metadata);
71     else
72         r->metadata = 0;
73     if (r->num_sortkeys)
74         r->sortkeys = nmem_malloc(nmem, sizeof(struct conf_sortkey) * r->num_sortkeys);
75     else
76         r->sortkeys = 0;
77
78     for (n = node->children; n; n = n->next)
79     {
80         if (n->type != XML_ELEMENT_NODE)
81             continue;
82         if (!strcmp((const char *) n->name, (const char *) "metadata"))
83         {
84             struct conf_metadata *md = &r->metadata[md_node];
85             xmlChar *name = xmlGetProp(n, (xmlChar *) "name");
86             xmlChar *brief = xmlGetProp(n, (xmlChar *) "brief");
87             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
88             xmlChar *merge = xmlGetProp(n, (xmlChar *) "merge");
89             xmlChar *type = xmlGetProp(n, (xmlChar *) "type");
90             xmlChar *termlist = xmlGetProp(n, (xmlChar *) "termlist");
91             xmlChar *rank = xmlGetProp(n, (xmlChar *) "rank");
92
93             if (!name)
94             {
95                 yaz_log(YLOG_FATAL, "Must specify name in metadata element");
96                 return 0;
97             }
98             md->name = nmem_strdup(nmem, (const char *) name);
99             if (brief)
100             {
101                 if (!strcmp((const char *) brief, "yes"))
102                     md->brief = 1;
103                 else if (strcmp((const char *) brief, "no"))
104                 {
105                     yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
106                     return 0;
107                 }
108             }
109             else
110                 md->brief = 0;
111
112             if (termlist)
113             {
114                 if (!strcmp((const char *) termlist, "yes"))
115                     md->termlist = 1;
116                 else if (strcmp((const char *) termlist, "no"))
117                 {
118                     yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
119                     return 0;
120                 }
121             }
122             else
123                 md->termlist = 0;
124
125             if (rank)
126                 md->rank = atoi((const char *) rank);
127             else
128                 md->rank = 0;
129
130             if (type)
131             {
132                 if (!strcmp((const char *) type, "generic"))
133                     md->type = Metadata_type_generic;
134                 else if (!strcmp((const char *) type, "year"))
135                     md->type = Metadata_type_year;
136                 else
137                 {
138                     yaz_log(YLOG_FATAL, "Unknown value for metadata/type: %s", type);
139                     return 0;
140                 }
141             }
142             else
143                 md->type = Metadata_type_generic;
144
145             if (merge)
146             {
147                 if (!strcmp((const char *) merge, "no"))
148                     md->merge = Metadata_merge_no;
149                 else if (!strcmp((const char *) merge, "unique"))
150                     md->merge = Metadata_merge_unique;
151                 else if (!strcmp((const char *) merge, "longest"))
152                     md->merge = Metadata_merge_longest;
153                 else if (!strcmp((const char *) merge, "range"))
154                     md->merge = Metadata_merge_range;
155                 else if (!strcmp((const char *) merge, "all"))
156                     md->merge = Metadata_merge_all;
157                 else
158                 {
159                     yaz_log(YLOG_FATAL, "Unknown value for metadata/merge: %s", merge);
160                     return 0;
161                 }
162             }
163             else
164                 md->merge = Metadata_merge_no;
165
166             if (sortkey && strcmp((const char *) sortkey, "no"))
167             {
168                 struct conf_sortkey *sk = &r->sortkeys[sk_node];
169                 if (md->merge == Metadata_merge_no)
170                 {
171                     yaz_log(YLOG_FATAL, "Can't specify sortkey on a non-merged field");
172                     return 0;
173                 }
174                 if (!strcmp((const char *) sortkey, "numeric"))
175                     sk->type = Metadata_sortkey_numeric;
176                 else if (!strcmp((const char *) sortkey, "skiparticle"))
177                     sk->type = Metadata_sortkey_skiparticle;
178                 else
179                 {
180                     yaz_log(YLOG_FATAL, "Unknown sortkey in metadata element: %s", sortkey);
181                     return 0;
182                 }
183                 sk->name = md->name;
184                 md->sortkey_offset = sk_node;
185                 sk_node++;
186             }
187             else
188                 md->sortkey_offset = -1;
189
190             xmlFree(name);
191             xmlFree(brief);
192             xmlFree(sortkey);
193             xmlFree(merge);
194             xmlFree(type);
195             xmlFree(termlist);
196             xmlFree(rank);
197             md_node++;
198         }
199         else
200         {
201             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
202             return 0;
203         }
204     }
205     return r;
206 }
207
208 static char *parse_settings(xmlNode *node)
209 {
210     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
211     char *r;
212
213     if (src)
214         r = nmem_strdup(nmem, (const char *) src);
215     else
216     {
217         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
218         return 0;
219     }
220     xmlFree(src);
221     return r;
222 }
223
224 static struct conf_server *parse_server(xmlNode *node)
225 {
226     xmlNode *n;
227     struct conf_server *r = nmem_malloc(nmem, sizeof(struct conf_server));
228
229     r->host = 0;
230     r->port = 0;
231     r->proxy_host = 0;
232     r->proxy_port = 0;
233     r->myurl = 0;
234     r->zproxy_host = 0;
235     r->zproxy_port = 0;
236     r->service = 0;
237     r->next = 0;
238     r->settings = 0;
239
240     for (n = node->children; n; n = n->next)
241     {
242         if (n->type != XML_ELEMENT_NODE)
243             continue;
244         if (!strcmp((const char *) n->name, "listen"))
245         {
246             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
247             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
248             if (port)
249                 r->port = atoi((const char *) port);
250             if (host)
251                 r->host = nmem_strdup(nmem, (const char *) host);
252             xmlFree(port);
253             xmlFree(host);
254         }
255         else if (!strcmp((const char *) n->name, "proxy"))
256         {
257             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
258             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
259             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
260             if (port)
261                 r->proxy_port = atoi((const char *) port);
262             if (host)
263                 r->proxy_host = nmem_strdup(nmem, (const char *) host);
264             if (myurl)
265                 r->myurl = nmem_strdup(nmem, (const char *) myurl);
266 #ifdef GAGA
267             else
268             {
269                 yaz_log(YLOG_FATAL, "Must specify @myurl for proxy");
270                 return 0;
271             }
272 #endif
273             xmlFree(port);
274             xmlFree(host);
275             xmlFree(myurl);
276         }
277         else if (!strcmp((const char *) n->name, "zproxy"))
278         {
279             xmlChar *port = 0;
280             xmlChar *host = 0;
281
282             port = xmlGetProp(n, (xmlChar *) "port");
283             host = xmlGetProp(n, (xmlChar *) "host");
284
285             if (port)
286                 r->zproxy_port = atoi((const char *) port);
287             if (host)
288                 r->zproxy_host = nmem_strdup(nmem, (const char *) host);
289
290             xmlFree(port);
291             xmlFree(host);
292         }
293         else if (!strcmp((const char *) n->name, "settings"))
294         {
295             if (r->settings)
296             {
297                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
298                 return 0;
299             }
300             if (!(r->settings = parse_settings(n)))
301                 return 0;
302         }
303         else if (!strcmp((const char *) n->name, "service"))
304         {
305             struct conf_service *s = parse_service(n);
306             if (!s)
307                 return 0;
308             r->service = s;
309         }
310         else
311         {
312             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
313             return 0;
314         }
315     }
316     return r;
317 }
318
319 xsltStylesheet *conf_load_stylesheet(const char *fname)
320 {
321     char path[256];
322     sprintf(path, "%s/%s", confdir, fname);
323     return xsltParseStylesheetFile((xmlChar *) path);
324 }
325
326 static struct conf_targetprofiles *parse_targetprofiles(xmlNode *node)
327 {
328     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
329     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
330     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
331
332     memset(r, 0, sizeof(*r));
333
334     if (type)
335     {
336         if (!strcmp((const char *) type, "local"))
337             r->type = Targetprofiles_local;
338         else
339         {
340             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
341             return 0;
342         }
343     }
344     else
345     {
346         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
347         return 0;
348     }
349
350     if (src)
351         r->src = nmem_strdup(nmem, (const char *) src);
352     else
353     {
354         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
355         return 0;
356     }
357     xmlFree(type);
358     xmlFree(src);
359     return r;
360 }
361
362 static struct conf_config *parse_config(xmlNode *root)
363 {
364     xmlNode *n;
365     struct conf_config *r = nmem_malloc(nmem, sizeof(struct conf_config));
366
367     r->servers = 0;
368     r->targetprofiles = 0;
369
370     for (n = root->children; n; n = n->next)
371     {
372         if (n->type != XML_ELEMENT_NODE)
373             continue;
374         if (!strcmp((const char *) n->name, "server"))
375         {
376             struct conf_server *tmp = parse_server(n);
377             if (!tmp)
378                 return 0;
379             tmp->next = r->servers;
380             r->servers = tmp;
381         }
382         else if (!strcmp((const char *) n->name, "targetprofiles"))
383         {
384             // It would be fun to be able to fix this sometime
385             if (r->targetprofiles)
386             {
387                 yaz_log(YLOG_FATAL, "Can't repeat targetprofiles");
388                 return 0;
389             }
390             if (!(r->targetprofiles = parse_targetprofiles(n)))
391                 return 0;
392         }
393         else
394         {
395             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
396             return 0;
397         }
398     }
399     return r;
400 }
401
402 int read_config(const char *fname)
403 {
404     xmlDoc *doc = xmlParseFile(fname);
405     const char *p;
406
407     if (!nmem)  // Initialize
408     {
409         nmem = nmem_create();
410         xmlSubstituteEntitiesDefault(1);
411         xmlLoadExtDtdDefaultValue = 1;
412     }
413     if (!doc)
414     {
415         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
416         exit(1);
417     }
418     if ((p = strrchr(fname, '/')))
419     {
420         int len = p - fname;
421         strncpy(confdir, fname, len);
422         confdir[len] = '\0';
423     }
424     config = parse_config(xmlDocGetRootElement(doc));
425     xmlFreeDoc(doc);
426
427     if (config)
428         return 1;
429     else
430         return 0;
431 }
432
433
434 /*
435  * Local variables:
436  * c-basic-offset: 4
437  * indent-tabs-mode: nil
438  * End:
439  * vim: shiftwidth=4 tabstop=8 expandtab
440  */