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