Remove unused code
[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     return service;
544 }
545
546 static char *parse_settings(struct conf_config *config,
547                             NMEM nmem, xmlNode *node)
548 {
549     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
550     char *r;
551
552     if (src)
553     {
554         WRBUF w = wrbuf_alloc();
555         conf_dir_path(config, w, (const char *) src);
556         r = nmem_strdup(nmem, wrbuf_cstr(w));
557         wrbuf_destroy(w);
558     }
559     else
560     {
561         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
562         return 0;
563     }
564     xmlFree(src);
565     return r;
566 }
567
568 static void inherit_server_settings(struct conf_server *server)
569 {
570     struct conf_service *s;
571     for (s = server->service; s; s = s->next)
572     {
573         if (!s->dictionary) /* service has no config settings ? */
574         {
575             if (server->server_settings)
576             {
577                 /* inherit settings from server */
578                 init_settings(s);
579                 settings_read_file(s, server->server_settings, 1);
580                 settings_read_file(s, server->server_settings, 2);
581             }
582             else
583             {
584                 yaz_log(YLOG_WARN, "service '%s' has no settings",
585                         s->id ? s->id : "unnamed");
586                 init_settings(s);
587             }
588         }
589
590         /* use relevance/sort/mergekey from server if not defined
591            for this service.. */
592         if (!s->relevance_pct)
593         {
594             if (server->relevance_pct)
595             {
596                 s->relevance_pct = server->relevance_pct;
597                 pp2_charset_incref(s->relevance_pct);
598             }
599             else
600                 s->relevance_pct = pp2_charset_create(0);
601         }
602         
603         if (!s->sort_pct)
604         {
605             if (server->sort_pct)
606             {
607                 s->sort_pct = server->sort_pct;
608                 pp2_charset_incref(s->sort_pct);
609             }
610             else
611                 s->sort_pct = pp2_charset_create(0);
612         }
613         
614         if (!s->mergekey_pct)
615         {
616             if (server->mergekey_pct)
617             {
618                 s->mergekey_pct = server->mergekey_pct;
619                 pp2_charset_incref(s->mergekey_pct);
620             }
621             else
622                 s->mergekey_pct = pp2_charset_create(0);
623         }
624     }
625 }
626
627 static struct conf_server *server_create(struct conf_config *config,
628                                          NMEM nmem, xmlNode *node)
629 {
630     xmlNode *n;
631     struct conf_server *server = nmem_malloc(nmem, sizeof(struct conf_server));
632
633     server->host = 0;
634     server->port = 0;
635     server->proxy_host = 0;
636     server->proxy_port = 0;
637     server->myurl = 0;
638     server->proxy_addr = 0;
639     server->service = 0;
640     server->next = 0;
641     server->relevance_pct = 0;
642     server->sort_pct = 0;
643     server->mergekey_pct = 0;
644     server->server_settings = 0;
645
646     for (n = node->children; n; n = n->next)
647     {
648         if (n->type != XML_ELEMENT_NODE)
649             continue;
650         if (!strcmp((const char *) n->name, "listen"))
651         {
652             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
653             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
654             if (port)
655                 server->port = atoi((const char *) port);
656             if (host)
657                 server->host = nmem_strdup(nmem, (const char *) host);
658             xmlFree(port);
659             xmlFree(host);
660         }
661         else if (!strcmp((const char *) n->name, "proxy"))
662         {
663             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
664             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
665             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
666             if (port)
667                 server->proxy_port = atoi((const char *) port);
668             if (host)
669                 server->proxy_host = nmem_strdup(nmem, (const char *) host);
670             if (myurl)
671                 server->myurl = nmem_strdup(nmem, (const char *) myurl);
672             xmlFree(port);
673             xmlFree(host);
674             xmlFree(myurl);
675         }
676         else if (!strcmp((const char *) n->name, "settings"))
677         {
678             if (server->server_settings)
679             {
680                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
681                 return 0;
682             }
683             if (!(server->server_settings = parse_settings(config, nmem, n)))
684                 return 0;
685         }
686         else if (!strcmp((const char *) n->name, "relevance"))
687         {
688             server->relevance_pct = pp2_charset_create_xml(n);
689             if (!server->relevance_pct)
690                 return 0;
691         }
692         else if (!strcmp((const char *) n->name, "sort"))
693         {
694             server->sort_pct = pp2_charset_create_xml(n);
695             if (!server->sort_pct)
696                 return 0;
697         }
698         else if (!strcmp((const char *) n->name, "mergekey"))
699         {
700             server->mergekey_pct = pp2_charset_create_xml(n);
701             if (!server->mergekey_pct)
702                 return 0;
703         }
704         else if (!strcmp((const char *) n->name, "service"))
705         {
706             char *service_id = (char *)
707                 xmlGetProp(n, (xmlChar *) "id");
708
709             struct conf_service **sp = &server->service;
710             for (; *sp; sp = &(*sp)->next)
711                 if ((*sp)->id && service_id &&
712                     0 == strcmp((*sp)->id, service_id))
713                 {
714                     yaz_log(YLOG_FATAL, "Duplicate service: %s", service_id);
715                     break;
716                 }
717                 else if (!(*sp)->id && !service_id)
718                 {
719                     yaz_log(YLOG_FATAL, "Duplicate unnamed service '%s'",
720                         service_id);
721                     break;
722                 }
723
724             if (*sp)  /* service already exist */
725             {
726                 xmlFree(service_id);
727                 return 0;
728             }
729             else
730             {
731                 struct conf_service *s = service_create(config, n,
732                                                         service_id);
733                 xmlFree(service_id);
734                 if (!s)
735                     return 0;
736                 *sp = s;
737             }
738         }
739         else
740         {
741             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
742             return 0;
743         }
744     }
745     inherit_server_settings(server);
746     return server;
747 }
748
749 xsltStylesheet *conf_load_stylesheet(struct conf_config *config,
750                                      const char *fname)
751 {
752     WRBUF w = wrbuf_alloc();
753     xsltStylesheet *s;
754
755     conf_dir_path(config, w, fname);
756     s = xsltParseStylesheetFile((xmlChar *) wrbuf_cstr(w));
757     wrbuf_destroy(w);
758     return s;
759 }
760
761 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
762                                                         xmlNode *node)
763 {
764     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
765     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
766     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
767
768     memset(r, 0, sizeof(*r));
769
770     if (type)
771     {
772         if (!strcmp((const char *) type, "local"))
773             r->type = Targetprofiles_local;
774         else
775         {
776             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
777             return 0;
778         }
779     }
780     else
781     {
782         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
783         return 0;
784     }
785
786     if (src)
787         r->src = nmem_strdup(nmem, (const char *) src);
788     else
789     {
790         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
791         return 0;
792     }
793     xmlFree(type);
794     xmlFree(src);
795     return r;
796 }
797
798 struct conf_service *locate_service(struct conf_server *server,
799                                     const char *service_id)
800 {
801     struct conf_service *s = server->service;
802     for (; s; s = s->next)
803         if (s->id && service_id && 0 == strcmp(s->id, service_id))
804             return s;
805         else if (!s->id && !service_id)
806             return s;
807     return 0;
808 }
809
810
811 static int parse_config(struct conf_config *config, xmlNode *root)
812 {
813     xmlNode *n;
814
815     for (n = root->children; n; n = n->next)
816     {
817         if (n->type != XML_ELEMENT_NODE)
818             continue;
819         if (!strcmp((const char *) n->name, "server"))
820         {
821             struct conf_server *tmp = server_create(config, config->nmem, n);
822             if (!tmp)
823                 return -1;
824             tmp->next = config->servers;
825             config->servers = tmp;
826         }
827         else if (!strcmp((const char *) n->name, "targetprofiles"))
828         {
829             yaz_log(YLOG_FATAL, "targetprofiles unsupported here. Must be part of service");
830             return -1;
831
832         }
833         else
834         {
835             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
836             return -1;
837         }
838     }
839     return 0;
840 }
841
842 static int process_config_includes(struct conf_config *config, xmlNode *n);
843
844 static int config_include_one(struct conf_config *config, xmlNode **sib,
845     const char *path)
846 {
847     struct stat st;
848     if (stat(path, &st) < 0)
849     {
850         yaz_log(YLOG_FATAL|YLOG_ERRNO, "stat %s", path);
851         return -1;
852     }
853     else
854     {
855         if ((st.st_mode & S_IFMT) == S_IFREG)
856         {
857             xmlDoc *doc = xmlParseFile(path);
858             if (doc)
859             {
860                 xmlNodePtr t = xmlDocGetRootElement(doc);
861                 int ret = process_config_includes(config, t);
862                 *sib = xmlAddNextSibling(*sib, xmlCopyNode(t, 1));
863                 xmlFreeDoc(doc);
864                 if (ret)
865                     return -1;
866             }
867             else
868             {
869                 yaz_log(YLOG_FATAL, "Could not parse %s", path);
870                 return -1;
871             }
872         }
873     }
874     return 0;
875 }
876
877 static int config_include_src(struct conf_config *config, xmlNode **np,
878                               const char *src)
879 {
880     int ret = 0; /* return code. OK so far */
881     WRBUF w = wrbuf_alloc();
882     xmlNodePtr sib; /* our sibling that we append */
883     xmlNodePtr c; /* tmp node */
884
885     wrbuf_printf(w, " begin include src=\"%s\" ", src);
886
887     /* replace include element with a 'begin' comment */
888     sib = xmlNewComment((const xmlChar *) wrbuf_cstr(w));
889     xmlReplaceNode(*np, sib);
890
891     xmlFreeNode(*np);
892
893     wrbuf_rewind(w);
894     conf_dir_path(config, w, src);
895 #if USE_POSIX_GLOB
896     {
897         size_t i;
898         glob_t glob_res;
899         glob(wrbuf_cstr(w), 0 /* flags */, 0 /* errfunc */, &glob_res);
900         
901         for (i = 0; ret == 0 && i < glob_res.gl_pathc; i++)
902         {
903             const char *path = glob_res.gl_pathv[i];
904             ret = config_include_one(config, &sib, path);
905         }
906         globfree(&glob_res);
907     }
908 #else
909     ret = config_include_one(config, &sib, wrbuf_cstr(w));
910 #endif
911     wrbuf_rewind(w);
912     wrbuf_printf(w, " end include src=\"%s\" ", src);
913     c = xmlNewComment((const xmlChar *) wrbuf_cstr(w));
914     sib = xmlAddNextSibling(sib, c);
915     
916     *np = sib;
917     wrbuf_destroy(w);
918     return ret;
919 }
920
921 static int process_config_includes(struct conf_config *config, xmlNode *n)
922 {
923     for (; n; n = n->next)
924     {
925         if (n->type == XML_ELEMENT_NODE)
926         {
927             if (!strcmp((const char *) n->name, "include"))
928             {
929                 xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
930                 if (src)
931                 {
932                     int ret = config_include_src(config, &n,
933                                                  (const char *) src);
934                     xmlFree(src);
935                     if (ret)
936                         return ret;
937                         
938                 }
939             }
940             else
941             {
942                 if (process_config_includes(config, n->children))
943                     return -1;
944             }
945         }
946     }
947     return 0;
948 }
949
950 struct conf_config *config_create(const char *fname, int verbose)
951 {
952     xmlDoc *doc = xmlParseFile(fname);
953     xmlNode *n;
954     const char *p;
955     int r;
956     NMEM nmem = nmem_create();
957     struct conf_config *config = nmem_malloc(nmem, sizeof(struct conf_config));
958
959     xmlSubstituteEntitiesDefault(1);
960     xmlLoadExtDtdDefaultValue = 1;
961     if (!doc)
962     {
963         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
964         nmem_destroy(nmem);
965         return 0;
966     }
967
968     config->nmem = nmem;
969     config->servers = 0;
970
971     config->confdir = wrbuf_alloc();
972     if ((p = strrchr(fname, 
973 #ifdef WIN32
974                      '\\'
975 #else
976                      '/'
977 #endif
978              )))
979     {
980         int len = p - fname;
981         wrbuf_write(config->confdir, fname, len);
982     }
983     wrbuf_puts(config->confdir, "");
984     
985     n = xmlDocGetRootElement(doc);
986     r = process_config_includes(config, n);
987     if (r == 0) /* OK */
988     {
989         if (verbose)
990         {
991             yaz_log(YLOG_LOG, "Configuration %s after include processing",
992                     fname);
993             xmlDocFormatDump(yaz_log_file(), doc, 0);
994         }
995         r = parse_config(config, n);
996     }
997     xmlFreeDoc(doc);
998
999     if (r)
1000     {
1001         config_destroy(config);
1002         return 0;
1003     }
1004     return config;
1005 }
1006
1007 void server_destroy(struct conf_server *server)
1008 {
1009     struct conf_service *s = server->service;
1010     while (s)
1011     {
1012         struct conf_service *s_next = s->next;
1013         service_destroy(s);
1014         s = s_next;
1015     }
1016     pp2_charset_destroy(server->relevance_pct);
1017     pp2_charset_destroy(server->sort_pct);
1018     pp2_charset_destroy(server->mergekey_pct);
1019 }
1020
1021 void config_destroy(struct conf_config *config)
1022 {
1023     if (config)
1024     {
1025         struct conf_server *server = config->servers;
1026         while (server)
1027         {
1028             struct conf_server *s_next = server->next;
1029             server_destroy(server);
1030             server = s_next;
1031         }
1032         wrbuf_destroy(config->confdir);
1033         nmem_destroy(config->nmem);
1034     }
1035 }
1036
1037 void config_stop_listeners(struct conf_config *conf)
1038 {
1039     struct conf_server *ser;
1040     for (ser = conf->servers; ser; ser = ser->next)
1041         http_close_server(ser);
1042 }
1043
1044 void config_start_databases(struct conf_config *conf)
1045 {
1046     struct conf_server *ser;
1047     for (ser = conf->servers; ser; ser = ser->next)
1048     {
1049         struct conf_service *s = ser->service;
1050         for (;s ; s = s->next)
1051             resolve_databases(s);
1052     }
1053 }
1054
1055 int config_start_listeners(struct conf_config *conf,
1056                            const char *listener_override)
1057 {
1058     struct conf_server *ser;
1059     for (ser = conf->servers; ser; ser = ser->next)
1060     {
1061         WRBUF w = wrbuf_alloc();
1062         int r;
1063         if (listener_override)
1064         {
1065             wrbuf_puts(w, listener_override);
1066             listener_override = 0; /* only first server is overriden */
1067         }
1068         else
1069         {
1070             if (ser->host)
1071                 wrbuf_puts(w, ser->host);
1072             if (ser->port)
1073             {
1074                 if (wrbuf_len(w))
1075                     wrbuf_puts(w, ":");
1076                 wrbuf_printf(w, "%d", ser->port);
1077             }
1078         }
1079         r = http_init(wrbuf_cstr(w), ser);
1080         wrbuf_destroy(w);
1081         if (r)
1082             return -1;
1083
1084         w = wrbuf_alloc();
1085         if (ser->proxy_host || ser->proxy_port)
1086         {
1087             if (ser->proxy_host)
1088                 wrbuf_puts(w, ser->proxy_host);
1089             if (ser->proxy_port)
1090             {
1091                 if (wrbuf_len(w))
1092                     wrbuf_puts(w, ":");
1093                 wrbuf_printf(w, "%d", ser->proxy_port);
1094             }
1095         }
1096         if (wrbuf_len(w))
1097             http_set_proxyaddr(wrbuf_cstr(w), ser);
1098         wrbuf_destroy(w);
1099     }
1100     return 0;
1101 }
1102
1103 /*
1104  * Local variables:
1105  * c-basic-offset: 4
1106  * c-file-style: "Stroustrup"
1107  * indent-tabs-mode: nil
1108  * End:
1109  * vim: shiftwidth=4 tabstop=8 expandtab
1110  */
1111