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