Proxy per-server. Clean various fixed size bufs.
[pazpar2-moved-to-github.git] / src / pazpar2_config.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2009 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <string.h>
25
26 #include <libxml/parser.h>
27 #include <libxml/tree.h>
28 #include <libxslt/xslt.h>
29 #include <libxslt/transform.h>
30 #include <libxslt/xsltutils.h>
31
32 #include <yaz/yaz-util.h>
33 #include <yaz/nmem.h>
34 #include <yaz/snprintf.h>
35 #include <yaz/tpath.h>
36
37 #include "pazpar2_config.h"
38 #include "settings.h"
39
40 static WRBUF confdir = 0;
41
42 static char *parse_settings(NMEM nmem, xmlNode *node);
43
44 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
45                                                         xmlNode *node);
46
47 static 
48 struct conf_metadata * conf_metadata_assign(NMEM nmem, 
49                                             struct conf_metadata * metadata,
50                                             const char *name,
51                                             enum conf_metadata_type type,
52                                             enum conf_metadata_merge merge,
53                                             enum conf_setting_type setting,
54                                             int brief,
55                                             int termlist,
56                                             int rank,
57                                             int sortkey_offset,
58                                             enum conf_metadata_mergekey mt)
59 {
60     if (!nmem || !metadata || !name)
61         return 0;
62     
63     metadata->name = nmem_strdup(nmem, name);
64
65     metadata->type = type;
66
67     // enforcing that type_year is always range_merge
68     if (metadata->type == Metadata_type_year)
69         metadata->merge = Metadata_merge_range;
70     else
71         metadata->merge = merge;    
72
73     metadata->setting = setting;
74     metadata->brief = brief;   
75     metadata->termlist = termlist;
76     metadata->rank = rank;    
77     metadata->sortkey_offset = sortkey_offset;
78     metadata->mergekey = mt;
79     return metadata;
80 }
81
82
83 static
84 struct conf_sortkey * conf_sortkey_assign(NMEM nmem, 
85                                           struct conf_sortkey * sortkey,
86                                           const char *name,
87                                           enum conf_sortkey_type type)
88 {
89     if (!nmem || !sortkey || !name)
90         return 0;
91     
92     sortkey->name = nmem_strdup(nmem, name);
93     sortkey->type = type;
94
95     return sortkey;
96 }
97
98
99 struct conf_service * conf_service_create(int num_metadata, int num_sortkeys,
100     const char *service_id)
101 {
102     struct conf_service * service = 0;
103     NMEM nmem = nmem_create();
104
105     //assert(nmem);
106     
107     service = nmem_malloc(nmem, sizeof(struct conf_service));
108     service->nmem = nmem;
109     service->next = 0;
110     service->settings = 0;
111     service->databases = 0;
112     service->targetprofiles = 0;
113
114
115     service->id = service_id ? nmem_strdup(nmem, service_id) : 0;
116     service->num_metadata = num_metadata;
117     service->metadata = 0;
118     if (service->num_metadata)
119       service->metadata 
120           = nmem_malloc(nmem, 
121                         sizeof(struct conf_metadata) * service->num_metadata);
122     service->num_sortkeys = num_sortkeys;
123     service->sortkeys = 0;
124     if (service->num_sortkeys)
125         service->sortkeys 
126             = nmem_malloc(nmem, 
127                           sizeof(struct conf_sortkey) * service->num_sortkeys);
128     service->dictionary = 0;
129     return service; 
130 }
131
132 struct conf_metadata* conf_service_add_metadata(struct conf_service *service,
133                                                 int field_id,
134                                                 const char *name,
135                                                 enum conf_metadata_type type,
136                                                 enum conf_metadata_merge merge,
137                                                 enum conf_setting_type setting,
138                                                 int brief,
139                                                 int termlist,
140                                                 int rank,
141                                                 int sortkey_offset,
142                                                 enum conf_metadata_mergekey mt)
143 {
144     struct conf_metadata * md = 0;
145
146     if (!service || !service->metadata || !service->num_metadata
147         || field_id < 0  || !(field_id < service->num_metadata))
148         return 0;
149
150     //md = &((service->metadata)[field_id]);
151     md = service->metadata + field_id;
152     md = conf_metadata_assign(service->nmem, md, name, type, merge, setting,
153                               brief, termlist, rank, sortkey_offset,
154                               mt);
155     return md;
156 }
157
158
159 struct conf_sortkey * conf_service_add_sortkey(struct conf_service *service,
160                                                int field_id,
161                                                const char *name,
162                                                enum conf_sortkey_type type)
163 {
164     struct conf_sortkey * sk = 0;
165
166     if (!service || !service->sortkeys || !service->num_sortkeys
167         || field_id < 0  || !(field_id < service->num_sortkeys))
168         return 0;
169
170     //sk = &((service->sortkeys)[field_id]);
171     sk = service->sortkeys + field_id;
172     sk = conf_sortkey_assign(service->nmem, sk, name, type);
173
174     return sk;
175 }
176
177
178 int conf_service_metadata_field_id(struct conf_service *service,
179                                    const char * name)
180 {
181     int i = 0;
182
183     if (!service || !service->metadata || !service->num_metadata)
184         return -1;
185
186     for(i = 0; i < service->num_metadata; i++) {
187         if (!strcmp(name, (service->metadata[i]).name))
188             return i;
189     }
190    
191     return -1;
192 }
193
194
195 int conf_service_sortkey_field_id(struct conf_service *service,
196                                   const char * name)
197 {
198     int i = 0;
199
200     if (!service || !service->sortkeys || !service->num_sortkeys)
201         return -1;
202
203     for(i = 0; i < service->num_sortkeys; i++) {
204         if (!strcmp(name, (service->sortkeys[i]).name))
205             return i;
206     }
207    
208     return -1;
209 }
210
211
212
213 /* Code to parse configuration file */
214 /* ==================================================== */
215
216 static struct conf_service *parse_service(xmlNode *node, const char *service_id)
217 {
218     xmlNode *n;
219     int md_node = 0;
220     int sk_node = 0;
221
222     struct conf_service *service = 0;
223     int num_metadata = 0;
224     int num_sortkeys = 0;
225     
226     // count num_metadata and num_sortkeys
227     for (n = node->children; n; n = n->next)
228         if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)
229                                                    n->name, "metadata"))
230         {
231             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
232             num_metadata++;
233             if (sortkey && strcmp((const char *) sortkey, "no"))
234                 num_sortkeys++;
235             xmlFree(sortkey);
236         }
237
238     service = conf_service_create(num_metadata, num_sortkeys, service_id);
239
240     for (n = node->children; n; n = n->next)
241     {
242         if (n->type != XML_ELEMENT_NODE)
243             continue;
244         if (!strcmp((const char *) n->name, "settings"))
245         {
246             if (service->settings)
247             {
248                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
249                 return 0;
250             }
251             service->settings = parse_settings(service->nmem, n);
252             if (!service->settings)
253                 return 0;
254         }
255         else if (!strcmp((const char *) n->name, (const char *) "targetprofiles"))
256         {
257             if (service->targetprofiles)
258             {
259                 yaz_log(YLOG_FATAL, "Can't repeat targetprofiles");
260                 return 0;
261             }
262             if (!(service->targetprofiles = 
263                   parse_targetprofiles(service->nmem, n)))
264                 return 0;
265         }
266         else if (!strcmp((const char *) n->name, (const char *) "metadata"))
267         {
268             xmlChar *xml_name = xmlGetProp(n, (xmlChar *) "name");
269             xmlChar *xml_brief = xmlGetProp(n, (xmlChar *) "brief");
270             xmlChar *xml_sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
271             xmlChar *xml_merge = xmlGetProp(n, (xmlChar *) "merge");
272             xmlChar *xml_type = xmlGetProp(n, (xmlChar *) "type");
273             xmlChar *xml_termlist = xmlGetProp(n, (xmlChar *) "termlist");
274             xmlChar *xml_rank = xmlGetProp(n, (xmlChar *) "rank");
275             xmlChar *xml_setting = xmlGetProp(n, (xmlChar *) "setting");
276             xmlChar *xml_mergekey = xmlGetProp(n, (xmlChar *) "mergekey");
277
278             enum conf_metadata_type type = Metadata_type_generic;
279             enum conf_metadata_merge merge = Metadata_merge_no;
280             enum conf_setting_type setting = Metadata_setting_no;
281             enum conf_sortkey_type sk_type = Metadata_sortkey_relevance;
282             enum conf_metadata_mergekey mergekey_type = Metadata_mergekey_no;
283             int brief = 0;
284             int termlist = 0;
285             int rank = 0;
286             int sortkey_offset = 0;
287             
288             // now do the parsing logic
289             if (!xml_name)
290             {
291                 yaz_log(YLOG_FATAL, "Must specify name in metadata element");
292                 return 0;
293             }
294             if (xml_brief)
295             {
296                 if (!strcmp((const char *) xml_brief, "yes"))
297                     brief = 1;
298                  else if (strcmp((const char *) xml_brief, "no"))
299                 {
300                     yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
301                     return 0;
302                 }
303             }
304             else
305                 brief = 0;
306
307             if (xml_termlist)
308             {
309                 if (!strcmp((const char *) xml_termlist, "yes"))
310                     termlist = 1;
311                 else if (strcmp((const char *) xml_termlist, "no"))
312                 {
313                     yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
314                     return 0;
315                 }
316             }
317             else
318                 termlist = 0;
319
320             if (xml_rank)
321                 rank = atoi((const char *) xml_rank);
322             else
323                 rank = 0;
324
325             if (xml_type)
326             {
327                 if (!strcmp((const char *) xml_type, "generic"))
328                     type = Metadata_type_generic;
329                 else if (!strcmp((const char *) xml_type, "year"))
330                     type = Metadata_type_year;
331                 else if (!strcmp((const char *) xml_type, "date"))
332                     type = Metadata_type_date;
333                 else
334                 {
335                     yaz_log(YLOG_FATAL, 
336                             "Unknown value for metadata/type: %s", xml_type);
337                     return 0;
338                 }
339             }
340             else
341                 type = Metadata_type_generic;
342
343             if (xml_merge)
344             {
345                 if (!strcmp((const char *) xml_merge, "no"))
346                     merge = Metadata_merge_no;
347                 else if (!strcmp((const char *) xml_merge, "unique"))
348                     merge = Metadata_merge_unique;
349                 else if (!strcmp((const char *) xml_merge, "longest"))
350                     merge = Metadata_merge_longest;
351                 else if (!strcmp((const char *) xml_merge, "range"))
352                     merge = Metadata_merge_range;
353                 else if (!strcmp((const char *) xml_merge, "all"))
354                     merge = Metadata_merge_all;
355                 else
356                 {
357                     yaz_log(YLOG_FATAL, 
358                             "Unknown value for metadata/merge: %s", xml_merge);
359                     return 0;
360                 }
361             }
362             else
363                 merge = Metadata_merge_no;
364
365             if (xml_setting)
366             {
367                 if (!strcmp((const char *) xml_setting, "no"))
368                     setting = Metadata_setting_no;
369                 else if (!strcmp((const char *) xml_setting, "postproc"))
370                     setting = Metadata_setting_postproc;
371                 else if (!strcmp((const char *) xml_setting, "parameter"))
372                     setting = Metadata_setting_parameter;
373                 else
374                 {
375                     yaz_log(YLOG_FATAL,
376                         "Unknown value for medadata/setting: %s", xml_setting);
377                     return 0;
378                 }
379             }
380
381             // add a sortkey if so specified
382             if (xml_sortkey && strcmp((const char *) xml_sortkey, "no"))
383             {
384                 if (merge == Metadata_merge_no)
385                 {
386                     yaz_log(YLOG_FATAL, 
387                             "Can't specify sortkey on a non-merged field");
388                     return 0;
389                 }
390                 if (!strcmp((const char *) xml_sortkey, "numeric"))
391                     sk_type = Metadata_sortkey_numeric;
392                 else if (!strcmp((const char *) xml_sortkey, "skiparticle"))
393                     sk_type = Metadata_sortkey_skiparticle;
394                 else
395                 {
396                     yaz_log(YLOG_FATAL,
397                             "Unknown sortkey in metadata element: %s", 
398                             xml_sortkey);
399                     return 0;
400                 }
401                 sortkey_offset = sk_node;
402
403                 conf_service_add_sortkey(
404 service, sk_node,
405                                          (const char *) xml_name, sk_type);
406                 
407                 sk_node++;
408             }
409             else
410                 sortkey_offset = -1;
411
412             if (xml_mergekey && strcmp((const char *) xml_mergekey, "no"))
413             {
414                 mergekey_type = Metadata_mergekey_yes;
415             }
416
417
418             // metadata known, assign values
419             conf_service_add_metadata(service, md_node,
420                                       (const char *) xml_name,
421                                       type, merge, setting,
422                                       brief, termlist, rank, sortkey_offset,
423                                       mergekey_type);
424
425             xmlFree(xml_name);
426             xmlFree(xml_brief);
427             xmlFree(xml_sortkey);
428             xmlFree(xml_merge);
429             xmlFree(xml_type);
430             xmlFree(xml_termlist);
431             xmlFree(xml_rank);
432             xmlFree(xml_setting);
433             md_node++;
434         }
435         else
436         {
437             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
438             return 0;
439         }
440     }
441     return service;
442 }
443
444 static char *parse_settings(NMEM nmem, xmlNode *node)
445 {
446     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
447     char *r;
448
449     if (src)
450     {
451         if (yaz_is_abspath((const char *) src))
452             r = nmem_strdup(nmem, (const char *) src);
453         else
454         {
455             r = nmem_malloc(nmem,
456                             wrbuf_len(confdir) + strlen((const char *) src) + 2);
457             sprintf(r, "%s/%s", wrbuf_cstr(confdir), src);
458         }
459     }
460     else
461     {
462         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
463         return 0;
464     }
465     xmlFree(src);
466     return r;
467 }
468
469 static struct conf_server *parse_server(NMEM nmem, xmlNode *node)
470 {
471     xmlNode *n;
472     struct conf_server *server = nmem_malloc(nmem, sizeof(struct conf_server));
473
474     server->host = 0;
475     server->port = 0;
476     server->proxy_host = 0;
477     server->proxy_port = 0;
478     server->myurl = 0;
479     server->proxy_addr = 0;
480     server->service = 0;
481     server->next = 0;
482     server->server_settings = 0;
483     server->relevance_pct = 0;
484     server->sort_pct = 0;
485     server->mergekey_pct = 0;
486
487     for (n = node->children; n; n = n->next)
488     {
489         if (n->type != XML_ELEMENT_NODE)
490             continue;
491         if (!strcmp((const char *) n->name, "listen"))
492         {
493             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
494             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
495             if (port)
496                 server->port = atoi((const char *) port);
497             if (host)
498                 server->host = nmem_strdup(nmem, (const char *) host);
499             xmlFree(port);
500             xmlFree(host);
501         }
502         else if (!strcmp((const char *) n->name, "proxy"))
503         {
504             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
505             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
506             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
507             if (port)
508                 server->proxy_port = atoi((const char *) port);
509             if (host)
510                 server->proxy_host = nmem_strdup(nmem, (const char *) host);
511             if (myurl)
512                 server->myurl = nmem_strdup(nmem, (const char *) myurl);
513             xmlFree(port);
514             xmlFree(host);
515             xmlFree(myurl);
516         }
517         else if (!strcmp((const char *) n->name, "settings"))
518         {
519             if (server->server_settings)
520             {
521                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
522                 return 0;
523             }
524             if (!(server->server_settings = parse_settings(nmem, n)))
525                 return 0;
526         }
527         else if (!strcmp((const char *) n->name, "relevance"))
528         {
529             server->relevance_pct = pp2_charset_create_xml(n);
530             if (!server->relevance_pct)
531                 return 0;
532         }
533         else if (!strcmp((const char *) n->name, "sort"))
534         {
535             server->sort_pct = pp2_charset_create_xml(n);
536             if (!server->sort_pct)
537                 return 0;
538         }
539         else if (!strcmp((const char *) n->name, "mergekey"))
540         {
541             server->mergekey_pct = pp2_charset_create_xml(n);
542             if (!server->mergekey_pct)
543                 return 0;
544         }
545         else if (!strcmp((const char *) n->name, "service"))
546         {
547             char *service_id = (char *)
548                 xmlGetProp(n, (xmlChar *) "id");
549
550             struct conf_service **sp = &server->service;
551             for (; *sp; sp = &(*sp)->next)
552                 if ((*sp)->id && service_id &&
553                     0 == strcmp((*sp)->id, service_id))
554                 {
555                     yaz_log(YLOG_FATAL, "Duplicate service: %s", service_id);
556                     break;
557                 }
558                 else if (!(*sp)->id && !service_id)
559                 {
560                     yaz_log(YLOG_FATAL, "Duplicate unnamed service");
561                     break;
562                 }
563
564             if (*sp)  /* service already exist */
565                 return 0;
566             else
567             {
568                 struct conf_service *s = parse_service(n, service_id);
569                 if (s)
570                 {
571                     s->relevance_pct = server->relevance_pct ?
572                         server->relevance_pct : pp2_charset_create(0);
573                     s->sort_pct = server->sort_pct ?
574                         server->sort_pct : pp2_charset_create(0);
575                     s->mergekey_pct = server->mergekey_pct ?
576                         server->mergekey_pct : pp2_charset_create(0);
577                     *sp = s;
578                 }
579             }
580             xmlFree(service_id);
581         }
582         else
583         {
584             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
585             return 0;
586         }
587     }
588     return server;
589 }
590
591 xsltStylesheet *conf_load_stylesheet(const char *fname)
592 {
593     char path[256];
594     if (yaz_is_abspath(fname))
595         yaz_snprintf(path, sizeof(path), fname);
596     else
597         yaz_snprintf(path, sizeof(path), "%s/%s", wrbuf_cstr(confdir), fname);
598     return xsltParseStylesheetFile((xmlChar *) path);
599 }
600
601 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
602                                                         xmlNode *node)
603 {
604     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
605     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
606     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
607
608     memset(r, 0, sizeof(*r));
609
610     if (type)
611     {
612         if (!strcmp((const char *) type, "local"))
613             r->type = Targetprofiles_local;
614         else
615         {
616             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
617             return 0;
618         }
619     }
620     else
621     {
622         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
623         return 0;
624     }
625
626     if (src)
627         r->src = nmem_strdup(nmem, (const char *) src);
628     else
629     {
630         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
631         return 0;
632     }
633     xmlFree(type);
634     xmlFree(src);
635     return r;
636 }
637
638 struct conf_service *locate_service(struct conf_server *server,
639                                     const char *service_id)
640 {
641     struct conf_service *s = server->service;
642     for (; s; s = s->next)
643         if (s->id && service_id && 0 == strcmp(s->id, service_id))
644             return s;
645         else if (!s->id && !service_id)
646             return s;
647     return 0;
648 }
649
650
651 static struct conf_config *parse_config(xmlNode *root)
652 {
653     NMEM nmem = nmem_create();
654     xmlNode *n;
655     struct conf_config *r = nmem_malloc(nmem, sizeof(struct conf_config));
656
657     r->nmem = nmem;
658     r->servers = 0;
659
660     for (n = root->children; n; n = n->next)
661     {
662         if (n->type != XML_ELEMENT_NODE)
663             continue;
664         if (!strcmp((const char *) n->name, "server"))
665         {
666             struct conf_server *tmp = parse_server(nmem, n);
667             if (!tmp)
668                 return 0;
669             tmp->next = r->servers;
670             r->servers = tmp;
671         }
672         else if (!strcmp((const char *) n->name, "targetprofiles"))
673         {
674             yaz_log(YLOG_FATAL, "targetprofiles unsupported here. Must be part of service");
675             return 0;
676
677         }
678         else
679         {
680             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
681             return 0;
682         }
683     }
684     return r;
685 }
686
687 struct conf_config *read_config(const char *fname)
688 {
689     xmlDoc *doc = xmlParseFile(fname);
690     const char *p;
691     struct conf_config *config;
692
693     xmlSubstituteEntitiesDefault(1);
694     xmlLoadExtDtdDefaultValue = 1;
695     if (!doc)
696     {
697         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
698         exit(1);
699     }
700     confdir = wrbuf_alloc();
701     if ((p = strrchr(fname, 
702 #ifdef WIN32
703                      '\\'
704 #else
705                      '/'
706 #endif
707              )))
708     {
709         int len = p - fname;
710         wrbuf_write(confdir, fname, len);
711     }
712     wrbuf_puts(confdir, "");
713     config = parse_config(xmlDocGetRootElement(doc));
714     xmlFreeDoc(doc);
715
716     return config;
717 }
718
719 void config_read_settings(struct conf_config *config,
720                           const char *path_override)
721 {
722     struct conf_service *s = config->servers->service;
723     for (;s ; s = s->next)
724     {
725         init_settings(s);
726         if (path_override)
727             settings_read(s, path_override);
728         else if (s->settings)
729             settings_read(s, s->settings);
730         else if (config->servers->server_settings)
731             settings_read(s, config->servers->server_settings);
732         else
733             yaz_log(YLOG_WARN, "No settings for service");
734     }
735 }
736
737 /*
738  * Local variables:
739  * c-basic-offset: 4
740  * c-file-style: "Stroustrup"
741  * indent-tabs-mode: nil
742  * End:
743  * vim: shiftwidth=4 tabstop=8 expandtab
744  */
745