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