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