Only use dlclose if DL is present/supported.
[yazproxy-moved-to-github.git] / src / yaz-proxy-config.cpp
1 /* $Id: yaz-proxy-config.cpp,v 1.16 2005-02-20 21:59:08 adam Exp $
2    Copyright (c) 1998-2005, Index Data.
3
4 This file is part of the yaz-proxy.
5
6 YAZ proxy is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with YAZ proxy; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #include <ctype.h>
23
24 #if HAVE_DLFCN_H
25 #include <dlfcn.h>
26 #endif
27
28 #include <yaz/log.h>
29 #include "proxyp.h"
30
31
32 class Yaz_ProxyModule {
33 private:
34     void *m_dl_handle;                 /* dlopen/close handle */
35     Yaz_ProxyModule_entry *m_entry;
36     Yaz_ProxyModule *m_next; 
37     void *m_user_handle;              /* user handle */
38 public:
39     Yaz_ProxyModule(void *dl_handle, Yaz_ProxyModule_entry *ent,
40                     Yaz_ProxyModule *next);
41     ~Yaz_ProxyModule();
42     Yaz_ProxyModule *get_next() { return m_next; };
43     int authenticate(const char *user, const char *group, const char *password);
44 };
45
46 Yaz_ProxyModule::Yaz_ProxyModule(void *dl_handle, Yaz_ProxyModule_entry *ent,
47                                  Yaz_ProxyModule *next)
48 {
49     m_dl_handle = dl_handle;
50     m_entry = ent;
51     m_next = next;
52     m_user_handle = 0;
53     if (m_entry->int_version == 0)
54     {
55         struct Yaz_ProxyModule_int0 *int0 =
56             reinterpret_cast<Yaz_ProxyModule_int0 *>(m_entry->fl);
57         if (int0->init)
58             m_user_handle = (*int0->init)();
59     }
60 }
61
62 Yaz_ProxyModule::~Yaz_ProxyModule()
63 {
64     if (m_entry->int_version == 0)
65     {
66         struct Yaz_ProxyModule_int0 *int0 =
67             reinterpret_cast<Yaz_ProxyModule_int0 *>(m_entry->fl);
68         if (int0->destroy)
69             (*int0->destroy)(m_user_handle);
70     }
71 #if HAVE_DLFCN_H
72     dlclose(m_dl_handle);
73 #endif
74 }
75
76 int Yaz_ProxyModule::authenticate(const char *user, const char *group,
77                                   const char *password)
78 {
79     if (m_entry->int_version == 0)
80     {
81         struct Yaz_ProxyModule_int0 *int0 =
82             reinterpret_cast<Yaz_ProxyModule_int0 *>(m_entry->fl);
83         
84         if (!int0->authenticate)
85             return YAZPROXY_RET_NOT_ME;
86         return (*int0->authenticate)(m_user_handle, user, group, password);
87     }
88     return YAZPROXY_RET_NOT_ME;
89 }
90
91 class Yaz_ProxyConfigP {
92     friend class Yaz_ProxyConfig;
93
94     Yaz_ProxyModule *m_modules;
95
96     int mycmp(const char *hay, const char *item, size_t len);
97     int match_list(int v, const char *m);
98     int atoi_l(const char **cp);
99 #if HAVE_XSLT
100     void load_modules(void);
101     void unload_modules(void);
102     int check_schema(xmlNodePtr ptr, Z_RecordComposition *comp,
103                      const char *schema_identifier);
104     xmlDocPtr m_docPtr;
105     xmlNodePtr m_proxyPtr;
106     void return_target_info(xmlNodePtr ptr, const char **url,
107                             int *limit_bw, int *limit_pdu, int *limit_req,
108                             int *target_idletime, int *client_idletime,
109                             int *keepalive_limit_bw, int *keepalive_limit_pdu,
110                             int *pre_init, const char **cql2rpn,
111                             const char **authentication,
112                             const char **negotiation_charset,
113                             const char **negotiation_lang);
114     void return_limit(xmlNodePtr ptr,
115                       int *limit_bw, int *limit_pdu, int *limit_req);
116     int check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
117                      char **addinfo);
118     xmlNodePtr find_target_node(const char *name, const char *db);
119     xmlNodePtr find_target_db(xmlNodePtr ptr, const char *db);
120     const char *get_text(xmlNodePtr ptr);
121     int check_type_1_attributes(ODR odr, xmlNodePtr ptr,
122                                 Z_AttributeList *attrs,
123                                 char **addinfo);
124     int check_type_1_structure(ODR odr, xmlNodePtr ptr, Z_RPNStructure *q,
125                                char **addinfo);
126     int get_explain_ptr(const char *host, const char *db,
127                         xmlNodePtr *ptr_target, xmlNodePtr *ptr_explain);
128 #endif
129     Yaz_ProxyConfigP();
130     ~Yaz_ProxyConfigP();
131 };
132
133 Yaz_ProxyConfigP::Yaz_ProxyConfigP()
134 {
135 #if HAVE_XSLT
136     m_docPtr = 0;
137     m_proxyPtr = 0;
138 #endif
139     m_modules = 0;
140 }
141
142 Yaz_ProxyConfigP::~Yaz_ProxyConfigP()
143 {
144 #if HAVE_XSLT
145     if (m_docPtr)
146         xmlFreeDoc(m_docPtr);
147 #endif
148 }
149
150 Yaz_ProxyConfig::Yaz_ProxyConfig()
151 {
152     m_cp = new Yaz_ProxyConfigP;
153 }
154
155 Yaz_ProxyConfig::~Yaz_ProxyConfig()
156 {
157     delete m_cp;
158 }
159
160 #if HAVE_XSLT
161 void Yaz_ProxyConfigP::unload_modules()
162 {
163     yaz_log(YLOG_WARN, "unload_modules not implemented yet");
164 }
165 #endif
166
167 #if HAVE_XSLT
168 void Yaz_ProxyConfigP::load_modules()
169 {
170     if (!m_proxyPtr)
171         return;
172     xmlNodePtr ptr;
173     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
174     {
175         const char *fname;
176         if (ptr->type == XML_ELEMENT_NODE 
177             && !strcmp((const char *) ptr->name, "module")
178             && (fname = get_text(ptr)))
179         {
180 #if HAVE_DLFCN_H
181             void *dl_handle = dlopen(fname, RTLD_NOW|RTLD_GLOBAL);
182             if (dl_handle)
183             {
184                 Yaz_ProxyModule_entry *fl_ptr = 0;
185                 fl_ptr = reinterpret_cast<Yaz_ProxyModule_entry *> 
186                     (dlsym(dl_handle, "yazproxy_module"));
187                 if (fl_ptr)
188                 {
189                     Yaz_ProxyModule *m = new Yaz_ProxyModule(dl_handle,
190                                                              fl_ptr,
191                                                              m_modules);
192                     m_modules = m;
193                     yaz_log(YLOG_LOG, "Loading %s OK", fname);
194                 }
195                 else
196                 {
197                     yaz_log(YLOG_WARN, "Loading %s FAIL: missing yazproxy_module symbol", fname);
198                     dlclose(dl_handle);
199                 }
200             }
201             else
202                 yaz_log(YLOG_WARN, "Loading %s FAIL: dlopen failed", fname);
203 #else
204             yaz_log(YLOG_WARN, "Loading &s FAIL: dl unsupported", fname);
205 #endif
206         }
207     }
208 }
209 #endif
210
211 int Yaz_ProxyConfig::read_xml(const char *fname)
212 {
213 #if HAVE_XSLT
214     xmlDocPtr ndoc = xmlParseFile(fname);
215
216     if (!ndoc)
217     {
218         yaz_log(YLOG_WARN, "Config file %s not found or parse error", fname);
219         return -1;  // no good
220     }
221     int noSubstitutions = xmlXIncludeProcess(ndoc);
222     if (noSubstitutions == -1)
223         yaz_log(YLOG_WARN, "XInclude processing failed on config %s", fname);
224
225     xmlNodePtr proxyPtr = xmlDocGetRootElement(ndoc);
226     if (!proxyPtr || proxyPtr->type != XML_ELEMENT_NODE ||
227         strcmp((const char *) proxyPtr->name, "proxy"))
228     {
229         yaz_log(YLOG_WARN, "No proxy element in %s", fname);
230         xmlFreeDoc(ndoc);
231         return -1;
232     }
233     m_cp->m_proxyPtr = proxyPtr;
234
235     // OK: release previous and make it the current one.
236     if (m_cp->m_docPtr)
237         xmlFreeDoc(m_cp->m_docPtr);
238     m_cp->m_docPtr = ndoc;
239
240     m_cp->unload_modules();
241     m_cp->load_modules();
242     return 0;
243 #else
244     return -2;
245 #endif
246 }
247
248 #if HAVE_XSLT
249 const char *Yaz_ProxyConfigP::get_text(xmlNodePtr ptr)
250 {
251     for(ptr = ptr->children; ptr; ptr = ptr->next)
252         if (ptr->type == XML_TEXT_NODE)
253         {
254             xmlChar *t = ptr->content;
255             if (t)
256             {
257                 while (*t == ' ')
258                     t++;
259                 return (const char *) t;
260             }
261         }
262     return 0;
263 }
264 #endif
265
266 #if HAVE_XSLT
267 void Yaz_ProxyConfigP::return_limit(xmlNodePtr ptr,
268                                    int *limit_bw,
269                                    int *limit_pdu,
270                                    int *limit_req)
271 {
272     for (ptr = ptr->children; ptr; ptr = ptr->next)
273     {
274         if (ptr->type == XML_ELEMENT_NODE 
275             && !strcmp((const char *) ptr->name, "bandwidth"))
276         {
277             const char *t = get_text(ptr);
278             if (t)
279                 *limit_bw = atoi(t);
280         }
281         if (ptr->type == XML_ELEMENT_NODE 
282             && !strcmp((const char *) ptr->name, "retrieve"))
283         {
284             const char *t = get_text(ptr);
285             if (t)
286                 *limit_req = atoi(t);
287         }
288         if (ptr->type == XML_ELEMENT_NODE 
289             && !strcmp((const char *) ptr->name, "pdu"))
290         {
291             const char *t = get_text(ptr);
292             if (t)
293                 *limit_pdu = atoi(t);
294         }
295     }
296 }
297 #endif
298
299 #if HAVE_XSLT
300 void Yaz_ProxyConfigP::return_target_info(xmlNodePtr ptr,
301                                           const char **url,
302                                           int *limit_bw,
303                                           int *limit_pdu,
304                                           int *limit_req,
305                                           int *target_idletime,
306                                           int *client_idletime,
307                                           int *keepalive_limit_bw,
308                                           int *keepalive_limit_pdu,
309                                           int *pre_init,
310                                           const char **cql2rpn,
311                                           const char **authentication,
312                                           const char **negotiation_charset,
313                                           const char **negotiation_lang)
314 {
315     *pre_init = 0;
316     int no_url = 0;
317     ptr = ptr->children;
318     for (; ptr; ptr = ptr->next)
319     {
320         if (ptr->type == XML_ELEMENT_NODE 
321             && !strcmp((const char *) ptr->name, "preinit"))
322         {
323             const char *v = get_text(ptr);
324             *pre_init = v ? atoi(v) : 1;
325         }
326         if (ptr->type == XML_ELEMENT_NODE 
327             && !strcmp((const char *) ptr->name, "url"))
328         {
329             const char *t = get_text(ptr);
330             if (t && no_url < MAX_ZURL_PLEX)
331             {
332                 url[no_url++] = t;
333                 url[no_url] = 0;
334             }
335         }
336         if (ptr->type == XML_ELEMENT_NODE 
337             && !strcmp((const char *) ptr->name, "keepalive"))
338         {
339             int dummy;
340             *keepalive_limit_bw = 500000;
341             *keepalive_limit_pdu = 1000;
342             return_limit(ptr, keepalive_limit_bw, keepalive_limit_pdu,
343                          &dummy);
344         }
345         if (ptr->type == XML_ELEMENT_NODE 
346             && !strcmp((const char *) ptr->name, "limit"))
347             return_limit(ptr, limit_bw, limit_pdu, limit_req);
348         if (ptr->type == XML_ELEMENT_NODE 
349             && !strcmp((const char *) ptr->name, "target-timeout"))
350         {
351             const char *t = get_text(ptr);
352             if (t)
353             {
354                 *target_idletime = atoi(t);
355                 if (*target_idletime < 0)
356                     *target_idletime = 0;
357             }
358         }
359         if (ptr->type == XML_ELEMENT_NODE 
360             && !strcmp((const char *) ptr->name, "client-timeout"))
361         {
362             const char *t = get_text(ptr);
363             if (t)
364             {
365                 *client_idletime = atoi(t);
366                 if (*client_idletime < 0)
367                     *client_idletime = 0;
368             }
369         }
370         if (ptr->type == XML_ELEMENT_NODE 
371             && !strcmp((const char *) ptr->name, "cql2rpn"))
372         {
373             const char *t = get_text(ptr);
374             if (t)
375                 *cql2rpn = t;
376         }
377         if (ptr->type == XML_ELEMENT_NODE 
378             && !strcmp((const char *) ptr->name, "authentication"))
379         {
380             const char *t = get_text(ptr);
381             if (t)
382                 *authentication = t;
383         }
384         if (ptr->type == XML_ELEMENT_NODE 
385             && !strcmp((const char *) ptr->name, "negotiation-charset"))
386         {
387             const char *t = get_text(ptr);
388             if (t)
389                 *negotiation_charset = t;
390         }
391         if (ptr->type == XML_ELEMENT_NODE 
392             && !strcmp((const char *) ptr->name, "negotiation-lang"))
393         {
394             const char *t = get_text(ptr);
395             if (t)
396                 *negotiation_lang = t;
397         }
398     }
399 }
400 #endif
401
402 int Yaz_ProxyConfigP::atoi_l(const char **cp)
403 {
404     int v = 0;
405     while (**cp && isdigit(**cp))
406     {
407         v = v*10 + (**cp - '0');
408         (*cp)++;
409     }
410     return v;
411 }
412
413 int Yaz_ProxyConfigP::match_list(int v, const char *m)
414 {
415     while(m && *m)
416     {
417         while(*m && isspace(*m))
418             m++;
419         if (*m == '*')
420             return 1;
421         int l = atoi_l(&m);
422         int h = l;
423         if (*m == '-')
424         {
425             ++m;
426             h = atoi_l(&m);
427         }
428         if (v >= l && v <= h)
429           return 1;
430         if (*m == ',')
431             m++;
432     }
433     return 0;
434 }
435
436 #if HAVE_XSLT
437 int Yaz_ProxyConfigP::check_type_1_attributes(ODR odr, xmlNodePtr ptrl,
438                                               Z_AttributeList *attrs,
439                                               char **addinfo)
440 {
441     int i;
442     for (i = 0; i<attrs->num_attributes; i++)
443     {
444         Z_AttributeElement *el = attrs->attributes[i];
445         
446         if (!el->attributeType)
447             continue;
448         int type = *el->attributeType;
449         int *value = 0;
450         
451         if (el->which == Z_AttributeValue_numeric && el->value.numeric)
452             value = el->value.numeric;
453         
454         xmlNodePtr ptr;
455         for(ptr = ptrl->children; ptr; ptr = ptr->next)
456         {
457             if (ptr->type == XML_ELEMENT_NODE &&
458                 !strcmp((const char *) ptr->name, "attribute"))
459             {
460                 const char *match_type = 0;
461                 const char *match_value = 0;
462                 const char *match_error = 0;
463                 struct _xmlAttr *attr;
464                 for (attr = ptr->properties; attr; attr = attr->next)
465                 {
466                     if (!strcmp((const char *) attr->name, "type") &&
467                         attr->children && attr->children->type == XML_TEXT_NODE)
468                         match_type = (const char *) attr->children->content;
469                     if (!strcmp((const char *) attr->name, "value") &&
470                         attr->children && attr->children->type == XML_TEXT_NODE)
471                         match_value = (const char *) attr->children->content;
472                     if (!strcmp((const char *) attr->name, "error") &&
473                         attr->children && attr->children->type == XML_TEXT_NODE)
474                         match_error = (const char *) attr->children->content;
475                 }
476                 if (match_type && match_value)
477                 {
478                     char addinfo_str[20];
479                     if (!match_list(type, match_type))
480                         continue;
481                     
482                     *addinfo_str = '\0';
483                     if (!strcmp(match_type, "*"))
484                         sprintf (addinfo_str, "%d", type);
485                     else if (value)
486                     {
487                         if (!match_list(*value, match_value))
488                             continue;
489                         sprintf (addinfo_str, "%d", *value);
490                     }
491                     else
492                         continue;
493                     
494                     if (match_error)
495                     {
496                         if (*addinfo_str)
497                             *addinfo = odr_strdup(odr, addinfo_str);
498                         return atoi(match_error);
499                     }
500                     break;
501                 }
502             }
503         }
504     }
505     return 0;
506 }
507 #endif
508
509 #if HAVE_XSLT
510 int Yaz_ProxyConfigP::check_type_1_structure(ODR odr, xmlNodePtr ptr,
511                                             Z_RPNStructure *q,
512                                             char **addinfo)
513 {
514     if (q->which == Z_RPNStructure_complex)
515     {
516         int e = check_type_1_structure(odr, ptr, q->u.complex->s1, addinfo);
517         if (e)
518             return e;
519         e = check_type_1_structure(odr, ptr, q->u.complex->s2, addinfo);
520         return e;
521     }
522     else if (q->which == Z_RPNStructure_simple)
523     {
524         if (q->u.simple->which == Z_Operand_APT)
525         {
526             return check_type_1_attributes(
527                 odr, ptr, q->u.simple->u.attributesPlusTerm->attributes,
528                 addinfo);
529         }
530     }
531     return 0;
532 }
533 #endif
534
535 #if HAVE_XSLT
536 int Yaz_ProxyConfigP::check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
537                                    char **addinfo)
538 {
539     // possibly check for Bib-1
540     return check_type_1_structure(odr, ptr, query->RPNStructure, addinfo);
541 }
542 #endif
543
544 int Yaz_ProxyConfig::check_query(ODR odr, const char *name, Z_Query *query,
545                                  char **addinfo)
546 {
547 #if HAVE_XSLT
548     xmlNodePtr ptr;
549     
550     ptr = m_cp->find_target_node(name, 0);
551     if (ptr)
552     {
553         if (query->which == Z_Query_type_1 || query->which == Z_Query_type_101)
554             return m_cp->check_type_1(odr, ptr, query->u.type_1, addinfo);
555     }
556 #endif
557     return 0;
558 }
559
560 #if HAVE_XSLT
561 int Yaz_ProxyConfigP::check_schema(xmlNodePtr ptr, Z_RecordComposition *comp,
562                                    const char *schema_identifier)
563 {
564     char *esn = 0;
565     int default_match = 1;
566     if (comp && comp->which == Z_RecordComp_simple &&
567         comp->u.simple && comp->u.simple->which == Z_ElementSetNames_generic)
568     {
569         esn = comp->u.simple->u.generic;
570     }
571     // if no ESN/schema was given accept..
572     if (!esn)
573         return 1;
574     // check if schema identifier match
575     if (schema_identifier && !strcmp(esn, schema_identifier))
576         return 1;
577     // Check each name element
578     for (; ptr; ptr = ptr->next)
579     {
580         if (ptr->type == XML_ELEMENT_NODE 
581             && !strcmp((const char *) ptr->name, "name"))
582         {
583             xmlNodePtr tptr = ptr->children;
584             default_match = 0;
585             for (; tptr; tptr = tptr->next)
586                 if (tptr->type == XML_TEXT_NODE && tptr->content)
587                 {
588                     xmlChar *t = tptr->content;
589                     while (*t && isspace(*t))
590                         t++;
591                     int i = 0;
592                     while (esn[i] && esn[i] == t[i])
593                         i++;
594                     if (!esn[i] && (!t[i] || isspace(t[i])))
595                         return 1;
596                 }
597         }
598     }
599     return default_match;
600 }
601 #endif
602
603 const char *Yaz_ProxyConfig::check_mime_type(const char *path)
604 {
605     struct {
606         const char *mask;
607         const char *type;
608     } types[] = {
609         {".xml", "text/xml"},
610         {".xsl", "text/xml"},
611         {".tkl", "text/xml"},
612         {".xsd", "text/xml"},
613         {".html", "text/html"},
614         {".jpg", "image/jpeg"},
615         {".png", "image/png"},
616         {".gif", "image/gif"},
617         {0, "text/plain"},
618         {0, 0},
619     };
620     int i;
621     size_t plen = strlen (path);
622     for (i = 0; types[i].type; i++)
623         if (types[i].mask == 0)
624             return types[i].type;
625         else
626         {
627             size_t mlen = strlen(types[i].mask);
628             if (plen > mlen && !memcmp(path+plen-mlen, types[i].mask, mlen))
629                 return types[i].type;
630         }
631     return "application/octet-stream";
632 }
633
634
635 int Yaz_ProxyConfig::check_authentication(const char *user,
636                                           const char *group,
637                                           const char *password)
638 {
639     Yaz_ProxyModule *m = m_cp->m_modules;
640
641     int ret = YAZPROXY_RET_NOT_ME;
642     for (; m; m = m->get_next())
643     {
644         ret = m->authenticate(user, group, password);
645         if (ret != YAZPROXY_RET_NOT_ME)
646             break;
647     }
648     if (ret == YAZPROXY_RET_PERM)
649         return 0;
650     return 1;
651 }
652
653 int Yaz_ProxyConfig::check_syntax(ODR odr, const char *name,
654                                   Odr_oid *syntax, Z_RecordComposition *comp,
655                                   char **addinfo,
656                                   char **stylesheet, char **schema,
657                                   char **backend_type,
658                                   char **backend_charset,
659                                   char **usemarcon_ini_stage1,
660                                   char **usemarcon_ini_stage2
661                                   )
662 {
663     if (stylesheet)
664     {
665         xfree (*stylesheet);
666         *stylesheet = 0;
667     }
668     if (schema)
669     {
670         xfree (*schema);
671         *schema = 0;
672     }
673     if (backend_type)
674     {
675         xfree (*backend_type);
676         *backend_type = 0;
677     }
678     if (backend_charset)
679     {
680         xfree (*backend_charset);
681         *backend_charset = 0;
682     }
683     if (usemarcon_ini_stage1)
684     {
685         xfree (*usemarcon_ini_stage1);
686         *usemarcon_ini_stage1 = 0;
687     }
688     if (usemarcon_ini_stage2)
689     {
690         xfree (*usemarcon_ini_stage2);
691         *usemarcon_ini_stage2 = 0;
692     }
693 #if HAVE_XSLT
694     int syntax_has_matched = 0;
695     xmlNodePtr ptr;
696     
697     ptr = m_cp->find_target_node(name, 0);
698     if (!ptr)
699         return 0;
700     for(ptr = ptr->children; ptr; ptr = ptr->next)
701     {
702         if (ptr->type == XML_ELEMENT_NODE &&
703             !strcmp((const char *) ptr->name, "syntax"))
704         {
705             int match = 0;  // if we match record syntax
706             const char *match_type = 0;
707             const char *match_error = 0;
708             const char *match_marcxml = 0;
709             const char *match_stylesheet = 0;
710             const char *match_identifier = 0;
711             const char *match_backend_type = 0;
712             const char *match_backend_charset = 0;
713             const char *match_usemarcon_ini_stage1 = 0;
714             const char *match_usemarcon_ini_stage2 = 0;
715             struct _xmlAttr *attr;
716             for (attr = ptr->properties; attr; attr = attr->next)
717             {
718                 if (!strcmp((const char *) attr->name, "type") &&
719                     attr->children && attr->children->type == XML_TEXT_NODE)
720                     match_type = (const char *) attr->children->content;
721                 if (!strcmp((const char *) attr->name, "error") &&
722                     attr->children && attr->children->type == XML_TEXT_NODE)
723                     match_error = (const char *) attr->children->content;
724                 if (!strcmp((const char *) attr->name, "marcxml") &&
725                     attr->children && attr->children->type == XML_TEXT_NODE)
726                     match_marcxml = (const char *) attr->children->content;
727                 if (!strcmp((const char *) attr->name, "stylesheet") &&
728                     attr->children && attr->children->type == XML_TEXT_NODE)
729                     match_stylesheet = (const char *) attr->children->content;
730                 if (!strcmp((const char *) attr->name, "identifier") &&
731                     attr->children && attr->children->type == XML_TEXT_NODE)
732                     match_identifier = (const char *) attr->children->content;
733                 if (!strcmp((const char *) attr->name, "backendtype") &&
734                     attr->children && attr->children->type == XML_TEXT_NODE)
735                     match_backend_type = (const char *)
736                         attr->children->content;
737                 if (!strcmp((const char *) attr->name, "backendcharset") &&
738                     attr->children && attr->children->type == XML_TEXT_NODE)
739                     match_backend_charset = (const char *)
740                         attr->children->content;
741                 if (!strcmp((const char *) attr->name, "usemarconstage1") &&
742                     attr->children && attr->children->type == XML_TEXT_NODE)
743                     match_usemarcon_ini_stage1 = (const char *)
744                         attr->children->content;
745                 if (!strcmp((const char *) attr->name, "usemarconstage2") &&
746                     attr->children && attr->children->type == XML_TEXT_NODE)
747                     match_usemarcon_ini_stage2 = (const char *)
748                         attr->children->content;
749             }
750             if (match_type)
751             {
752                 if (!strcmp(match_type, "*"))
753                     match = 1;
754                 else if (!strcmp(match_type, "none"))
755                 {
756                     if (syntax == 0)
757                         match = 1;
758                 }
759                 else if (syntax)
760                 {
761                     int match_oid[OID_SIZE];
762                     oid_name_to_oid(CLASS_RECSYN, match_type, match_oid);
763                     if (oid_oidcmp(match_oid, syntax) == 0)
764                         match = 1;
765                 }
766             }
767             if (match)
768             {
769                 if (!match_error)
770                     syntax_has_matched = 1;
771                 match = m_cp->check_schema(ptr->children, comp,
772                                            match_identifier);
773             }
774             if (match)
775             {
776                 if (stylesheet && match_stylesheet)
777                 {
778                     xfree(*stylesheet);
779                     *stylesheet = xstrdup(match_stylesheet);
780                 }
781                 if (schema && match_identifier)
782                 {
783                     xfree(*schema);
784                     *schema = xstrdup(match_identifier);
785                 }
786                 if (backend_type && match_backend_type)
787                 {
788                     xfree(*backend_type);
789                     *backend_type = xstrdup(match_backend_type);
790                 }
791                 if (backend_charset && match_backend_charset)
792                 {
793                     xfree(*backend_charset);
794                     *backend_charset = xstrdup(match_backend_charset);
795                 }
796                 if (usemarcon_ini_stage1 && match_usemarcon_ini_stage1)
797                 {
798                     xfree(*usemarcon_ini_stage1);
799                     *usemarcon_ini_stage1 = xstrdup(match_usemarcon_ini_stage1);
800                 }
801                 if (usemarcon_ini_stage1 && match_usemarcon_ini_stage2)
802                 {
803                     xfree(*usemarcon_ini_stage2);
804                     *usemarcon_ini_stage2 = xstrdup(match_usemarcon_ini_stage2);
805                 }
806                 if (match_marcxml)
807                 {
808                     return -1;
809                 }
810                 if (match_error)
811                 {
812                     if (syntax_has_matched)  // if syntax OK, bad schema/ESN
813                         return 25;
814                     if (syntax)
815                     {
816                         char dotoid_str[100];
817                         oid_to_dotstring(syntax, dotoid_str);
818                         *addinfo = odr_strdup(odr, dotoid_str);
819                     }
820                     return atoi(match_error);
821                 }
822                 return 0;
823             }
824         }
825     }
826 #endif
827     return 0;
828 }
829
830 #if HAVE_XSLT
831 xmlNodePtr Yaz_ProxyConfigP::find_target_db(xmlNodePtr ptr, const char *db)
832 {
833     xmlNodePtr dptr;
834     if (!db)
835         return ptr;
836     if (!ptr)
837         return 0;
838     for (dptr = ptr->children; dptr; dptr = dptr->next)
839         if (dptr->type == XML_ELEMENT_NODE &&
840             !strcmp((const char *) dptr->name, "database"))
841         {
842             struct _xmlAttr *attr;
843             for (attr = dptr->properties; attr; attr = attr->next)
844                 if (!strcmp((const char *) attr->name, "name"))
845                 {
846                     if (attr->children
847                         && attr->children->type==XML_TEXT_NODE
848                         && attr->children->content 
849                         && (!strcmp((const char *) attr->children->content, db)
850                             || !strcmp((const char *) attr->children->content,
851                                        "*")))
852                         return dptr;
853                 }
854         }
855     return ptr;
856 }
857     
858 xmlNodePtr Yaz_ProxyConfigP::find_target_node(const char *name, const char *db)
859 {
860     xmlNodePtr ptr;
861     if (!m_proxyPtr)
862         return 0;
863     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
864     {
865         if (ptr->type == XML_ELEMENT_NODE &&
866             !strcmp((const char *) ptr->name, "target"))
867         {
868             // default one ? 
869             if (!name)
870             {
871                 // <target default="1"> ?
872                 struct _xmlAttr *attr;
873                 for (attr = ptr->properties; attr; attr = attr->next)
874                     if (!strcmp((const char *) attr->name, "default") &&
875                         attr->children && attr->children->type == XML_TEXT_NODE)
876                     {
877                         xmlChar *t = attr->children->content;
878                         if (!t || *t == '1')
879                         {
880                             return find_target_db(ptr, db);
881                         }
882                     }
883             }
884             else
885             {
886                 // <target name="name"> ?
887                 struct _xmlAttr *attr;
888                 for (attr = ptr->properties; attr; attr = attr->next)
889                     if (!strcmp((const char *) attr->name, "name"))
890                     {
891                         if (attr->children
892                             && attr->children->type==XML_TEXT_NODE
893                             && attr->children->content 
894                             && (!strcmp((const char *) attr->children->content,
895                                         name)
896                                 || !strcmp((const char *) attr->children->content,
897                                            "*")))
898                         {
899                             return find_target_db(ptr, db);
900                         }
901                     }
902             }
903         }
904     }
905     return 0;
906 }
907 #endif
908
909 int Yaz_ProxyConfig::get_target_no(int no,
910                                    const char **name,
911                                    const char **url,
912                                    int *limit_bw,
913                                    int *limit_pdu,
914                                    int *limit_req,
915                                    int *target_idletime,
916                                    int *client_idletime,
917                                    int *max_clients,
918                                    int *keepalive_limit_bw,
919                                    int *keepalive_limit_pdu,
920                                    int *pre_init,
921                                    const char **cql2rpn,
922                                    const char **authentication,
923                                    const char **negotiation_charset,
924                                    const char **negotiation_lang)
925 {
926 #if HAVE_XSLT
927     xmlNodePtr ptr;
928     if (!m_cp->m_proxyPtr)
929         return 0;
930     int i = 0;
931     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
932         if (ptr->type == XML_ELEMENT_NODE &&
933             !strcmp((const char *) ptr->name, "target"))
934         {
935             if (i == no)
936             {
937                 struct _xmlAttr *attr;
938                 for (attr = ptr->properties; attr; attr = attr->next)
939                     if (!strcmp((const char *) attr->name, "name"))
940                     {
941                         if (attr->children
942                             && attr->children->type==XML_TEXT_NODE
943                             && attr->children->content)
944                             *name = (const char *) attr->children->content;
945                     }
946                 m_cp->return_target_info(
947                     ptr, url,
948                     limit_bw, limit_pdu, limit_req,
949                     target_idletime, client_idletime,
950                     keepalive_limit_bw, keepalive_limit_pdu,
951                     pre_init, cql2rpn, authentication,
952                     negotiation_charset, negotiation_lang);
953                 return 1;
954             }
955             i++;
956         }
957 #endif
958     return 0;
959 }
960
961 int Yaz_ProxyConfigP::mycmp(const char *hay, const char *item, size_t len)
962 {
963     if (len == strlen(item) && memcmp(hay, item, len) == 0)
964         return 1;
965     return 0;
966 }
967
968 void Yaz_ProxyConfig::get_generic_info(int *log_mask,
969                                        int *max_clients)
970 {
971 #if HAVE_XSLT
972     xmlNodePtr ptr;
973     if (!m_cp->m_proxyPtr)
974         return;
975     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
976     {
977         if (ptr->type == XML_ELEMENT_NODE 
978             && !strcmp((const char *) ptr->name, "log"))
979         {
980             const char *v = m_cp->get_text(ptr);
981             *log_mask = 0;
982             while (v && *v)
983             {
984                 const char *cp = v;
985                 while (*cp && *cp != ',' && !isspace(*cp))
986                     cp++;
987                 size_t len = cp - v;
988                 if (m_cp->mycmp(v, "client-apdu", len))
989                     *log_mask |= PROXY_LOG_APDU_CLIENT;
990                 if (m_cp->mycmp(v, "server-apdu", len))
991                     *log_mask |= PROXY_LOG_APDU_SERVER;
992                 if (m_cp->mycmp(v, "client-requests", len))
993                     *log_mask |= PROXY_LOG_REQ_CLIENT;
994                 if (m_cp->mycmp(v, "server-requests", len))
995                     *log_mask |= PROXY_LOG_REQ_SERVER;
996                 if (isdigit(*v))
997                     *log_mask |= atoi(v);
998                 if (*cp == ',')
999                     cp++;
1000                 while (*cp && isspace(*cp))
1001                     cp++;
1002                 v = cp;
1003             }
1004         }
1005         if (ptr->type == XML_ELEMENT_NODE &&
1006             !strcmp((const char *) ptr->name, "max-clients"))
1007         {
1008             const char *t = m_cp->get_text(ptr);
1009             if (t)
1010             {
1011                 *max_clients = atoi(t);
1012                 if (*max_clients  < 1)
1013                     *max_clients = 1;
1014             }
1015         }
1016     }
1017 #endif
1018 }
1019
1020 #if HAVE_XSLT
1021 int Yaz_ProxyConfigP::get_explain_ptr(const char *host, const char *db,
1022                                       xmlNodePtr *ptr_target,
1023                                       xmlNodePtr *ptr_explain)
1024 {
1025     xmlNodePtr ptr;
1026     if (!m_proxyPtr)
1027         return 0;
1028     if (!db)
1029         return 0;
1030     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
1031     {
1032         if (ptr->type == XML_ELEMENT_NODE &&
1033             !strcmp((const char *) ptr->name, "target"))
1034         {
1035             *ptr_target = ptr;
1036             xmlNodePtr ptr = (*ptr_target)->children;
1037             for (; ptr; ptr = ptr->next)
1038             {
1039                 if (ptr->type == XML_ELEMENT_NODE &&
1040                     !strcmp((const char *) ptr->name, "explain"))
1041                 {
1042                     *ptr_explain = ptr;
1043                     xmlNodePtr ptr = (*ptr_explain)->children;
1044
1045                     for (; ptr; ptr = ptr->next)
1046                         if (ptr->type == XML_ELEMENT_NODE &&
1047                             !strcmp((const char *) ptr->name, "serverInfo"))
1048                             break;
1049                     if (!ptr)
1050                         continue;
1051                     for (ptr = ptr->children; ptr; ptr = ptr->next)
1052                         if (ptr->type == XML_ELEMENT_NODE &&
1053                             !strcmp((const char *) ptr->name, "database"))
1054                             break;
1055                     
1056                     if (!ptr)
1057                         continue;
1058                     for (ptr = ptr->children; ptr; ptr = ptr->next)
1059                         if (ptr->type == XML_TEXT_NODE &&
1060                             ptr->content &&
1061                             !strcmp((const char *) ptr->content, db))
1062                             break;
1063                     if (!ptr)
1064                         continue;
1065                     return 1;
1066                 }
1067             }
1068         }
1069     }
1070     return 0;
1071 }
1072 #endif
1073
1074 const char *Yaz_ProxyConfig::get_explain_name(const char *db,
1075                                               const char **backend_db)
1076 {
1077 #if HAVE_XSLT
1078     xmlNodePtr ptr_target, ptr_explain;
1079     if (m_cp->get_explain_ptr(0, db, &ptr_target, &ptr_explain)
1080         && ptr_target)
1081     {
1082         struct _xmlAttr *attr;
1083         const char *name = 0;
1084         
1085         for (attr = ptr_target->properties; attr; attr = attr->next)
1086             if (!strcmp((const char *) attr->name, "name")
1087                 && attr->children
1088                 && attr->children->type==XML_TEXT_NODE
1089                 && attr->children->content 
1090                 && attr->children->content[0])
1091             {
1092                 name = (const char *)attr->children->content;
1093                 break;
1094             }
1095         if (name)
1096         {
1097             for (attr = ptr_target->properties; attr; attr = attr->next)
1098                 if (!strcmp((const char *) attr->name, "database"))
1099                 {
1100                     if (attr->children
1101                         && attr->children->type==XML_TEXT_NODE
1102                         && attr->children->content)
1103                         *backend_db = (const char *) attr->children->content;
1104                 }
1105             return name;
1106         }
1107     }
1108 #endif
1109     return 0;
1110 }
1111
1112 char *Yaz_ProxyConfig::get_explain_doc(ODR odr, const char *name,
1113                                        const char *db, int *len)
1114 {
1115 #if HAVE_XSLT
1116     xmlNodePtr ptr_target, ptr_explain;
1117     if (m_cp->get_explain_ptr(0 /* host */, db, &ptr_target, &ptr_explain))
1118     {
1119         xmlNodePtr ptr2 = xmlCopyNode(ptr_explain, 1);
1120         
1121         xmlDocPtr doc = xmlNewDoc((const xmlChar *) "1.0");
1122         
1123         xmlDocSetRootElement(doc, ptr2);
1124         
1125         xmlChar *buf_out;
1126         xmlDocDumpMemory(doc, &buf_out, len);
1127         char *content = (char*) odr_malloc(odr, *len);
1128         memcpy(content, buf_out, *len);
1129         
1130         xmlFree(buf_out);
1131         xmlFreeDoc(doc);
1132         return content;
1133     }
1134 #endif
1135     return 0;
1136 }
1137
1138 void Yaz_ProxyConfig::get_target_info(const char *name,
1139                                       const char **url,
1140                                       int *limit_bw,
1141                                       int *limit_pdu,
1142                                       int *limit_req,
1143                                       int *target_idletime,
1144                                       int *client_idletime,
1145                                       int *max_clients,
1146                                       int *keepalive_limit_bw,
1147                                       int *keepalive_limit_pdu,
1148                                       int *pre_init,
1149                                       const char **cql2rpn,
1150                                       const char **authentication,
1151                                       const char **negotiation_charset,
1152                                       const char **negotiation_lang)
1153 {
1154 #if HAVE_XSLT
1155     xmlNodePtr ptr;
1156     if (!m_cp->m_proxyPtr)
1157     {
1158         url[0] = name;
1159         url[1] = 0;
1160         return;
1161     }
1162     url[0] = 0;
1163     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
1164     {
1165         if (ptr->type == XML_ELEMENT_NODE &&
1166             !strcmp((const char *) ptr->name, "max-clients"))
1167         {
1168             const char *t = m_cp->get_text(ptr);
1169             if (t)
1170             {
1171                 *max_clients = atoi(t);
1172                 if (*max_clients  < 1)
1173                     *max_clients = 1;
1174             }
1175         }
1176     }
1177     ptr = m_cp->find_target_node(name, 0);
1178     if (ptr)
1179     {
1180         if (name)
1181         {
1182             url[0] = name;
1183             url[1] = 0;
1184         }
1185         m_cp->return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
1186                                  target_idletime, client_idletime,
1187                                  keepalive_limit_bw, keepalive_limit_pdu,
1188                                  pre_init, cql2rpn, authentication,
1189                                  negotiation_charset, negotiation_lang);
1190     }
1191 #else
1192     *url = name;
1193     return;
1194 #endif
1195 }
1196
1197