GPLv2. Added appendix with full license. Added refernece to that from
[pazpar2-moved-to-github.git] / src / config.c
1 /* $Id: config.c,v 1.23 2007-04-10 08:48:56 adam 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.23 2007-04-10 08:48:56 adam 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(termlist);
195             xmlFree(rank);
196             md_node++;
197         }
198         else
199         {
200             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
201             return 0;
202         }
203     }
204     return r;
205 }
206
207 static char *parse_settings(xmlNode *node)
208 {
209     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
210     char *r;
211
212     if (src)
213         r = nmem_strdup(nmem, (const char *) src);
214     else
215     {
216         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
217         return 0;
218     }
219     xmlFree(src);
220     return r;
221 }
222
223 static struct conf_server *parse_server(xmlNode *node)
224 {
225     xmlNode *n;
226     struct conf_server *r = nmem_malloc(nmem, sizeof(struct conf_server));
227
228     r->host = 0;
229     r->port = 0;
230     r->proxy_host = 0;
231     r->proxy_port = 0;
232     r->myurl = 0;
233     r->zproxy_host = 0;
234     r->zproxy_port = 0;
235     r->service = 0;
236     r->next = 0;
237     r->settings = 0;
238
239     for (n = node->children; n; n = n->next)
240     {
241         if (n->type != XML_ELEMENT_NODE)
242             continue;
243         if (!strcmp((const char *) n->name, "listen"))
244         {
245             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
246             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
247             if (port)
248                 r->port = atoi((const char *) port);
249             if (host)
250                 r->host = nmem_strdup(nmem, (const char *) host);
251             xmlFree(port);
252             xmlFree(host);
253         }
254         else if (!strcmp((const char *) n->name, "proxy"))
255         {
256             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
257             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
258             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
259             if (port)
260                 r->proxy_port = atoi((const char *) port);
261             if (host)
262                 r->proxy_host = nmem_strdup(nmem, (const char *) host);
263             if (myurl)
264                 r->myurl = nmem_strdup(nmem, (const char *) myurl);
265 #ifdef GAGA
266             else
267             {
268                 yaz_log(YLOG_FATAL, "Must specify @myurl for proxy");
269                 return 0;
270             }
271 #endif
272             xmlFree(port);
273             xmlFree(host);
274             xmlFree(myurl);
275         }
276         else if (!strcmp((const char *) n->name, "zproxy"))
277         {
278             xmlChar *port = 0;
279             xmlChar *host = 0;
280
281             port = xmlGetProp(n, (xmlChar *) "port");
282             host = xmlGetProp(n, (xmlChar *) "host");
283
284             if (port)
285                 r->zproxy_port = atoi((const char *) port);
286             if (host)
287                 r->zproxy_host = nmem_strdup(nmem, (const char *) host);
288
289             xmlFree(port);
290             xmlFree(host);
291         }
292         else if (!strcmp((const char *) n->name, "settings"))
293         {
294             if (r->settings)
295             {
296                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
297                 return 0;
298             }
299             if (!(r->settings = parse_settings(n)))
300                 return 0;
301         }
302         else if (!strcmp((const char *) n->name, "service"))
303         {
304             struct conf_service *s = parse_service(n);
305             if (!s)
306                 return 0;
307             r->service = s;
308         }
309         else
310         {
311             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
312             return 0;
313         }
314     }
315     return r;
316 }
317
318 xsltStylesheet *conf_load_stylesheet(const char *fname)
319 {
320     char path[256];
321     sprintf(path, "%s/%s", confdir, fname);
322     return xsltParseStylesheetFile((xmlChar *) path);
323 }
324
325 static struct conf_targetprofiles *parse_targetprofiles(xmlNode *node)
326 {
327     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
328     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
329     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
330
331     memset(r, 0, sizeof(*r));
332
333     if (type)
334     {
335         if (!strcmp((const char *) type, "local"))
336             r->type = Targetprofiles_local;
337         else
338         {
339             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
340             return 0;
341         }
342     }
343     else
344     {
345         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
346         return 0;
347     }
348
349     if (src)
350         r->src = nmem_strdup(nmem, (const char *) src);
351     else
352     {
353         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
354         return 0;
355     }
356     xmlFree(type);
357     xmlFree(src);
358     return r;
359 }
360
361 static struct conf_config *parse_config(xmlNode *root)
362 {
363     xmlNode *n;
364     struct conf_config *r = nmem_malloc(nmem, sizeof(struct conf_config));
365
366     r->servers = 0;
367     r->targetprofiles = 0;
368
369     for (n = root->children; n; n = n->next)
370     {
371         if (n->type != XML_ELEMENT_NODE)
372             continue;
373         if (!strcmp((const char *) n->name, "server"))
374         {
375             struct conf_server *tmp = parse_server(n);
376             if (!tmp)
377                 return 0;
378             tmp->next = r->servers;
379             r->servers = tmp;
380         }
381         else if (!strcmp((const char *) n->name, "targetprofiles"))
382         {
383             // It would be fun to be able to fix this sometime
384             if (r->targetprofiles)
385             {
386                 yaz_log(YLOG_FATAL, "Can't repeat targetprofiles");
387                 return 0;
388             }
389             if (!(r->targetprofiles = parse_targetprofiles(n)))
390                 return 0;
391         }
392         else
393         {
394             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
395             return 0;
396         }
397     }
398     return r;
399 }
400
401 int read_config(const char *fname)
402 {
403     xmlDoc *doc = xmlParseFile(fname);
404     const char *p;
405
406     if (!nmem)  // Initialize
407     {
408         nmem = nmem_create();
409         xmlSubstituteEntitiesDefault(1);
410         xmlLoadExtDtdDefaultValue = 1;
411     }
412     if (!doc)
413     {
414         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
415         exit(1);
416     }
417     if ((p = strrchr(fname, '/')))
418     {
419         int len = p - fname;
420         strncpy(confdir, fname, len);
421         confdir[len] = '\0';
422     }
423     config = parse_config(xmlDocGetRootElement(doc));
424     xmlFreeDoc(doc);
425
426     if (config)
427         return 1;
428     else
429         return 0;
430 }
431
432
433 /*
434  * Local variables:
435  * c-basic-offset: 4
436  * indent-tabs-mode: nil
437  * End:
438  * vim: shiftwidth=4 tabstop=8 expandtab
439  */