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