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