ICU per metadata element (icu_chain attribute)
[pazpar2-moved-to-github.git] / src / pazpar2_config.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2011 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <string.h>
25 #include <assert.h>
26
27 #include <libxml/parser.h>
28 #include <libxml/tree.h>
29
30 #include <yaz/yaz-util.h>
31 #include <yaz/nmem.h>
32 #include <yaz/snprintf.h>
33 #include <yaz/tpath.h>
34 #include <yaz/xml_include.h>
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #include "ppmutex.h"
42 #include "incref.h"
43 #include "pazpar2_config.h"
44 #include "settings.h"
45 #include "eventl.h"
46 #include "http.h"
47
48 struct conf_config
49 {
50     NMEM nmem; /* for conf_config and servers memory */
51     struct conf_server *servers;
52     
53     int no_threads;
54     WRBUF confdir;
55     iochan_man_t iochan_man;
56     database_hosts_t database_hosts;
57 };
58
59
60 static char *parse_settings(struct conf_config *config,
61                             NMEM nmem, xmlNode *node);
62
63 static void conf_metadata_assign(NMEM nmem, 
64                                  struct conf_metadata * metadata,
65                                  const char *name,
66                                  enum conf_metadata_type type,
67                                  enum conf_metadata_merge merge,
68                                  enum conf_setting_type setting,
69                                  int brief,
70                                  int termlist,
71                                  int rank,
72                                  int sortkey_offset,
73                                  enum conf_metadata_mergekey mt,
74                                  const char *icu_chain)
75 {
76     assert(nmem && metadata && name);
77     
78     metadata->name = nmem_strdup(nmem, name);
79
80     metadata->type = type;
81
82     // enforcing that type_year is always range_merge
83     if (metadata->type == Metadata_type_year)
84         metadata->merge = Metadata_merge_range;
85     else
86         metadata->merge = merge;    
87
88     metadata->setting = setting;
89     metadata->brief = brief;   
90     metadata->termlist = termlist;
91     metadata->rank = rank;    
92     metadata->sortkey_offset = sortkey_offset;
93     metadata->mergekey = mt;
94     metadata->icu_chain = nmem_strdup_null(nmem, icu_chain);
95 }
96
97
98 static void conf_sortkey_assign(NMEM nmem, 
99                                 struct conf_sortkey * sortkey,
100                                 const char *name,
101                                 enum conf_sortkey_type type)
102 {
103     assert(nmem && sortkey && name);
104     
105     sortkey->name = nmem_strdup(nmem, name);
106     sortkey->type = type;
107 }
108
109
110 static struct conf_service *service_init(struct conf_server *server,
111                                          int num_metadata, int num_sortkeys,
112                                          const char *service_id)
113 {
114     struct conf_service * service = 0;
115     NMEM nmem = nmem_create();
116
117     service = nmem_malloc(nmem, sizeof(struct conf_service));
118     service->mutex = 0;
119     service->ref_count = 1;
120     service->nmem = nmem;
121     service->next = 0;
122     service->settings = 0;
123     service->databases = 0;
124     service->server = server;
125     service->session_timeout = 60; /* default session timeout */
126     service->z3950_session_timeout = 180;
127     service->z3950_operation_timeout = 30;
128
129     service->relevance_pct = 0;
130     service->sort_pct = 0;
131     service->mergekey_pct = 0;
132     service->facet_pct = 0;
133
134     service->id = service_id ? nmem_strdup(nmem, service_id) : 0;
135     service->num_metadata = num_metadata;
136     service->metadata = 0;
137     if (service->num_metadata)
138       service->metadata 
139           = nmem_malloc(nmem, 
140                         sizeof(struct conf_metadata) * service->num_metadata);
141     service->num_sortkeys = num_sortkeys;
142     service->sortkeys = 0;
143     if (service->num_sortkeys)
144         service->sortkeys 
145             = nmem_malloc(nmem, 
146                           sizeof(struct conf_sortkey) * service->num_sortkeys);
147     service->dictionary = 0;
148     return service; 
149 }
150
151 static struct conf_metadata* conf_service_add_metadata(
152     struct conf_service *service,
153     int field_id,
154     const char *name,
155     enum conf_metadata_type type,
156     enum conf_metadata_merge merge,
157     enum conf_setting_type setting,
158     int brief,
159     int termlist,
160     int rank,
161     int sortkey_offset,
162     enum conf_metadata_mergekey mt,
163     const char *icu_chain)
164 {
165     struct conf_metadata * md = 0;
166
167     if (!service || !service->metadata || !service->num_metadata
168         || field_id < 0  || !(field_id < service->num_metadata))
169         return 0;
170
171     md = service->metadata + field_id;
172     conf_metadata_assign(service->nmem, md, name, type, merge, setting,
173                          brief, termlist, rank, sortkey_offset,
174                          mt, icu_chain);
175     return md;
176 }
177
178
179 static struct conf_sortkey * conf_service_add_sortkey(
180     struct conf_service *service,
181     int field_id,
182     const char *name,
183     enum conf_sortkey_type type)
184 {
185     struct conf_sortkey * sk = 0;
186
187     if (!service || !service->sortkeys || !service->num_sortkeys
188         || field_id < 0  || !(field_id < service->num_sortkeys))
189         return 0;
190
191     //sk = &((service->sortkeys)[field_id]);
192     sk = service->sortkeys + field_id;
193     conf_sortkey_assign(service->nmem, sk, name, type);
194
195     return sk;
196 }
197
198
199 int conf_service_metadata_field_id(struct conf_service *service,
200                                    const char * name)
201 {
202     int i = 0;
203
204     if (!service || !service->metadata || !service->num_metadata)
205         return -1;
206
207     for(i = 0; i < service->num_metadata; i++) {
208         if (!strcmp(name, (service->metadata[i]).name))
209             return i;
210     }
211    
212     return -1;
213 }
214
215
216 int conf_service_sortkey_field_id(struct conf_service *service,
217                                   const char * name)
218 {
219     int i = 0;
220
221     if (!service || !service->sortkeys || !service->num_sortkeys)
222         return -1;
223
224     for(i = 0; i < service->num_sortkeys; i++) {
225         if (!strcmp(name, (service->sortkeys[i]).name))
226             return i;
227     }
228    
229     return -1;
230 }
231
232 static void conf_dir_path(struct conf_config *config, WRBUF w, const char *src)
233 {
234     if (config->confdir && wrbuf_len(config->confdir) > 0 &&
235         !yaz_is_abspath(src))
236     {
237         wrbuf_printf(w, "%s/%s", wrbuf_cstr(config->confdir), src);
238     }
239     else
240         wrbuf_puts(w, src);
241 }
242
243 void service_destroy(struct conf_service *service)
244 {
245     if (service)
246     {
247         if (!pazpar2_decref(&service->ref_count, service->mutex))
248         {
249             pp2_charset_destroy(service->relevance_pct);
250             pp2_charset_destroy(service->sort_pct);
251             pp2_charset_destroy(service->mergekey_pct);
252             pp2_charset_destroy(service->facet_pct);
253             yaz_mutex_destroy(&service->mutex);
254             nmem_destroy(service->nmem);
255         }
256     }
257 }
258
259 void service_incref(struct conf_service *service)
260 {
261     yaz_log(YLOG_LOG, "service_incref. p=%p cnt=%d", service,
262             service->ref_count);
263     pazpar2_incref(&service->ref_count, service->mutex);
264 }
265
266 static int parse_metadata(struct conf_service *service, xmlNode *n,
267                           int *md_node, int *sk_node)
268 {
269     enum conf_metadata_type type = Metadata_type_generic;
270     enum conf_metadata_merge merge = Metadata_merge_no;
271     enum conf_setting_type setting = Metadata_setting_no;
272     enum conf_sortkey_type sk_type = Metadata_sortkey_relevance;
273     enum conf_metadata_mergekey mergekey_type = Metadata_mergekey_no;
274     int brief = 0;
275     int termlist = 0;
276     int rank = 0;
277     int sortkey_offset = 0;
278     xmlChar *xml_name = 0;
279     xmlChar *xml_brief = 0;
280     xmlChar *xml_sortkey = 0;
281     xmlChar *xml_merge = 0;
282     xmlChar *xml_type = 0;
283     xmlChar *xml_termlist = 0;
284     xmlChar *xml_rank = 0;
285     xmlChar *xml_setting = 0;
286     xmlChar *xml_mergekey = 0;
287     xmlChar *xml_icu_chain = 0;
288     struct _xmlAttr *attr;
289     for (attr = n->properties; attr; attr = attr->next)
290     {
291         if (!xmlStrcmp(attr->name, BAD_CAST "name") &&
292             attr->children && attr->children->type == XML_TEXT_NODE)
293             xml_name = attr->children->content;
294         else if (!xmlStrcmp(attr->name, BAD_CAST "brief") &&
295                  attr->children && attr->children->type == XML_TEXT_NODE)
296             xml_brief = attr->children->content;
297         else if (!xmlStrcmp(attr->name, BAD_CAST "sortkey") &&
298                  attr->children && attr->children->type == XML_TEXT_NODE)
299             xml_sortkey = attr->children->content;
300         else if (!xmlStrcmp(attr->name, BAD_CAST "merge") &&
301                  attr->children && attr->children->type == XML_TEXT_NODE)
302             xml_merge = attr->children->content;
303         else if (!xmlStrcmp(attr->name, BAD_CAST "type") &&
304                  attr->children && attr->children->type == XML_TEXT_NODE)
305             xml_type = attr->children->content;
306         else if (!xmlStrcmp(attr->name, BAD_CAST "termlist") &&
307                  attr->children && attr->children->type == XML_TEXT_NODE)
308             xml_termlist = attr->children->content;
309         else if (!xmlStrcmp(attr->name, BAD_CAST "rank") &&
310                  attr->children && attr->children->type == XML_TEXT_NODE)
311             xml_rank = attr->children->content;
312         else if (!xmlStrcmp(attr->name, BAD_CAST "setting") &&
313                  attr->children && attr->children->type == XML_TEXT_NODE)
314             xml_setting = attr->children->content;
315         else if (!xmlStrcmp(attr->name, BAD_CAST "mergekey") &&
316                  attr->children && attr->children->type == XML_TEXT_NODE)
317             xml_mergekey = attr->children->content;
318         else if (!xmlStrcmp(attr->name, BAD_CAST "icu_chain") &&
319                  attr->children && attr->children->type == XML_TEXT_NODE)
320             xml_icu_chain = attr->children->content;
321         else
322         {
323             yaz_log(YLOG_FATAL, "Unknown metadata attribute '%s'", attr->name);
324             return -1;
325         }
326     }
327     
328     // now do the parsing logic
329     if (!xml_name)
330     {
331         yaz_log(YLOG_FATAL, "Must specify name in metadata element");
332         return -1;
333     }
334     if (xml_brief)
335     {
336         if (!strcmp((const char *) xml_brief, "yes"))
337             brief = 1;
338         else if (strcmp((const char *) xml_brief, "no"))
339         {
340             yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
341             return -1;
342         }
343     }
344     
345     if (xml_termlist)
346     {
347         if (!strcmp((const char *) xml_termlist, "yes"))
348             termlist = 1;
349         else if (strcmp((const char *) xml_termlist, "no"))
350         {
351             yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
352             return -1;
353         }
354     }
355     
356     if (xml_rank)
357         rank = atoi((const char *) xml_rank);
358
359     if (xml_type)
360     {
361         if (!strcmp((const char *) xml_type, "generic"))
362             type = Metadata_type_generic;
363         else if (!strcmp((const char *) xml_type, "year"))
364             type = Metadata_type_year;
365         else if (!strcmp((const char *) xml_type, "date"))
366             type = Metadata_type_date;
367         else
368         {
369             yaz_log(YLOG_FATAL, 
370                     "Unknown value for metadata/type: %s", xml_type);
371             return -1;
372         }
373     }
374     
375     if (xml_merge)
376     {
377         if (!strcmp((const char *) xml_merge, "no"))
378             merge = Metadata_merge_no;
379         else if (!strcmp((const char *) xml_merge, "unique"))
380             merge = Metadata_merge_unique;
381         else if (!strcmp((const char *) xml_merge, "longest"))
382             merge = Metadata_merge_longest;
383         else if (!strcmp((const char *) xml_merge, "range"))
384             merge = Metadata_merge_range;
385         else if (!strcmp((const char *) xml_merge, "all"))
386             merge = Metadata_merge_all;
387         else
388         {
389             yaz_log(YLOG_FATAL, 
390                     "Unknown value for metadata/merge: %s", xml_merge);
391             return -1;
392         }
393     }
394     
395     if (xml_setting)
396     {
397         if (!strcmp((const char *) xml_setting, "no"))
398             setting = Metadata_setting_no;
399         else if (!strcmp((const char *) xml_setting, "postproc"))
400             setting = Metadata_setting_postproc;
401         else if (!strcmp((const char *) xml_setting, "parameter"))
402             setting = Metadata_setting_parameter;
403         else
404         {
405             yaz_log(YLOG_FATAL,
406                     "Unknown value for medadata/setting: %s", xml_setting);
407             return -1;
408         }
409     }
410     
411     // add a sortkey if so specified
412     if (xml_sortkey && strcmp((const char *) xml_sortkey, "no"))
413     {
414         if (merge == Metadata_merge_no)
415         {
416             yaz_log(YLOG_FATAL, 
417                     "Can't specify sortkey on a non-merged field");
418             return -1;
419         }
420         if (!strcmp((const char *) xml_sortkey, "numeric"))
421             sk_type = Metadata_sortkey_numeric;
422         else if (!strcmp((const char *) xml_sortkey, "skiparticle"))
423             sk_type = Metadata_sortkey_skiparticle;
424         else
425         {
426             yaz_log(YLOG_FATAL,
427                     "Unknown sortkey in metadata element: %s", 
428                     xml_sortkey);
429             return -1;
430         }
431         sortkey_offset = *sk_node;
432         
433         conf_service_add_sortkey(service, *sk_node,
434                                  (const char *) xml_name, sk_type);        
435         (*sk_node)++;
436     }
437     else
438         sortkey_offset = -1;
439     
440     if (xml_mergekey)
441     {
442         if (!strcmp((const char *) xml_mergekey, "required"))
443             mergekey_type = Metadata_mergekey_required;
444         else if (!strcmp((const char *) xml_mergekey, "optional"))
445             mergekey_type = Metadata_mergekey_optional;
446         else if (!strcmp((const char *) xml_mergekey, "no"))
447             mergekey_type = Metadata_mergekey_no;
448         else
449         {
450             yaz_log(YLOG_FATAL, "Unknown value for mergekey: %s", xml_mergekey);
451             return -1;
452         }
453     }
454
455     // metadata known, assign values
456     conf_service_add_metadata(service, *md_node,
457                               (const char *) xml_name,
458                               type, merge, setting,
459                               brief, termlist, rank, sortkey_offset,
460                               mergekey_type, (const char *) xml_icu_chain);
461     (*md_node)++;
462     return 0;
463 }
464
465 static struct conf_service *service_create_static(struct conf_server *server,
466                                                   xmlNode *node,
467                                                   const char *service_id)
468 {
469     xmlNode *n;
470     int md_node = 0;
471     int sk_node = 0;
472
473     struct conf_service *service = 0;
474     int num_metadata = 0;
475     int num_sortkeys = 0;
476     int got_settings = 0;
477     
478     // count num_metadata and num_sortkeys
479     for (n = node->children; n; n = n->next)
480         if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)
481                                                    n->name, "metadata"))
482         {
483             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
484             num_metadata++;
485             if (sortkey && strcmp((const char *) sortkey, "no"))
486                 num_sortkeys++;
487             xmlFree(sortkey);
488         }
489
490     service = service_init(server, num_metadata, num_sortkeys, service_id);
491
492     for (n = node->children; n; n = n->next)
493     {
494         if (n->type != XML_ELEMENT_NODE)
495             continue;
496         if (!strcmp((const char *) n->name, "timeout"))
497         {
498             xmlChar *src = xmlGetProp(n, (xmlChar *) "session");
499             if (src)
500             {
501                 service->session_timeout = atoi((const char *) src);
502                 xmlFree(src);
503                 if (service->session_timeout < 9)
504                 {
505                     yaz_log(YLOG_FATAL, "session timeout out of range");
506                     return 0;
507                 }
508             }
509             src = xmlGetProp(n, (xmlChar *) "z3950_operation");
510             if (src)
511             {
512                 service->z3950_operation_timeout = atoi((const char *) src);
513                 xmlFree(src);
514                 if (service->z3950_session_timeout < 9)
515                 {
516                     yaz_log(YLOG_FATAL, "Z39.50 operation timeout out of range");
517                     return 0;
518                 }
519             }
520             src = xmlGetProp(n, (xmlChar *) "z3950_session");
521             if (src)
522             {
523                 service->z3950_session_timeout = atoi((const char *) src);
524                 xmlFree(src);
525                 if (service->z3950_session_timeout < 9)
526                 {
527                     yaz_log(YLOG_FATAL, "Z39.50 session timeout out of range");
528                     return 0;
529                 }
530             }
531         }
532         else if (!strcmp((const char *) n->name, "settings"))
533             got_settings++;
534         else if (!strcmp((const char *) n->name, "relevance"))
535         {
536             if (service->relevance_pct)
537             {
538                 yaz_log(YLOG_LOG, "relevance may not repeat in service");
539                 return 0;
540             }
541             else
542             {
543                 service->relevance_pct = pp2_charset_create_xml(n);
544                 if (!service->relevance_pct)
545                     return 0;
546             }
547         }
548         else if (!strcmp((const char *) n->name, "sort"))
549         {
550             if (service->sort_pct)
551             {
552                 yaz_log(YLOG_LOG, "sort may not repeat in service");
553                 return 0;
554             }
555             else
556             {
557                 service->sort_pct = pp2_charset_create_xml(n);
558                 if (!service->sort_pct)
559                     return 0;
560             }
561         }
562         else if (!strcmp((const char *) n->name, "mergekey"))
563         {
564             if (service->mergekey_pct)
565             {
566                 yaz_log(YLOG_LOG, "mergekey may not repeat in service");
567                 return 0;
568             }
569             else
570             {
571                 service->mergekey_pct = pp2_charset_create_xml(n);
572                 if (!service->mergekey_pct)
573                     return 0;
574             }
575         }
576         else if (!strcmp((const char *) n->name, "facet"))
577         {
578             if (service->facet_pct)
579             {
580                 yaz_log(YLOG_LOG, "facet may not repeat in service");
581                 return 0;
582             }
583             else
584             {
585                 service->facet_pct = pp2_charset_create_xml(n);
586                 if (!service->facet_pct)
587                     return 0;
588             }
589         }
590         else if (!strcmp((const char *) n->name, (const char *) "metadata"))
591         {
592             if (parse_metadata(service, n, &md_node, &sk_node))
593                 return 0;
594         }
595         else
596         {
597             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
598             return 0;
599         }
600     }
601     if (got_settings)
602     {
603         int pass;
604         /* metadata has been read.. Consider now settings */
605         init_settings(service);
606         for (pass = 1; pass <= 2; pass++)
607         {
608             for (n = node->children; n; n = n->next)
609             {
610                 if (n->type != XML_ELEMENT_NODE)
611                     continue;
612                 if (!strcmp((const char *) n->name, "settings"))
613                 {
614                     xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
615                     if (src)
616                     {
617                         WRBUF w = wrbuf_alloc();
618                         conf_dir_path(server->config, w, (const char *) src);
619                         settings_read_file(service, wrbuf_cstr(w), pass);
620                         wrbuf_destroy(w);
621                         xmlFree(src);
622                     }
623                     else
624                     {
625                         settings_read_node(service, n, pass);
626                     }
627                 }
628             }
629         }
630     }
631     return service;
632 }
633
634 static char *parse_settings(struct conf_config *config,
635                             NMEM nmem, xmlNode *node)
636 {
637     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
638     char *r;
639
640     if (src)
641     {
642         WRBUF w = wrbuf_alloc();
643         conf_dir_path(config, w, (const char *) src);
644         r = nmem_strdup(nmem, wrbuf_cstr(w));
645         wrbuf_destroy(w);
646     }
647     else
648     {
649         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
650         return 0;
651     }
652     xmlFree(src);
653     return r;
654 }
655
656 static void inherit_server_settings(struct conf_service *s)
657 {
658     struct conf_server *server = s->server;
659     if (!s->dictionary) /* service has no config settings ? */
660     {
661         if (server->server_settings)
662         {
663             /* inherit settings from server */
664             init_settings(s);
665             settings_read_file(s, server->server_settings, 1);
666             settings_read_file(s, server->server_settings, 2);
667         }
668         else
669         {
670             yaz_log(YLOG_WARN, "service '%s' has no settings",
671                     s->id ? s->id : "unnamed");
672             init_settings(s);
673         }
674     }
675     
676     /* use relevance/sort/mergekey/facet from server if not defined
677        for this service.. */
678     if (!s->relevance_pct)
679     {
680         if (server->relevance_pct)
681         {
682             s->relevance_pct = server->relevance_pct;
683             pp2_charset_incref(s->relevance_pct);
684         }
685         else
686             s->relevance_pct = pp2_charset_create_a_to_z();
687     }
688     
689     if (!s->sort_pct)
690     {
691         if (server->sort_pct)
692         {
693             s->sort_pct = server->sort_pct;
694             pp2_charset_incref(s->sort_pct);
695         }
696         else
697             s->sort_pct = pp2_charset_create_a_to_z();
698     }
699     
700     if (!s->mergekey_pct)
701     {
702         if (server->mergekey_pct)
703         {
704             s->mergekey_pct = server->mergekey_pct;
705             pp2_charset_incref(s->mergekey_pct);
706         }
707         else
708             s->mergekey_pct = pp2_charset_create_a_to_z();
709     }
710
711     if (!s->facet_pct)
712     {
713         if (server->facet_pct)
714         {
715             s->facet_pct = server->facet_pct;
716             pp2_charset_incref(s->facet_pct);
717         }
718         else
719             s->facet_pct = pp2_charset_create(0);
720     }
721 }
722
723 struct conf_service *service_create(struct conf_server *server,
724                                     xmlNode *node)
725 {
726     struct conf_service *service = service_create_static(server,
727                                                          node, 0);
728     if (service)
729     {
730         inherit_server_settings(service);
731         resolve_databases(service);
732         assert(service->mutex == 0);
733         pazpar2_mutex_create(&service->mutex, "conf");
734     }
735     return service;
736 }
737
738 static struct conf_server *server_create(struct conf_config *config,
739                                          NMEM nmem, xmlNode *node)
740 {
741     xmlNode *n;
742     struct conf_server *server = nmem_malloc(nmem, sizeof(struct conf_server));
743     xmlChar *server_id = xmlGetProp(node, (xmlChar *) "id");
744
745     server->host = 0;
746     server->port = 0;
747     server->proxy_host = 0;
748     server->proxy_port = 0;
749     server->myurl = 0;
750     server->service = 0;
751     server->config = config;
752     server->next = 0;
753     server->relevance_pct = 0;
754     server->sort_pct = 0;
755     server->mergekey_pct = 0;
756     server->facet_pct = 0;
757     server->server_settings = 0;
758     server->http_server = 0;
759     server->iochan_man = 0;
760     server->database_hosts = 0;
761
762     if (server_id)
763     {
764         server->server_id = nmem_strdup(nmem, (const char *)server_id);
765         xmlFree(server_id);
766     }
767     else
768         server->server_id = 0;
769     for (n = node->children; n; n = n->next)
770     {
771         if (n->type != XML_ELEMENT_NODE)
772             continue;
773         if (!strcmp((const char *) n->name, "listen"))
774         {
775             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
776             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
777             if (port)
778                 server->port = atoi((const char *) port);
779             if (host)
780                 server->host = nmem_strdup(nmem, (const char *) host);
781             xmlFree(port);
782             xmlFree(host);
783         }
784         else if (!strcmp((const char *) n->name, "proxy"))
785         {
786             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
787             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
788             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
789             if (port)
790                 server->proxy_port = atoi((const char *) port);
791             if (host)
792                 server->proxy_host = nmem_strdup(nmem, (const char *) host);
793             if (myurl)
794                 server->myurl = nmem_strdup(nmem, (const char *) myurl);
795             xmlFree(port);
796             xmlFree(host);
797             xmlFree(myurl);
798         }
799         else if (!strcmp((const char *) n->name, "settings"))
800         {
801             if (server->server_settings)
802             {
803                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
804                 return 0;
805             }
806             if (!(server->server_settings = parse_settings(config, nmem, n)))
807                 return 0;
808         }
809         else if (!strcmp((const char *) n->name, "relevance"))
810         {
811             server->relevance_pct = pp2_charset_create_xml(n);
812             if (!server->relevance_pct)
813                 return 0;
814         }
815         else if (!strcmp((const char *) n->name, "sort"))
816         {
817             server->sort_pct = pp2_charset_create_xml(n);
818             if (!server->sort_pct)
819                 return 0;
820         }
821         else if (!strcmp((const char *) n->name, "mergekey"))
822         {
823             server->mergekey_pct = pp2_charset_create_xml(n);
824             if (!server->mergekey_pct)
825                 return 0;
826         }
827         else if (!strcmp((const char *) n->name, "facet"))
828         {
829             server->facet_pct = pp2_charset_create_xml(n);
830             if (!server->facet_pct)
831                 return 0;
832         }
833         else if (!strcmp((const char *) n->name, "service"))
834         {
835             char *service_id = (char *)
836                 xmlGetProp(n, (xmlChar *) "id");
837
838             struct conf_service **sp = &server->service;
839             for (; *sp; sp = &(*sp)->next)
840                 if ((*sp)->id && service_id &&
841                     0 == strcmp((*sp)->id, service_id))
842                 {
843                     yaz_log(YLOG_FATAL, "Duplicate service: %s", service_id);
844                     break;
845                 }
846                 else if (!(*sp)->id && !service_id)
847                 {
848                     yaz_log(YLOG_FATAL, "Duplicate unnamed service");
849                     break;
850                 }
851
852             if (*sp)  /* service already exist */
853             {
854                 xmlFree(service_id);
855                 return 0;
856             }
857             else
858             {
859                 struct conf_service *s = service_create_static(server, n,
860                                                                service_id);
861                 xmlFree(service_id);
862                 if (!s)
863                     return 0;
864                 *sp = s;
865             }
866         }
867         else
868         {
869             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
870             return 0;
871         }
872     }
873     if (server->service)
874     {
875         struct conf_service *s;
876         for (s = server->service; s; s = s->next)
877             inherit_server_settings(s);
878     }
879     return server;
880 }
881
882 WRBUF conf_get_fname(struct conf_config *config, const char *fname)
883 {
884     WRBUF w = wrbuf_alloc();
885
886     conf_dir_path(config, w, fname);
887     return w;
888 }
889
890 struct conf_service *locate_service(struct conf_server *server,
891                                     const char *service_id)
892 {
893     struct conf_service *s = server->service;
894     for (; s; s = s->next)
895         if (s->id && service_id && 0 == strcmp(s->id, service_id))
896             break;
897         else if (!s->id && !service_id)
898             break;
899     if (s)
900         service_incref(s);
901     return s;
902 }
903
904 void info_services(struct conf_server *server, WRBUF w)
905 {
906     struct conf_service *s = server->service;
907     wrbuf_puts(w, " <services>\n");
908     for (; s; s = s->next)
909     {
910         wrbuf_puts(w, "  <service");
911         if (s->id)
912         {
913             wrbuf_puts(w, " id=\"");
914             wrbuf_xmlputs(w, s->id);
915             wrbuf_puts(w, "\"");
916         }
917         wrbuf_puts(w, "/>");
918
919         wrbuf_puts(w, "\n");
920     }
921     wrbuf_puts(w, " </services>\n");
922 }
923
924 static int parse_config(struct conf_config *config, xmlNode *root)
925 {
926     xmlNode *n;
927
928     for (n = root->children; n; n = n->next)
929     {
930         if (n->type != XML_ELEMENT_NODE)
931             continue;
932         if (!strcmp((const char *) n->name, "server"))
933         {
934             struct conf_server *tmp = server_create(config, config->nmem, n);
935             if (!tmp)
936                 return -1;
937             tmp->next = config->servers;
938             config->servers = tmp;
939         }
940         else if (!strcmp((const char *) n->name, "threads"))
941         {
942             xmlChar *number = xmlGetProp(n, (xmlChar *) "number");
943             if (number)
944             {
945                 config->no_threads = atoi((const char *) number);
946                 xmlFree(number);
947             }
948         }
949         else if (!strcmp((const char *) n->name, "targetprofiles"))
950         {
951             yaz_log(YLOG_FATAL, "targetprofiles unsupported here. Must be part of service");
952             return -1;
953
954         }
955         else
956         {
957             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
958             return -1;
959         }
960     }
961     return 0;
962 }
963
964 struct conf_config *config_create(const char *fname, int verbose)
965 {
966     xmlDoc *doc = xmlParseFile(fname);
967     xmlNode *n;
968     const char *p;
969     int r;
970     NMEM nmem = nmem_create();
971     struct conf_config *config = nmem_malloc(nmem, sizeof(struct conf_config));
972
973     xmlSubstituteEntitiesDefault(1);
974     xmlLoadExtDtdDefaultValue = 1;
975     if (!doc)
976     {
977         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
978         nmem_destroy(nmem);
979         return 0;
980     }
981
982     config->nmem = nmem;
983     config->servers = 0;
984     config->no_threads = 0;
985     config->iochan_man = 0;
986
987     config->confdir = wrbuf_alloc();
988     if ((p = strrchr(fname, 
989 #ifdef WIN32
990                      '\\'
991 #else
992                      '/'
993 #endif
994              )))
995     {
996         int len = p - fname;
997         wrbuf_write(config->confdir, fname, len);
998     }
999     wrbuf_puts(config->confdir, "");
1000     
1001     n = xmlDocGetRootElement(doc);
1002     r = yaz_xml_include_simple(n, wrbuf_cstr(config->confdir));
1003     if (r == 0) /* OK */
1004     {
1005         if (verbose)
1006         {
1007             yaz_log(YLOG_LOG, "Configuration %s after include processing",
1008                     fname);
1009 #if LIBXML_VERSION >= 20600
1010             xmlDocFormatDump(yaz_log_file(), doc, 0);
1011 #else
1012             xmlDocDump(yaz_log_file(), doc);
1013 #endif
1014         }
1015         r = parse_config(config, n);
1016     }
1017     xmlFreeDoc(doc);
1018
1019     if (r)
1020     {
1021         config_destroy(config);
1022         return 0;
1023     }
1024     return config;
1025 }
1026
1027 void server_destroy(struct conf_server *server)
1028 {
1029     struct conf_service *s = server->service;
1030     while (s)
1031     {
1032         struct conf_service *s_next = s->next;
1033         service_destroy(s);
1034         s = s_next;
1035     }
1036     pp2_charset_destroy(server->relevance_pct);
1037     pp2_charset_destroy(server->sort_pct);
1038     pp2_charset_destroy(server->mergekey_pct);
1039     pp2_charset_destroy(server->facet_pct);
1040     yaz_log(YLOG_LOG, "server_destroy server=%p", server);
1041     http_server_destroy(server->http_server);
1042 }
1043
1044 void config_destroy(struct conf_config *config)
1045 {
1046     if (config)
1047     {
1048         struct conf_server *server = config->servers;
1049         iochan_man_destroy(&config->iochan_man);    
1050         while (server)
1051         {
1052             struct conf_server *s_next = server->next;
1053             server_destroy(server);
1054             server = s_next;
1055         }
1056         database_hosts_destroy(&config->database_hosts);
1057
1058         wrbuf_destroy(config->confdir);
1059         nmem_destroy(config->nmem);
1060     }
1061 }
1062
1063 void config_stop_listeners(struct conf_config *conf)
1064 {
1065     struct conf_server *ser;
1066     for (ser = conf->servers; ser; ser = ser->next)
1067         http_close_server(ser);
1068 }
1069
1070 void config_process_events(struct conf_config *conf)
1071 {
1072     struct conf_server *ser;
1073     
1074     conf->database_hosts = database_hosts_create();
1075     for (ser = conf->servers; ser; ser = ser->next)
1076     {
1077         struct conf_service *s = ser->service;
1078
1079         ser->database_hosts = conf->database_hosts;
1080
1081         for (;s ; s = s->next)
1082         {
1083             resolve_databases(s);
1084             assert(s->mutex == 0);
1085             pazpar2_mutex_create(&s->mutex, "service");
1086         }
1087         http_mutex_init(ser);
1088     }
1089     iochan_man_events(conf->iochan_man);    
1090 }
1091
1092 int config_start_listeners(struct conf_config *conf,
1093                            const char *listener_override,
1094                            const char *record_fname)
1095 {
1096     struct conf_server *ser;
1097
1098     conf->iochan_man = iochan_man_create(conf->no_threads);
1099     for (ser = conf->servers; ser; ser = ser->next)
1100     {
1101         WRBUF w = wrbuf_alloc();
1102         int r;
1103
1104         ser->iochan_man = conf->iochan_man;
1105         if (listener_override)
1106         {
1107             wrbuf_puts(w, listener_override);
1108             listener_override = 0; /* only first server is overriden */
1109         }
1110         else
1111         {
1112             if (ser->host)
1113                 wrbuf_puts(w, ser->host);
1114             if (ser->port)
1115             {
1116                 if (wrbuf_len(w))
1117                     wrbuf_puts(w, ":");
1118                 wrbuf_printf(w, "%d", ser->port);
1119             }
1120         }
1121         r = http_init(wrbuf_cstr(w), ser, record_fname);
1122         wrbuf_destroy(w);
1123         if (r)
1124             return -1;
1125
1126         w = wrbuf_alloc();
1127         if (ser->proxy_host || ser->proxy_port)
1128         {
1129             if (ser->proxy_host)
1130                 wrbuf_puts(w, ser->proxy_host);
1131             if (ser->proxy_port)
1132             {
1133                 if (wrbuf_len(w))
1134                     wrbuf_puts(w, ":");
1135                 wrbuf_printf(w, "%d", ser->proxy_port);
1136             }
1137         }
1138         if (wrbuf_len(w))
1139             http_set_proxyaddr(wrbuf_cstr(w), ser);
1140         wrbuf_destroy(w);
1141     }
1142     return 0;
1143 }
1144
1145 /*
1146  * Local variables:
1147  * c-basic-offset: 4
1148  * c-file-style: "Stroustrup"
1149  * indent-tabs-mode: nil
1150  * End:
1151  * vim: shiftwidth=4 tabstop=8 expandtab
1152  */
1153