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