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