Embeddable settings (repeatable).
[pazpar2-moved-to-github.git] / src / pazpar2_config.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2009 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 #include <libxslt/xslt.h>
30 #include <libxslt/transform.h>
31 #include <libxslt/xsltutils.h>
32
33 #include <yaz/yaz-util.h>
34 #include <yaz/nmem.h>
35 #include <yaz/snprintf.h>
36 #include <yaz/tpath.h>
37
38 #if HAVE_GLOB_H
39 #define USE_POSIX_GLOB 1
40 #else
41 #define USE_POSIX_GLOB 0
42 #endif
43
44
45 #if USE_POSIX_GLOB
46 #include <glob.h>
47 #endif
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #if HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 #include "pazpar2_config.h"
54 #include "settings.h"
55 #include "eventl.h"
56 #include "http.h"
57
58 struct conf_config
59 {
60     NMEM nmem; /* for conf_config and servers memory */
61     struct conf_server *servers;
62     WRBUF confdir;
63 };
64
65
66 static char *parse_settings(struct conf_config *config,
67                             NMEM nmem, xmlNode *node);
68
69 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
70                                                         xmlNode *node);
71
72 static void conf_metadata_assign(NMEM nmem, 
73                                  struct conf_metadata * metadata,
74                                  const char *name,
75                                  enum conf_metadata_type type,
76                                  enum conf_metadata_merge merge,
77                                  enum conf_setting_type setting,
78                                  int brief,
79                                  int termlist,
80                                  int rank,
81                                  int sortkey_offset,
82                                  enum conf_metadata_mergekey mt)
83 {
84     assert(nmem && metadata && name);
85     
86     metadata->name = nmem_strdup(nmem, name);
87
88     metadata->type = type;
89
90     // enforcing that type_year is always range_merge
91     if (metadata->type == Metadata_type_year)
92         metadata->merge = Metadata_merge_range;
93     else
94         metadata->merge = merge;    
95
96     metadata->setting = setting;
97     metadata->brief = brief;   
98     metadata->termlist = termlist;
99     metadata->rank = rank;    
100     metadata->sortkey_offset = sortkey_offset;
101     metadata->mergekey = mt;
102 }
103
104
105 static void conf_sortkey_assign(NMEM nmem, 
106                                 struct conf_sortkey * sortkey,
107                                 const char *name,
108                                 enum conf_sortkey_type type)
109 {
110     assert(nmem && sortkey && name);
111     
112     sortkey->name = nmem_strdup(nmem, name);
113     sortkey->type = type;
114 }
115
116
117 struct conf_service *conf_service_create(struct conf_config *config,
118                                          int num_metadata, int num_sortkeys,
119                                           const char *service_id)
120 {
121     struct conf_service * service = 0;
122     NMEM nmem = nmem_create();
123
124     service = nmem_malloc(nmem, sizeof(struct conf_service));
125     service->nmem = nmem;
126     service->next = 0;
127     service->settings = 0;
128     service->databases = 0;
129     service->targetprofiles = 0;
130     service->config = config;
131
132     service->id = service_id ? nmem_strdup(nmem, service_id) : 0;
133     service->num_metadata = num_metadata;
134     service->metadata = 0;
135     if (service->num_metadata)
136       service->metadata 
137           = nmem_malloc(nmem, 
138                         sizeof(struct conf_metadata) * service->num_metadata);
139     service->num_sortkeys = num_sortkeys;
140     service->sortkeys = 0;
141     if (service->num_sortkeys)
142         service->sortkeys 
143             = nmem_malloc(nmem, 
144                           sizeof(struct conf_sortkey) * service->num_sortkeys);
145     service->dictionary = 0;
146     return service; 
147 }
148
149 struct conf_metadata* conf_service_add_metadata(struct conf_service *service,
150                                                 int field_id,
151                                                 const char *name,
152                                                 enum conf_metadata_type type,
153                                                 enum conf_metadata_merge merge,
154                                                 enum conf_setting_type setting,
155                                                 int brief,
156                                                 int termlist,
157                                                 int rank,
158                                                 int sortkey_offset,
159                                                 enum conf_metadata_mergekey mt)
160 {
161     struct conf_metadata * md = 0;
162
163     if (!service || !service->metadata || !service->num_metadata
164         || field_id < 0  || !(field_id < service->num_metadata))
165         return 0;
166
167     md = service->metadata + field_id;
168     conf_metadata_assign(service->nmem, md, name, type, merge, setting,
169                          brief, termlist, rank, sortkey_offset,
170                          mt);
171     return md;
172 }
173
174
175 struct conf_sortkey * conf_service_add_sortkey(struct conf_service *service,
176                                                int field_id,
177                                                const char *name,
178                                                enum conf_sortkey_type type)
179 {
180     struct conf_sortkey * sk = 0;
181
182     if (!service || !service->sortkeys || !service->num_sortkeys
183         || field_id < 0  || !(field_id < service->num_sortkeys))
184         return 0;
185
186     //sk = &((service->sortkeys)[field_id]);
187     sk = service->sortkeys + field_id;
188     conf_sortkey_assign(service->nmem, sk, name, type);
189
190     return sk;
191 }
192
193
194 int conf_service_metadata_field_id(struct conf_service *service,
195                                    const char * name)
196 {
197     int i = 0;
198
199     if (!service || !service->metadata || !service->num_metadata)
200         return -1;
201
202     for(i = 0; i < service->num_metadata; i++) {
203         if (!strcmp(name, (service->metadata[i]).name))
204             return i;
205     }
206    
207     return -1;
208 }
209
210
211 int conf_service_sortkey_field_id(struct conf_service *service,
212                                   const char * name)
213 {
214     int i = 0;
215
216     if (!service || !service->sortkeys || !service->num_sortkeys)
217         return -1;
218
219     for(i = 0; i < service->num_sortkeys; i++) {
220         if (!strcmp(name, (service->sortkeys[i]).name))
221             return i;
222     }
223    
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 static void service_destroy(struct conf_service *service)
239 {
240     if (service)
241     {
242         pp2_charset_destroy(service->relevance_pct);
243         pp2_charset_destroy(service->sort_pct);
244         pp2_charset_destroy(service->mergekey_pct);
245         nmem_destroy(service->nmem);
246     }
247 }
248
249 static struct conf_service *service_create(struct conf_config *config,
250                                            xmlNode *node,
251                                            const char *service_id,
252                                            const char *server_settings)
253 {
254     xmlNode *n;
255     int md_node = 0;
256     int sk_node = 0;
257
258     struct conf_service *service = 0;
259     int num_metadata = 0;
260     int num_sortkeys = 0;
261     int got_settings = 0;
262     
263     // count num_metadata and num_sortkeys
264     for (n = node->children; n; n = n->next)
265         if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)
266                                                    n->name, "metadata"))
267         {
268             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
269             num_metadata++;
270             if (sortkey && strcmp((const char *) sortkey, "no"))
271                 num_sortkeys++;
272             xmlFree(sortkey);
273         }
274
275     service = conf_service_create(config,
276                                   num_metadata, num_sortkeys, service_id);
277
278     for (n = node->children; n; n = n->next)
279     {
280         if (n->type != XML_ELEMENT_NODE)
281             continue;
282         if (!strcmp((const char *) n->name, "settings"))
283             got_settings++;
284         else if (!strcmp((const char *) n->name, (const char *) "targetprofiles"))
285         {
286             if (service->targetprofiles)
287             {
288                 yaz_log(YLOG_FATAL, "Can't repeat targetprofiles");
289                 return 0;
290             }
291             if (!(service->targetprofiles = 
292                   parse_targetprofiles(service->nmem, n)))
293                 return 0;
294         }
295         else if (!strcmp((const char *) n->name, (const char *) "metadata"))
296         {
297             xmlChar *xml_name = xmlGetProp(n, (xmlChar *) "name");
298             xmlChar *xml_brief = xmlGetProp(n, (xmlChar *) "brief");
299             xmlChar *xml_sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
300             xmlChar *xml_merge = xmlGetProp(n, (xmlChar *) "merge");
301             xmlChar *xml_type = xmlGetProp(n, (xmlChar *) "type");
302             xmlChar *xml_termlist = xmlGetProp(n, (xmlChar *) "termlist");
303             xmlChar *xml_rank = xmlGetProp(n, (xmlChar *) "rank");
304             xmlChar *xml_setting = xmlGetProp(n, (xmlChar *) "setting");
305             xmlChar *xml_mergekey = xmlGetProp(n, (xmlChar *) "mergekey");
306
307             enum conf_metadata_type type = Metadata_type_generic;
308             enum conf_metadata_merge merge = Metadata_merge_no;
309             enum conf_setting_type setting = Metadata_setting_no;
310             enum conf_sortkey_type sk_type = Metadata_sortkey_relevance;
311             enum conf_metadata_mergekey mergekey_type = Metadata_mergekey_no;
312             int brief = 0;
313             int termlist = 0;
314             int rank = 0;
315             int sortkey_offset = 0;
316             
317             // now do the parsing logic
318             if (!xml_name)
319             {
320                 yaz_log(YLOG_FATAL, "Must specify name in metadata element");
321                 return 0;
322             }
323             if (xml_brief)
324             {
325                 if (!strcmp((const char *) xml_brief, "yes"))
326                     brief = 1;
327                  else if (strcmp((const char *) xml_brief, "no"))
328                 {
329                     yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
330                     return 0;
331                 }
332             }
333             else
334                 brief = 0;
335
336             if (xml_termlist)
337             {
338                 if (!strcmp((const char *) xml_termlist, "yes"))
339                     termlist = 1;
340                 else if (strcmp((const char *) xml_termlist, "no"))
341                 {
342                     yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
343                     return 0;
344                 }
345             }
346             else
347                 termlist = 0;
348
349             if (xml_rank)
350                 rank = atoi((const char *) xml_rank);
351             else
352                 rank = 0;
353
354             if (xml_type)
355             {
356                 if (!strcmp((const char *) xml_type, "generic"))
357                     type = Metadata_type_generic;
358                 else if (!strcmp((const char *) xml_type, "year"))
359                     type = Metadata_type_year;
360                 else if (!strcmp((const char *) xml_type, "date"))
361                     type = Metadata_type_date;
362                 else
363                 {
364                     yaz_log(YLOG_FATAL, 
365                             "Unknown value for metadata/type: %s", xml_type);
366                     return 0;
367                 }
368             }
369             else
370                 type = Metadata_type_generic;
371
372             if (xml_merge)
373             {
374                 if (!strcmp((const char *) xml_merge, "no"))
375                     merge = Metadata_merge_no;
376                 else if (!strcmp((const char *) xml_merge, "unique"))
377                     merge = Metadata_merge_unique;
378                 else if (!strcmp((const char *) xml_merge, "longest"))
379                     merge = Metadata_merge_longest;
380                 else if (!strcmp((const char *) xml_merge, "range"))
381                     merge = Metadata_merge_range;
382                 else if (!strcmp((const char *) xml_merge, "all"))
383                     merge = Metadata_merge_all;
384                 else
385                 {
386                     yaz_log(YLOG_FATAL, 
387                             "Unknown value for metadata/merge: %s", xml_merge);
388                     return 0;
389                 }
390             }
391             else
392                 merge = Metadata_merge_no;
393
394             if (xml_setting)
395             {
396                 if (!strcmp((const char *) xml_setting, "no"))
397                     setting = Metadata_setting_no;
398                 else if (!strcmp((const char *) xml_setting, "postproc"))
399                     setting = Metadata_setting_postproc;
400                 else if (!strcmp((const char *) xml_setting, "parameter"))
401                     setting = Metadata_setting_parameter;
402                 else
403                 {
404                     yaz_log(YLOG_FATAL,
405                         "Unknown value for medadata/setting: %s", xml_setting);
406                     return 0;
407                 }
408             }
409
410             // add a sortkey if so specified
411             if (xml_sortkey && strcmp((const char *) xml_sortkey, "no"))
412             {
413                 if (merge == Metadata_merge_no)
414                 {
415                     yaz_log(YLOG_FATAL, 
416                             "Can't specify sortkey on a non-merged field");
417                     return 0;
418                 }
419                 if (!strcmp((const char *) xml_sortkey, "numeric"))
420                     sk_type = Metadata_sortkey_numeric;
421                 else if (!strcmp((const char *) xml_sortkey, "skiparticle"))
422                     sk_type = Metadata_sortkey_skiparticle;
423                 else
424                 {
425                     yaz_log(YLOG_FATAL,
426                             "Unknown sortkey in metadata element: %s", 
427                             xml_sortkey);
428                     return 0;
429                 }
430                 sortkey_offset = sk_node;
431
432                 conf_service_add_sortkey(service, sk_node,
433                                          (const char *) xml_name, sk_type);
434                 
435                 sk_node++;
436             }
437             else
438                 sortkey_offset = -1;
439
440             if (xml_mergekey && strcmp((const char *) xml_mergekey, "no"))
441             {
442                 mergekey_type = Metadata_mergekey_yes;
443             }
444
445
446             // metadata known, assign values
447             conf_service_add_metadata(service, md_node,
448                                       (const char *) xml_name,
449                                       type, merge, setting,
450                                       brief, termlist, rank, sortkey_offset,
451                                       mergekey_type);
452
453             xmlFree(xml_name);
454             xmlFree(xml_brief);
455             xmlFree(xml_sortkey);
456             xmlFree(xml_merge);
457             xmlFree(xml_type);
458             xmlFree(xml_termlist);
459             xmlFree(xml_rank);
460             xmlFree(xml_setting);
461             md_node++;
462         }
463         else
464         {
465             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
466             return 0;
467         }
468     }
469     init_settings(service);
470     if (got_settings)
471     {
472         int pass;
473         /* metadata has been read.. Consider now settings */
474         for (pass = 1; pass <= 2; pass++)
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, "settings"))
481                 {
482                     xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
483                     if (src)
484                     {
485                         WRBUF w = wrbuf_alloc();
486                         conf_dir_path(config, w, (const char *) src);
487                         settings_read_file(service, wrbuf_cstr(w), pass);
488                         wrbuf_destroy(w);
489                         xmlFree(src);
490                     }
491                     else
492                     {
493                         settings_read_node(service, n, pass);
494                     }
495                 }
496             }
497         }
498     }
499     else
500     {
501         if (server_settings)
502         {
503             settings_read_file(service, server_settings, 1);
504             settings_read_file(service, server_settings, 2);
505         }
506     }
507     return service;
508 }
509
510 static char *parse_settings(struct conf_config *config,
511                             NMEM nmem, xmlNode *node)
512 {
513     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
514     char *r;
515
516     if (src)
517     {
518         WRBUF w = wrbuf_alloc();
519         conf_dir_path(config, w, (const char *) src);
520         r = nmem_strdup(nmem, wrbuf_cstr(w));
521         wrbuf_destroy(w);
522     }
523     else
524     {
525         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
526         return 0;
527     }
528     xmlFree(src);
529     return r;
530 }
531
532 static struct conf_server *parse_server(struct conf_config *config,
533                                         NMEM nmem, xmlNode *node)
534 {
535     xmlNode *n;
536     const char *server_settings = 0;
537     struct conf_server *server = nmem_malloc(nmem, sizeof(struct conf_server));
538
539     server->host = 0;
540     server->port = 0;
541     server->proxy_host = 0;
542     server->proxy_port = 0;
543     server->myurl = 0;
544     server->proxy_addr = 0;
545     server->service = 0;
546     server->next = 0;
547     server->relevance_pct = 0;
548     server->sort_pct = 0;
549     server->mergekey_pct = 0;
550
551     for (n = node->children; n; n = n->next)
552     {
553         if (n->type != XML_ELEMENT_NODE)
554             continue;
555         if (!strcmp((const char *) n->name, "listen"))
556         {
557             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
558             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
559             if (port)
560                 server->port = atoi((const char *) port);
561             if (host)
562                 server->host = nmem_strdup(nmem, (const char *) host);
563             xmlFree(port);
564             xmlFree(host);
565         }
566         else if (!strcmp((const char *) n->name, "proxy"))
567         {
568             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
569             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
570             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
571             if (port)
572                 server->proxy_port = atoi((const char *) port);
573             if (host)
574                 server->proxy_host = nmem_strdup(nmem, (const char *) host);
575             if (myurl)
576                 server->myurl = nmem_strdup(nmem, (const char *) myurl);
577             xmlFree(port);
578             xmlFree(host);
579             xmlFree(myurl);
580         }
581         else if (!strcmp((const char *) n->name, "settings"))
582         {
583             if (server_settings)
584             {
585                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
586                 return 0;
587             }
588             if (!(server_settings = parse_settings(config, nmem, n)))
589                 return 0;
590         }
591         else if (!strcmp((const char *) n->name, "relevance"))
592         {
593             server->relevance_pct = pp2_charset_create_xml(n);
594             if (!server->relevance_pct)
595                 return 0;
596         }
597         else if (!strcmp((const char *) n->name, "sort"))
598         {
599             server->sort_pct = pp2_charset_create_xml(n);
600             if (!server->sort_pct)
601                 return 0;
602         }
603         else if (!strcmp((const char *) n->name, "mergekey"))
604         {
605             server->mergekey_pct = pp2_charset_create_xml(n);
606             if (!server->mergekey_pct)
607                 return 0;
608         }
609         else if (!strcmp((const char *) n->name, "service"))
610         {
611             char *service_id = (char *)
612                 xmlGetProp(n, (xmlChar *) "id");
613
614             struct conf_service **sp = &server->service;
615             for (; *sp; sp = &(*sp)->next)
616                 if ((*sp)->id && service_id &&
617                     0 == strcmp((*sp)->id, service_id))
618                 {
619                     yaz_log(YLOG_FATAL, "Duplicate service: %s", service_id);
620                     break;
621                 }
622                 else if (!(*sp)->id && !service_id)
623                 {
624                     yaz_log(YLOG_FATAL, "Duplicate unnamed service");
625                     break;
626                 }
627
628             if (*sp)  /* service already exist */
629                 return 0;
630             else
631             {
632                 struct conf_service *s = service_create(config, n,
633                                                         service_id,
634                                                         server_settings);
635                 if (s)
636                 {
637                     if (server->relevance_pct)
638                     {
639                         s->relevance_pct = server->relevance_pct;
640                         pp2_charset_incref(s->relevance_pct);
641                     }
642                     else
643                         s->relevance_pct = pp2_charset_create(0);
644
645                     if (server->sort_pct)
646                     {
647                         s->sort_pct = server->sort_pct;
648                         pp2_charset_incref(s->sort_pct);
649                     }
650                     else
651                         s->sort_pct = pp2_charset_create(0);
652
653                     if (server->mergekey_pct)
654                     {
655                         s->mergekey_pct = server->mergekey_pct;
656                         pp2_charset_incref(s->mergekey_pct);
657                     }
658                     else
659                         s->mergekey_pct = pp2_charset_create(0);
660                     *sp = s;
661                 }
662             }
663             xmlFree(service_id);
664         }
665         else
666         {
667             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
668             return 0;
669         }
670     }
671     return server;
672 }
673
674 xsltStylesheet *conf_load_stylesheet(struct conf_config *config,
675                                      const char *fname)
676 {
677     WRBUF w = wrbuf_alloc();
678     xsltStylesheet *s;
679
680     conf_dir_path(config, w, fname);
681     s = xsltParseStylesheetFile((xmlChar *) wrbuf_cstr(w));
682     wrbuf_destroy(w);
683     return s;
684 }
685
686 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
687                                                         xmlNode *node)
688 {
689     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
690     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
691     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
692
693     memset(r, 0, sizeof(*r));
694
695     if (type)
696     {
697         if (!strcmp((const char *) type, "local"))
698             r->type = Targetprofiles_local;
699         else
700         {
701             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
702             return 0;
703         }
704     }
705     else
706     {
707         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
708         return 0;
709     }
710
711     if (src)
712         r->src = nmem_strdup(nmem, (const char *) src);
713     else
714     {
715         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
716         return 0;
717     }
718     xmlFree(type);
719     xmlFree(src);
720     return r;
721 }
722
723 struct conf_service *locate_service(struct conf_server *server,
724                                     const char *service_id)
725 {
726     struct conf_service *s = server->service;
727     for (; s; s = s->next)
728         if (s->id && service_id && 0 == strcmp(s->id, service_id))
729             return s;
730         else if (!s->id && !service_id)
731             return s;
732     return 0;
733 }
734
735
736 static int parse_config(struct conf_config *config, xmlNode *root)
737 {
738     xmlNode *n;
739
740     for (n = root->children; n; n = n->next)
741     {
742         if (n->type != XML_ELEMENT_NODE)
743             continue;
744         if (!strcmp((const char *) n->name, "server"))
745         {
746             struct conf_server *tmp = parse_server(config, config->nmem, n);
747             if (!tmp)
748                 return -1;
749             tmp->next = config->servers;
750             config->servers = tmp;
751         }
752         else if (!strcmp((const char *) n->name, "targetprofiles"))
753         {
754             yaz_log(YLOG_FATAL, "targetprofiles unsupported here. Must be part of service");
755             return -1;
756
757         }
758         else
759         {
760             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
761             return -1;
762         }
763     }
764     return 0;
765 }
766
767 static int process_config_includes(struct conf_config *config, xmlNode *n);
768
769 static int config_include_one(struct conf_config *config, xmlNode **sib,
770     const char *path)
771 {
772     struct stat st;
773     if (stat(path, &st) < 0)
774     {
775         yaz_log(YLOG_FATAL|YLOG_ERRNO, "stat %s", path);
776         return -1;
777     }
778     else
779     {
780         if ((st.st_mode & S_IFMT) == S_IFREG)
781         {
782             xmlDoc *doc = xmlParseFile(path);
783             if (doc)
784             {
785                 xmlNodePtr t = xmlDocGetRootElement(doc);
786                 int ret = process_config_includes(config, t);
787                 *sib = xmlAddNextSibling(*sib, xmlCopyNode(t, 1));
788                 xmlFreeDoc(doc);
789                 if (ret)
790                     return -1;
791             }
792             else
793             {
794                 yaz_log(YLOG_FATAL, "Could not parse %s", path);
795                 return -1;
796             }
797         }
798     }
799     return 0;
800 }
801
802 static int config_include_src(struct conf_config *config, xmlNode **np,
803                               const char *src)
804 {
805     int ret = 0; /* return code. OK so far */
806     WRBUF w = wrbuf_alloc();
807     xmlNodePtr sib; /* our sibling that we append */
808     xmlNodePtr c; /* tmp node */
809
810     wrbuf_printf(w, " begin include src=\"%s\" ", src);
811
812     /* replace include element with a 'begin' comment */
813     sib = xmlNewComment((const xmlChar *) wrbuf_cstr(w));
814     xmlReplaceNode(*np, sib);
815
816     xmlFreeNode(*np);
817
818     wrbuf_rewind(w);
819     conf_dir_path(config, w, src);
820 #if USE_POSIX_GLOB
821     {
822         size_t i;
823         glob_t glob_res;
824         glob(wrbuf_cstr(w), 0 /* flags */, 0 /* errfunc */, &glob_res);
825         
826         for (i = 0; ret == 0 && i < glob_res.gl_pathc; i++)
827         {
828             const char *path = glob_res.gl_pathv[i];
829             ret = config_include_one(config, &sib, path);
830         }
831         globfree(&glob_res);
832     }
833 #else
834     ret = config_include_one(config, &sib, wrbuf_cstr(w));
835 #endif
836     wrbuf_rewind(w);
837     wrbuf_printf(w, " end include src=\"%s\" ", src);
838     c = xmlNewComment((const xmlChar *) wrbuf_cstr(w));
839     sib = xmlAddNextSibling(sib, c);
840     
841     *np = sib;
842     wrbuf_destroy(w);
843     return ret;
844 }
845
846 static int process_config_includes(struct conf_config *config, xmlNode *n)
847 {
848     for (; n; n = n->next)
849     {
850         if (n->type == XML_ELEMENT_NODE)
851         {
852             if (!strcmp((const char *) n->name, "include"))
853             {
854                 xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
855                 if (src)
856                 {
857                     int ret = config_include_src(config, &n,
858                                                  (const char *) src);
859                     xmlFree(src);
860                     if (ret)
861                         return ret;
862                         
863                 }
864             }
865             else
866             {
867                 if (process_config_includes(config, n->children))
868                     return -1;
869             }
870         }
871     }
872     return 0;
873 }
874
875 struct conf_config *config_create(const char *fname, int verbose)
876 {
877     xmlDoc *doc = xmlParseFile(fname);
878     xmlNode *n;
879     const char *p;
880     int r;
881     NMEM nmem = nmem_create();
882     struct conf_config *config = nmem_malloc(nmem, sizeof(struct conf_config));
883
884     xmlSubstituteEntitiesDefault(1);
885     xmlLoadExtDtdDefaultValue = 1;
886     if (!doc)
887     {
888         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
889         return 0;
890     }
891
892     config->nmem = nmem;
893     config->servers = 0;
894
895     config->confdir = wrbuf_alloc();
896     if ((p = strrchr(fname, 
897 #ifdef WIN32
898                      '\\'
899 #else
900                      '/'
901 #endif
902              )))
903     {
904         int len = p - fname;
905         wrbuf_write(config->confdir, fname, len);
906     }
907     wrbuf_puts(config->confdir, "");
908     
909     n = xmlDocGetRootElement(doc);
910     r = process_config_includes(config, n);
911     if (r == 0) /* OK */
912     {
913         if (verbose)
914         {
915             yaz_log(YLOG_LOG, "Configuration %s after include processing",
916                     fname);
917             xmlDocFormatDump(yaz_log_file(), doc, 0);
918         }
919         r = parse_config(config, n);
920     }
921     xmlFreeDoc(doc);
922
923     if (r)
924     {
925         config_destroy(config);
926         return 0;
927     }
928     return config;
929 }
930
931 void server_destroy(struct conf_server *server)
932 {
933     struct conf_service *s = server->service;
934     while (s)
935     {
936         struct conf_service *s_next = s->next;
937         service_destroy(s);
938         s = s_next;
939     }
940     pp2_charset_destroy(server->relevance_pct);
941     pp2_charset_destroy(server->sort_pct);
942     pp2_charset_destroy(server->mergekey_pct);
943 }
944
945 void config_destroy(struct conf_config *config)
946 {
947     if (config)
948     {
949         struct conf_server *server = config->servers;
950         while (server)
951         {
952             struct conf_server *s_next = server->next;
953             server_destroy(server);
954             server = s_next;
955         }
956         wrbuf_destroy(config->confdir);
957         nmem_destroy(config->nmem);
958     }
959 }
960
961 void config_stop_listeners(struct conf_config *conf)
962 {
963     struct conf_server *ser;
964     for (ser = conf->servers; ser; ser = ser->next)
965         http_close_server(ser);
966 }
967
968 void config_start_databases(struct conf_config *conf)
969 {
970     struct conf_server *ser;
971     for (ser = conf->servers; ser; ser = ser->next)
972     {
973         struct conf_service *s = ser->service;
974         for (;s ; s = s->next)
975             resolve_databases(s);
976     }
977 }
978
979 int config_start_listeners(struct conf_config *conf,
980                            const char *listener_override)
981 {
982     struct conf_server *ser;
983     for (ser = conf->servers; ser; ser = ser->next)
984     {
985         WRBUF w = wrbuf_alloc();
986         int r;
987         if (listener_override)
988         {
989             wrbuf_puts(w, listener_override);
990             listener_override = 0; /* only first server is overriden */
991         }
992         else
993         {
994             if (ser->host)
995                 wrbuf_puts(w, ser->host);
996             if (ser->port)
997             {
998                 if (wrbuf_len(w))
999                     wrbuf_puts(w, ":");
1000                 wrbuf_printf(w, "%d", ser->port);
1001             }
1002         }
1003         r = http_init(wrbuf_cstr(w), ser);
1004         wrbuf_destroy(w);
1005         if (r)
1006             return -1;
1007
1008         w = wrbuf_alloc();
1009         if (ser->proxy_host || ser->proxy_port)
1010         {
1011             if (ser->proxy_host)
1012                 wrbuf_puts(w, ser->proxy_host);
1013             if (ser->proxy_port)
1014             {
1015                 if (wrbuf_len(w))
1016                     wrbuf_puts(w, ":");
1017                 wrbuf_printf(w, "%d", ser->proxy_port);
1018             }
1019         }
1020         if (wrbuf_len(w))
1021             http_set_proxyaddr(wrbuf_cstr(w), ser);
1022         wrbuf_destroy(w);
1023     }
1024     return 0;
1025 }
1026
1027 /*
1028  * Local variables:
1029  * c-basic-offset: 4
1030  * c-file-style: "Stroustrup"
1031  * indent-tabs-mode: nil
1032  * End:
1033  * vim: shiftwidth=4 tabstop=8 expandtab
1034  */
1035