splitting record specific definition out from pazpar2.h header to record.h header...
[pazpar2-moved-to-github.git] / src / config.c
1 /* $Id: config.c,v 1.27 2007-04-20 11:00:29 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.27 2007-04-20 11:00:29 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
48 struct conf_metadata * conf_metadata_assign(NMEM nmem, 
49                                             struct conf_metadata * metadata,
50                                             const char *name,
51                                             enum conf_metadata_type type,
52                                             enum conf_metadata_merge merge,
53                                             int brief,
54                                             int termlist,
55                                             int rank,
56                                             int sortkey_offset)
57 {
58     if (!nmem || !metadata || !name)
59         return 0;
60     
61     metadata->name = nmem_strdup(nmem, name);
62     metadata->type = type;
63     metadata->merge = merge;
64     metadata->brief = brief;   
65     metadata->termlist = termlist;
66     metadata->rank = rank;    
67     metadata->sortkey_offset = sortkey_offset;
68
69     return metadata;
70 }
71
72
73 struct conf_sortkey * conf_sortkey_assign(NMEM nmem, 
74                                           struct conf_sortkey * sortkey,
75                                           const char *name,
76                                           enum conf_sortkey_type type)
77 {
78     if (!nmem || !sortkey || !name)
79         return 0;
80     
81     sortkey->name = nmem_strdup(nmem, name);
82     sortkey->type = type;
83
84     return sortkey;
85 }
86
87
88 struct conf_service * conf_service_create(NMEM nmem,
89                                           int num_metadata, int num_sortkeys)
90 {
91     struct conf_service * service
92         = nmem_malloc(nmem, sizeof(struct conf_service));
93     service->num_metadata = num_metadata;
94     service->metadata = 0;
95     if (service->num_metadata)
96       service->metadata 
97           = nmem_malloc(nmem, 
98                         sizeof(struct conf_metadata) * service->num_metadata);
99     service->num_sortkeys = num_sortkeys;
100     service->sortkeys = 0;
101     if (service->num_sortkeys)
102         service->sortkeys 
103             = nmem_malloc(nmem, 
104                           sizeof(struct conf_sortkey) * service->num_sortkeys);
105     return service; 
106 }
107
108 struct conf_metadata* conf_service_add_metadata(NMEM nmem, 
109                                                 struct conf_service *service,
110                                                 int position,
111                                                 const char *name,
112                                                 enum conf_metadata_type type,
113                                                 enum conf_metadata_merge merge,
114                                                 int brief,
115                                                 int termlist,
116                                                 int rank,
117                                                 int sortkey_offset)
118 {
119     struct conf_metadata * md = 0;
120
121     if (!service || !service->metadata || !service->num_metadata
122         || position < 0  || !(position < service->num_metadata))
123         return 0;
124
125     //md = &((service->metadata)[position]);
126     md = service->metadata + position;
127     md = conf_metadata_assign(nmem, md, name, type, merge, 
128                              brief, termlist, rank, sortkey_offset);
129     return md;
130 }
131
132
133 struct conf_sortkey * conf_service_add_sortkey(NMEM nmem,
134                                                struct conf_service *service,
135                                                int position,
136                                                const char *name,
137                                                enum conf_sortkey_type type)
138 {
139     struct conf_sortkey * sk = 0;
140
141     if (!service || !service->sortkeys || !service->num_sortkeys
142         || position < 0  || !(position < service->num_sortkeys))
143         return 0;
144
145     //sk = &((service->sortkeys)[position]);
146     sk = service->sortkeys + position;
147     sk = conf_sortkey_assign(nmem, sk, name, type);
148
149     return sk;
150 }
151
152
153
154
155
156 /* Code to parse configuration file */
157 /* ==================================================== */
158
159 static struct conf_service *parse_service(xmlNode *node)
160 {
161     xmlNode *n;
162     int md_node = 0;
163     int sk_node = 0;
164
165     struct conf_service *service = 0;
166     int num_metadata = 0;
167     int num_sortkeys = 0;
168     
169     // count num_metadata and num_sortkeys
170     for (n = node->children; n; n = n->next)
171         if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)
172                                                    n->name, "metadata"))
173         {
174             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
175             num_metadata++;
176             if (sortkey && strcmp((const char *) sortkey, "no"))
177                 num_sortkeys++;
178             xmlFree(sortkey);
179         }
180
181     service = conf_service_create(nmem, num_metadata, num_sortkeys);    
182
183     for (n = node->children; n; n = n->next)
184     {
185         if (n->type != XML_ELEMENT_NODE)
186             continue;
187         if (!strcmp((const char *) n->name, (const char *) "metadata"))
188         {
189             xmlChar *xml_name = xmlGetProp(n, (xmlChar *) "name");
190             xmlChar *xml_brief = xmlGetProp(n, (xmlChar *) "brief");
191             xmlChar *xml_sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
192             xmlChar *xml_merge = xmlGetProp(n, (xmlChar *) "merge");
193             xmlChar *xml_type = xmlGetProp(n, (xmlChar *) "type");
194             xmlChar *xml_termlist = xmlGetProp(n, (xmlChar *) "termlist");
195             xmlChar *xml_rank = xmlGetProp(n, (xmlChar *) "rank");
196
197             enum conf_metadata_type type = Metadata_type_generic;
198             enum conf_metadata_merge merge = Metadata_merge_no;
199             int brief = 0;
200             int termlist = 0;
201             int rank = 0;
202             int sortkey_offset = 0;
203             enum conf_sortkey_type sk_type = Metadata_sortkey_relevance;
204             
205             // now do the parsing logic
206             if (!xml_name)
207             {
208                 yaz_log(YLOG_FATAL, "Must specify name in metadata element");
209                 return 0;
210             }
211             if (xml_brief)
212             {
213                 if (!strcmp((const char *) xml_brief, "yes"))
214                     brief = 1;
215                  else if (strcmp((const char *) xml_brief, "no"))
216                 {
217                     yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
218                     return 0;
219                 }
220             }
221             else
222                 brief = 0;
223
224             if (xml_termlist)
225             {
226                 if (!strcmp((const char *) xml_termlist, "yes"))
227                     termlist = 1;
228                 else if (strcmp((const char *) xml_termlist, "no"))
229                 {
230                     yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
231                     return 0;
232                 }
233             }
234             else
235                 termlist = 0;
236
237             if (xml_rank)
238                 rank = atoi((const char *) xml_rank);
239             else
240                 rank = 0;
241
242             if (xml_type)
243             {
244                 if (!strcmp((const char *) xml_type, "generic"))
245                     type = Metadata_type_generic;
246                 else if (!strcmp((const char *) xml_type, "year"))
247                     type = Metadata_type_year;
248                 else
249                 {
250                     yaz_log(YLOG_FATAL, "Unknown value for metadata/type: %s", xml_type);
251                     return 0;
252                 }
253             }
254             else
255                 type = Metadata_type_generic;
256
257             if (xml_merge)
258             {
259                 if (!strcmp((const char *) xml_merge, "no"))
260                     merge = Metadata_merge_no;
261                 else if (!strcmp((const char *) xml_merge, "unique"))
262                     merge = Metadata_merge_unique;
263                 else if (!strcmp((const char *) xml_merge, "longest"))
264                     merge = Metadata_merge_longest;
265                 else if (!strcmp((const char *) xml_merge, "range"))
266                     merge = Metadata_merge_range;
267                 else if (!strcmp((const char *) xml_merge, "all"))
268                     merge = Metadata_merge_all;
269                 else
270                 {
271                     yaz_log(YLOG_FATAL, 
272                             "Unknown value for metadata/merge: %s", xml_merge);
273                     return 0;
274                 }
275             }
276             else
277                 merge = Metadata_merge_no;
278
279             // add a sortkey if so specified
280             if (xml_sortkey && strcmp((const char *) xml_sortkey, "no"))
281             {
282                 if (merge == Metadata_merge_no)
283                 {
284                     yaz_log(YLOG_FATAL, 
285                             "Can't specify sortkey on a non-merged field");
286                     return 0;
287                 }
288                 if (!strcmp((const char *) xml_sortkey, "numeric"))
289                     sk_type = Metadata_sortkey_numeric;
290                 else if (!strcmp((const char *) xml_sortkey, "skiparticle"))
291                     sk_type = Metadata_sortkey_skiparticle;
292                 else
293                 {
294                     yaz_log(YLOG_FATAL,
295                             "Unknown sortkey in metadata element: %s", 
296                             xml_sortkey);
297                     return 0;
298                 }
299                 sortkey_offset = sk_node;
300
301                 conf_service_add_sortkey(nmem, service, sk_node,
302                                          (const char *) xml_name, sk_type);
303                 
304                 sk_node++;
305             }
306             else
307                 sortkey_offset = -1;
308
309             // metadata known, assign values
310             conf_service_add_metadata(nmem, service, md_node,
311                                       (const char *) xml_name,
312                                       type, merge,
313                                       brief, termlist, rank, sortkey_offset);
314
315             xmlFree(xml_name);
316             xmlFree(xml_brief);
317             xmlFree(xml_sortkey);
318             xmlFree(xml_merge);
319             xmlFree(xml_type);
320             xmlFree(xml_termlist);
321             xmlFree(xml_rank);
322             md_node++;
323         }
324         else
325         {
326             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
327             return 0;
328         }
329     }
330     return service;
331 }
332
333 static char *parse_settings(xmlNode *node)
334 {
335     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
336     char *r;
337
338     if (src)
339         r = nmem_strdup(nmem, (const char *) src);
340     else
341     {
342         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
343         return 0;
344     }
345     xmlFree(src);
346     return r;
347 }
348
349 static struct conf_server *parse_server(xmlNode *node)
350 {
351     xmlNode *n;
352     struct conf_server *r = nmem_malloc(nmem, sizeof(struct conf_server));
353
354     r->host = 0;
355     r->port = 0;
356     r->proxy_host = 0;
357     r->proxy_port = 0;
358     r->myurl = 0;
359     r->zproxy_host = 0;
360     r->zproxy_port = 0;
361     r->service = 0;
362     r->next = 0;
363     r->settings = 0;
364
365     for (n = node->children; n; n = n->next)
366     {
367         if (n->type != XML_ELEMENT_NODE)
368             continue;
369         if (!strcmp((const char *) n->name, "listen"))
370         {
371             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
372             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
373             if (port)
374                 r->port = atoi((const char *) port);
375             if (host)
376                 r->host = nmem_strdup(nmem, (const char *) host);
377             xmlFree(port);
378             xmlFree(host);
379         }
380         else if (!strcmp((const char *) n->name, "proxy"))
381         {
382             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
383             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
384             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
385             if (port)
386                 r->proxy_port = atoi((const char *) port);
387             if (host)
388                 r->proxy_host = nmem_strdup(nmem, (const char *) host);
389             if (myurl)
390                 r->myurl = nmem_strdup(nmem, (const char *) myurl);
391 #ifdef GAGA
392             else
393             {
394                 yaz_log(YLOG_FATAL, "Must specify @myurl for proxy");
395                 return 0;
396             }
397 #endif
398             xmlFree(port);
399             xmlFree(host);
400             xmlFree(myurl);
401         }
402         else if (!strcmp((const char *) n->name, "zproxy"))
403         {
404             xmlChar *port = 0;
405             xmlChar *host = 0;
406
407             port = xmlGetProp(n, (xmlChar *) "port");
408             host = xmlGetProp(n, (xmlChar *) "host");
409
410             if (port)
411                 r->zproxy_port = atoi((const char *) port);
412             if (host)
413                 r->zproxy_host = nmem_strdup(nmem, (const char *) host);
414
415             xmlFree(port);
416             xmlFree(host);
417         }
418         else if (!strcmp((const char *) n->name, "settings"))
419         {
420             if (r->settings)
421             {
422                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
423                 return 0;
424             }
425             if (!(r->settings = parse_settings(n)))
426                 return 0;
427         }
428         else if (!strcmp((const char *) n->name, "service"))
429         {
430             struct conf_service *s = parse_service(n);
431             if (!s)
432                 return 0;
433             r->service = s;
434         }
435         else
436         {
437             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
438             return 0;
439         }
440     }
441     return r;
442 }
443
444 xsltStylesheet *conf_load_stylesheet(const char *fname)
445 {
446     char path[256];
447     sprintf(path, "%s/%s", confdir, fname);
448     return xsltParseStylesheetFile((xmlChar *) path);
449 }
450
451 static struct conf_targetprofiles *parse_targetprofiles(xmlNode *node)
452 {
453     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
454     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
455     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
456
457     memset(r, 0, sizeof(*r));
458
459     if (type)
460     {
461         if (!strcmp((const char *) type, "local"))
462             r->type = Targetprofiles_local;
463         else
464         {
465             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
466             return 0;
467         }
468     }
469     else
470     {
471         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
472         return 0;
473     }
474
475     if (src)
476         r->src = nmem_strdup(nmem, (const char *) src);
477     else
478     {
479         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
480         return 0;
481     }
482     xmlFree(type);
483     xmlFree(src);
484     return r;
485 }
486
487 static struct conf_config *parse_config(xmlNode *root)
488 {
489     xmlNode *n;
490     struct conf_config *r = nmem_malloc(nmem, sizeof(struct conf_config));
491
492     r->servers = 0;
493     r->targetprofiles = 0;
494
495     for (n = root->children; n; n = n->next)
496     {
497         if (n->type != XML_ELEMENT_NODE)
498             continue;
499         if (!strcmp((const char *) n->name, "server"))
500         {
501             struct conf_server *tmp = parse_server(n);
502             if (!tmp)
503                 return 0;
504             tmp->next = r->servers;
505             r->servers = tmp;
506         }
507         else if (!strcmp((const char *) n->name, "targetprofiles"))
508         {
509             // It would be fun to be able to fix this sometime
510             if (r->targetprofiles)
511             {
512                 yaz_log(YLOG_FATAL, "Can't repeat targetprofiles");
513                 return 0;
514             }
515             if (!(r->targetprofiles = parse_targetprofiles(n)))
516                 return 0;
517         }
518         else
519         {
520             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
521             return 0;
522         }
523     }
524     return r;
525 }
526
527 int read_config(const char *fname)
528 {
529     xmlDoc *doc = xmlParseFile(fname);
530     const char *p;
531
532     if (!nmem)  // Initialize
533     {
534         nmem = nmem_create();
535         xmlSubstituteEntitiesDefault(1);
536         xmlLoadExtDtdDefaultValue = 1;
537     }
538     if (!doc)
539     {
540         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
541         exit(1);
542     }
543     if ((p = strrchr(fname, '/')))
544     {
545         int len = p - fname;
546         strncpy(confdir, fname, len);
547         confdir[len] = '\0';
548     }
549     config = parse_config(xmlDocGetRootElement(doc));
550     xmlFreeDoc(doc);
551
552     if (config)
553         return 1;
554     else
555         return 0;
556 }
557
558
559 /*
560  * Local variables:
561  * c-basic-offset: 4
562  * indent-tabs-mode: nil
563  * End:
564  * vim: shiftwidth=4 tabstop=8 expandtab
565  */