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