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