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