Allow client-ip to be logged for each log entry. Speciy
[yazproxy-moved-to-github.git] / src / yaz-proxy-config.cpp
1 /* $Id: yaz-proxy-config.cpp,v 1.29 2006-04-06 12:04:19 adam Exp $
2    Copyright (c) 1998-2006, Index Data.
3
4 This file is part of the yazproxy.
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,
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);
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 {
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     }
208 }
209 #endif
210
211 #if HAVE_XSLT
212 void Yaz_ProxyConfigP::return_target_info(xmlNodePtr ptr,
213                                           const char **url,
214                                           int *limit_bw,
215                                           int *limit_pdu,
216                                           int *limit_req,
217                                           int *limit_search,
218                                           int *target_idletime,
219                                           int *client_idletime,
220                                           int *keepalive_limit_bw,
221                                           int *keepalive_limit_pdu,
222                                           int *pre_init,
223                                           const char **cql2rpn,
224                                           const char **negotiation_charset,
225                                           const char **negotiation_lang,
226                                           const char **target_charset,
227                                           const char **default_client_query_charset)
228 {
229     *pre_init = 0;
230     int no_url = 0;
231     ptr = ptr->children;
232     for (; ptr; ptr = ptr->next)
233     {
234         if (ptr->type == XML_ELEMENT_NODE
235             && !strcmp((const char *) ptr->name, "preinit"))
236         {
237             const char *v = get_text(ptr);
238             *pre_init = v ? atoi(v) : 1;
239         }
240         if (ptr->type == XML_ELEMENT_NODE
241             && !strcmp((const char *) ptr->name, "url"))
242         {
243             const char *t = get_text(ptr);
244             if (t && no_url < MAX_ZURL_PLEX)
245             {
246                 url[no_url++] = t;
247                 url[no_url] = 0;
248             }
249         }
250         if (ptr->type == XML_ELEMENT_NODE
251             && !strcmp((const char *) ptr->name, "keepalive"))
252         {
253             int dummy;
254             *keepalive_limit_bw = 500000;
255             *keepalive_limit_pdu = 1000;
256             return_limit(ptr, keepalive_limit_bw, keepalive_limit_pdu,
257                          &dummy, &dummy);
258         }
259         if (ptr->type == XML_ELEMENT_NODE 
260             && !strcmp((const char *) ptr->name, "limit"))
261             return_limit(ptr, limit_bw, limit_pdu, limit_req,
262                          limit_search);
263         if (ptr->type == XML_ELEMENT_NODE 
264             && !strcmp((const char *) ptr->name, "target-timeout"))
265         {
266             const char *t = get_text(ptr);
267             if (t)
268             {
269                 *target_idletime = atoi(t);
270                 if (*target_idletime < 0)
271                     *target_idletime = 0;
272             }
273         }
274         if (ptr->type == XML_ELEMENT_NODE
275             && !strcmp((const char *) ptr->name, "client-timeout"))
276         {
277             const char *t = get_text(ptr);
278             if (t)
279             {
280                 *client_idletime = atoi(t);
281                 if (*client_idletime < 0)
282                     *client_idletime = 0;
283             }
284         }
285         if (ptr->type == XML_ELEMENT_NODE
286             && !strcmp((const char *) ptr->name, "cql2rpn"))
287         {
288             const char *t = get_text(ptr);
289             if (t)
290                 *cql2rpn = t;
291         }
292         if (ptr->type == XML_ELEMENT_NODE
293             && !strcmp((const char *) ptr->name, "target-charset"))
294         {
295             const char *t = get_text(ptr);
296             if (t && target_charset)
297                 *target_charset = t;
298         }
299         if (ptr->type == XML_ELEMENT_NODE
300             && !strcmp((const char *) ptr->name, "default-client-charset"))
301         {
302             const char *t = get_text(ptr);
303             if (t && default_client_query_charset)
304                 *default_client_query_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         {".css", "text/css"},
540         {".pdf", "application/pdf"},
541         {0, "text/plain"},
542         {0, 0},
543     };
544     int i;
545     size_t plen = strlen (path);
546     for (i = 0; types[i].type; i++)
547         if (types[i].mask == 0)
548             return types[i].type;
549         else
550         {
551             size_t mlen = strlen(types[i].mask);
552             if (plen > mlen && !memcmp(path+plen-mlen, types[i].mask, mlen))
553                 return types[i].type;
554         }
555     return "application/octet-stream";
556 }
557
558
559 void Yaz_ProxyConfig::target_authentication(const char *name,
560                                             ODR odr, Z_InitRequest *req)
561 {
562 #if HAVE_XSLT
563     xmlNodePtr ptr = m_cp->find_target_node(name, 0);
564     if (!ptr)
565         return ;
566
567     for (ptr = ptr->children; ptr; ptr = ptr->next)
568         if (ptr->type == XML_ELEMENT_NODE &&
569             !strcmp((const char *) ptr->name, "target-authentication"))
570         {
571             struct _xmlAttr *attr;
572             const char *type = "open";
573             for (attr = ptr->properties; attr; attr = attr->next)
574             {
575                 if (!strcmp((const char *) attr->name, "type") &&
576                     attr->children && attr->children->type == XML_TEXT_NODE)
577                     type = (const char *) attr->children->content;
578             }
579             const char *t = m_cp->get_text(ptr);
580             if (!t || !strcmp(type, "none"))
581             {
582                 req->idAuthentication = 0;
583             }
584             else if (!strcmp(type, "anonymous"))
585             {
586                 req->idAuthentication =
587                     (Z_IdAuthentication *)
588                     odr_malloc (odr, sizeof(*req->idAuthentication));
589                 req->idAuthentication->which =
590                     Z_IdAuthentication_anonymous;
591                 req->idAuthentication->u.anonymous = odr_nullval();
592             }
593             else if (!strcmp(type, "open"))
594             {
595                 req->idAuthentication =
596                     (Z_IdAuthentication *)
597                     odr_malloc (odr, sizeof(*req->idAuthentication));
598                 req->idAuthentication->which =
599                     Z_IdAuthentication_open;
600                 req->idAuthentication->u.open = odr_strdup (odr, t);
601             }
602             else if (!strcmp(type, "idPass"))
603             {
604                 char user[64], group[64], password[64];
605                 *group = '\0';
606                 *password = '\0';
607                 *user = '\0';
608                 sscanf(t, "%63[^:]:%63[^:]:%63s", user, group, password);
609
610                 req->idAuthentication =
611                     (Z_IdAuthentication *)
612                     odr_malloc (odr, sizeof(*req->idAuthentication));
613                 req->idAuthentication->which =
614                     Z_IdAuthentication_idPass;
615                 req->idAuthentication->u.idPass =
616                     (Z_IdPass*) odr_malloc(odr, sizeof(Z_IdPass));
617                 req->idAuthentication->u.idPass->userId =
618                     *user ? odr_strdup(odr, user) : 0;
619                 req->idAuthentication->u.idPass->groupId =
620                     *group ? odr_strdup(odr, group) : 0;
621                 req->idAuthentication->u.idPass->password =
622                     *password ? odr_strdup(odr, password) : 0;
623             }
624         }
625 #endif
626 }
627
628 int Yaz_ProxyConfig::client_authentication(const char *name,
629                                            const char *user,
630                                            const char *group,
631                                            const char *password,
632                                            const char *peer_IP)
633 {
634     int ret = YAZPROXY_RET_NOT_ME;
635 #if HAVE_XSLT
636     xmlNodePtr ptr;
637     ptr = m_cp->find_target_node(name, 0);
638     if (!ptr)
639         return 1;
640     for (ptr = ptr->children; ptr; ptr = ptr->next)
641         if (ptr->type == XML_ELEMENT_NODE &&
642             !strcmp((const char *) ptr->name, "client-authentication"))
643         {
644             struct _xmlAttr *attr;
645             const char *module_name = 0;
646             for (attr = ptr->properties; attr; attr = attr->next)
647             {
648                 if (!strcmp((const char *) attr->name, "module") &&
649                     attr->children && attr->children->type == XML_TEXT_NODE)
650                     module_name = (const char *) attr->children->content;
651             }
652             ret = m_cp->m_modules.authenticate(module_name,
653                                                name, ptr,
654                                                user, group, password,
655                                                peer_IP
656                 );
657             if (ret != YAZPROXY_RET_NOT_ME)
658                 break;
659         }
660 #endif
661     if (ret == YAZPROXY_RET_PERM)
662         return 0;
663     return 1;
664 }
665
666 int Yaz_ProxyConfig::check_syntax(ODR odr, const char *name,
667                                   Odr_oid *syntax, Z_RecordComposition *comp,
668                                   char **addinfo,
669                                   char **stylesheet, char **schema,
670                                   char **backend_type,
671                                   char **backend_charset,
672                                   char **usemarcon_ini_stage1,
673                                   char **usemarcon_ini_stage2
674                                   )
675 {
676     if (stylesheet)
677     {
678         xfree (*stylesheet);
679         *stylesheet = 0;
680     }
681     if (schema)
682     {
683         xfree (*schema);
684         *schema = 0;
685     }
686     if (backend_type)
687     {
688         xfree (*backend_type);
689         *backend_type = 0;
690     }
691     if (backend_charset)
692     {
693         xfree (*backend_charset);
694         *backend_charset = 0;
695     }
696     if (usemarcon_ini_stage1)
697     {
698         xfree (*usemarcon_ini_stage1);
699         *usemarcon_ini_stage1 = 0;
700     }
701     if (usemarcon_ini_stage2)
702     {
703         xfree (*usemarcon_ini_stage2);
704         *usemarcon_ini_stage2 = 0;
705     }
706 #if HAVE_XSLT
707     int syntax_has_matched = 0;
708     xmlNodePtr ptr;
709
710     ptr = m_cp->find_target_node(name, 0);
711     if (!ptr)
712         return 0;
713     for(ptr = ptr->children; ptr; ptr = ptr->next)
714     {
715         if (ptr->type == XML_ELEMENT_NODE &&
716             !strcmp((const char *) ptr->name, "syntax"))
717         {
718             int match = 0;  // if we match record syntax
719             const char *match_type = 0;
720             const char *match_error = 0;
721             const char *match_marcxml = 0;
722             const char *match_stylesheet = 0;
723             const char *match_identifier = 0;
724             const char *match_backend_type = 0;
725             const char *match_backend_charset = 0;
726             const char *match_usemarcon_ini_stage1 = 0;
727             const char *match_usemarcon_ini_stage2 = 0;
728             struct _xmlAttr *attr;
729             for (attr = ptr->properties; attr; attr = attr->next)
730             {
731                 if (!strcmp((const char *) attr->name, "type") &&
732                     attr->children && attr->children->type == XML_TEXT_NODE)
733                     match_type = (const char *) attr->children->content;
734                 if (!strcmp((const char *) attr->name, "error") &&
735                     attr->children && attr->children->type == XML_TEXT_NODE)
736                     match_error = (const char *) attr->children->content;
737                 if (!strcmp((const char *) attr->name, "marcxml") &&
738                     attr->children && attr->children->type == XML_TEXT_NODE)
739                     match_marcxml = (const char *) attr->children->content;
740                 if (!strcmp((const char *) attr->name, "stylesheet") &&
741                     attr->children && attr->children->type == XML_TEXT_NODE)
742                     match_stylesheet = (const char *) attr->children->content;
743                 if (!strcmp((const char *) attr->name, "identifier") &&
744                     attr->children && attr->children->type == XML_TEXT_NODE)
745                     match_identifier = (const char *) attr->children->content;
746                 if (!strcmp((const char *) attr->name, "backendtype") &&
747                     attr->children && attr->children->type == XML_TEXT_NODE)
748                     match_backend_type = (const char *)
749                         attr->children->content;
750                 if (!strcmp((const char *) attr->name, "backendcharset") &&
751                     attr->children && attr->children->type == XML_TEXT_NODE)
752                     match_backend_charset = (const char *)
753                         attr->children->content;
754                 if (!strcmp((const char *) attr->name, "usemarconstage1") &&
755                     attr->children && attr->children->type == XML_TEXT_NODE)
756                     match_usemarcon_ini_stage1 = (const char *)
757                         attr->children->content;
758                 if (!strcmp((const char *) attr->name, "usemarconstage2") &&
759                     attr->children && attr->children->type == XML_TEXT_NODE)
760                     match_usemarcon_ini_stage2 = (const char *)
761                         attr->children->content;
762             }
763             if (match_type)
764             {
765                 if (!strcmp(match_type, "*"))
766                     match = 1;
767                 else if (!strcmp(match_type, "none"))
768                 {
769                     if (syntax == 0)
770                         match = 1;
771                 }
772                 else if (syntax)
773                 {
774                     int match_oid[OID_SIZE];
775                     oid_name_to_oid(CLASS_RECSYN, match_type, match_oid);
776                     if (oid_oidcmp(match_oid, syntax) == 0)
777                         match = 1;
778                 }
779             }
780             if (match)
781             {
782                 if (!match_error)
783                     syntax_has_matched = 1;
784                 match = m_cp->check_schema(ptr->children, comp,
785                                            match_identifier);
786             }
787             if (match)
788             {
789                 if (stylesheet && match_stylesheet)
790                 {
791                     xfree(*stylesheet);
792                     *stylesheet = xstrdup(match_stylesheet);
793                 }
794                 if (schema && match_identifier)
795                 {
796                     xfree(*schema);
797                     *schema = xstrdup(match_identifier);
798                 }
799                 if (backend_type && match_backend_type)
800                 {
801                     xfree(*backend_type);
802                     *backend_type = xstrdup(match_backend_type);
803                 }
804                 if (backend_charset && match_backend_charset)
805                 {
806                     xfree(*backend_charset);
807                     *backend_charset = xstrdup(match_backend_charset);
808                 }
809                 if (usemarcon_ini_stage1 && match_usemarcon_ini_stage1)
810                 {
811                     xfree(*usemarcon_ini_stage1);
812                     *usemarcon_ini_stage1 = xstrdup(match_usemarcon_ini_stage1);
813                 }
814                 if (usemarcon_ini_stage1 && match_usemarcon_ini_stage2)
815                 {
816                     xfree(*usemarcon_ini_stage2);
817                     *usemarcon_ini_stage2 = xstrdup(match_usemarcon_ini_stage2);
818                 }
819                 if (match_marcxml)
820                 {
821                     return -1;
822                 }
823                 if (match_error)
824                 {
825                     if (syntax_has_matched)  // if syntax OK, bad schema/ESN
826                         return 25;
827                     if (syntax)
828                     {
829                         char dotoid_str[100];
830                         oid_to_dotstring(syntax, dotoid_str);
831                         *addinfo = odr_strdup(odr, dotoid_str);
832                     }
833                     return atoi(match_error);
834                 }
835                 return 0;
836             }
837         }
838     }
839 #endif
840     return 0;
841 }
842
843 #if HAVE_XSLT
844 xmlNodePtr Yaz_ProxyConfigP::find_target_db(xmlNodePtr ptr, const char *db)
845 {
846     xmlNodePtr dptr;
847     if (!db)
848         return ptr;
849     if (!ptr)
850         return 0;
851     for (dptr = ptr->children; dptr; dptr = dptr->next)
852         if (dptr->type == XML_ELEMENT_NODE &&
853             !strcmp((const char *) dptr->name, "database"))
854         {
855             struct _xmlAttr *attr;
856             for (attr = dptr->properties; attr; attr = attr->next)
857                 if (!strcmp((const char *) attr->name, "name"))
858                 {
859                     if (attr->children
860                         && attr->children->type==XML_TEXT_NODE
861                         && attr->children->content
862                         && (!strcmp((const char *) attr->children->content, db)
863                             || !strcmp((const char *) attr->children->content,
864                                        "*")))
865                         return dptr;
866                 }
867         }
868     return ptr;
869 }
870
871 xmlNodePtr Yaz_ProxyConfigP::find_target_node(const char *name, const char *db)
872 {
873     xmlNodePtr ptr;
874     if (!m_proxyPtr)
875         return 0;
876     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
877     {
878         if (ptr->type == XML_ELEMENT_NODE &&
879             !strcmp((const char *) ptr->name, "target"))
880         {
881             // default one ?
882             if (!name)
883             {
884                 // <target default="1"> ?
885                 struct _xmlAttr *attr;
886                 for (attr = ptr->properties; attr; attr = attr->next)
887                     if (!strcmp((const char *) attr->name, "default") &&
888                         attr->children && attr->children->type == XML_TEXT_NODE)
889                     {
890                         xmlChar *t = attr->children->content;
891                         if (!t || *t == '1')
892                         {
893                             return find_target_db(ptr, db);
894                         }
895                     }
896             }
897             else
898             {
899                 // <target name="name"> ?
900                 struct _xmlAttr *attr;
901                 for (attr = ptr->properties; attr; attr = attr->next)
902                     if (!strcmp((const char *) attr->name, "name"))
903                     {
904                         if (attr->children
905                             && attr->children->type==XML_TEXT_NODE
906                             && attr->children->content
907                             && (!strcmp((const char *) attr->children->content,
908                                         name)
909                                 || !strcmp((const char *) attr->children->content,
910                                            "*")))
911                         {
912                             return find_target_db(ptr, db);
913                         }
914                     }
915             }
916         }
917     }
918     return 0;
919 }
920 #endif
921
922 int Yaz_ProxyConfig::get_target_no(int no,
923                                    const char **name,
924                                    const char **url,
925                                    int *limit_bw,
926                                    int *limit_pdu,
927                                    int *limit_req,
928                                    int *limit_search,
929                                    int *target_idletime,
930                                    int *client_idletime,
931                                    int *max_clients,
932                                    int *keepalive_limit_bw,
933                                    int *keepalive_limit_pdu,
934                                    int *pre_init,
935                                    const char **cql2rpn,
936                                    const char **authentication,
937                                    const char **negotiation_charset,
938                                    const char **negotiation_lang,
939                                    const char **target_charset,
940                                    const char **default_client_query_charset)
941 {
942 #if HAVE_XSLT
943     xmlNodePtr ptr;
944     if (!m_cp->m_proxyPtr)
945         return 0;
946     int i = 0;
947     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
948         if (ptr->type == XML_ELEMENT_NODE &&
949             !strcmp((const char *) ptr->name, "target"))
950         {
951             if (i == no)
952             {
953                 struct _xmlAttr *attr;
954                 for (attr = ptr->properties; attr; attr = attr->next)
955                     if (!strcmp((const char *) attr->name, "name"))
956                     {
957                         if (attr->children
958                             && attr->children->type==XML_TEXT_NODE
959                             && attr->children->content)
960                             *name = (const char *) attr->children->content;
961                     }
962                 m_cp->return_target_info(
963                     ptr, url,
964                     limit_bw, limit_pdu, limit_req,
965                     limit_search,
966                     target_idletime, client_idletime,
967                     keepalive_limit_bw, keepalive_limit_pdu,
968                     pre_init, cql2rpn,
969                     negotiation_charset, negotiation_lang, target_charset,
970                     default_client_query_charset);
971                 return 1;
972             }
973             i++;
974         }
975 #endif
976     return 0;
977 }
978
979 int Yaz_ProxyConfigP::mycmp(const char *hay, const char *item, size_t len)
980 {
981     if (len == strlen(item) && memcmp(hay, item, len) == 0)
982         return 1;
983     return 0;
984 }
985
986 int Yaz_ProxyConfig::get_file_access_info(const char *path)
987 {
988 #if HAVE_XSLT
989     xmlNodePtr ptr;
990     if (!m_cp->m_proxyPtr)
991         return 0;
992     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
993     {
994         if (ptr->type == XML_ELEMENT_NODE
995             && !strcmp((const char *) ptr->name, "docpath"))
996         {
997             const char *docpath = m_cp->get_text(ptr);
998             size_t docpath_len = strlen(docpath);
999             if (docpath_len < strlen(path) && path[docpath_len] == '/'
1000                 && !memcmp(docpath, path, docpath_len))
1001                 return 1;
1002         }
1003     }
1004 #endif
1005     return 0;
1006 }
1007
1008 void Yaz_ProxyConfig::get_generic_info(int *log_mask,
1009                                        int *max_clients,
1010                                        int *max_connect,
1011                                        int *limit_connect)
1012 {
1013     *max_connect = 0;
1014     *limit_connect = 0;
1015 #if HAVE_XSLT
1016     xmlNodePtr ptr;
1017     if (!m_cp->m_proxyPtr)
1018         return;
1019     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
1020     {
1021         if (ptr->type == XML_ELEMENT_NODE
1022             && !strcmp((const char *) ptr->name, "log"))
1023         {
1024             const char *v = m_cp->get_text(ptr);
1025             *log_mask = 0;
1026             while (v && *v)
1027             {
1028                 const char *cp = v;
1029                 while (*cp && *cp != ',' && !isspace(*cp))
1030                     cp++;
1031                 size_t len = cp - v;
1032                 if (m_cp->mycmp(v, "client-apdu", len))
1033                     *log_mask |= PROXY_LOG_APDU_CLIENT;
1034                 if (m_cp->mycmp(v, "server-apdu", len))
1035                     *log_mask |= PROXY_LOG_APDU_SERVER;
1036                 if (m_cp->mycmp(v, "client-requests", len))
1037                     *log_mask |= PROXY_LOG_REQ_CLIENT;
1038                 if (m_cp->mycmp(v, "server-requests", len))
1039                     *log_mask |= PROXY_LOG_REQ_SERVER;
1040                 if (m_cp->mycmp(v, "client-ip", len))
1041                     *log_mask |= PROXY_LOG_IP_CLIENT;
1042                 if (isdigit(*v))
1043                     *log_mask |= atoi(v);
1044                 if (*cp == ',')
1045                     cp++;
1046                 while (*cp && isspace(*cp))
1047                     cp++;
1048                 v = cp;
1049             }
1050         }
1051         else if (ptr->type == XML_ELEMENT_NODE &&
1052             !strcmp((const char *) ptr->name, "max-clients"))
1053         {
1054             const char *t = m_cp->get_text(ptr);
1055             if (t)
1056             {
1057                 *max_clients = atoi(t);
1058                 if (*max_clients  < 1)
1059                     *max_clients = 1;
1060             }
1061         }
1062         else if (ptr->type == XML_ELEMENT_NODE &&
1063             !strcmp((const char *) ptr->name, "max-connect"))
1064         {
1065             const char *t = m_cp->get_text(ptr);
1066             if (t)
1067                 *max_connect = atoi(t);
1068         }
1069         else if (ptr->type == XML_ELEMENT_NODE &&
1070             !strcmp((const char *) ptr->name, "limit-connect"))
1071         {
1072             const char *t = m_cp->get_text(ptr);
1073             if (t)
1074                 *limit_connect = atoi(t);
1075         }
1076         else if (ptr->type == XML_ELEMENT_NODE &&
1077             !strcmp((const char *) ptr->name, "target"))
1078             ;
1079         else if (ptr->type == XML_ELEMENT_NODE &&
1080             !strcmp((const char *) ptr->name, "docpath"))
1081             ;
1082         else if (ptr->type == XML_ELEMENT_NODE)
1083         {
1084             yaz_log(YLOG_WARN, "0 Unknown element %s in yazproxy config",
1085                     ptr->name);
1086         }
1087     }
1088 #endif
1089 }
1090
1091 #if HAVE_XSLT
1092 int Yaz_ProxyConfigP::get_explain_ptr(const char *host, const char *db,
1093                                       xmlNodePtr *ptr_target,
1094                                       xmlNodePtr *ptr_explain)
1095 {
1096     xmlNodePtr ptr;
1097     if (!m_proxyPtr)
1098         return 0;
1099     if (!db)
1100         return 0;
1101     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
1102     {
1103         if (ptr->type == XML_ELEMENT_NODE &&
1104             !strcmp((const char *) ptr->name, "target"))
1105         {
1106             *ptr_target = ptr;
1107             xmlNodePtr ptr = (*ptr_target)->children;
1108             for (; ptr; ptr = ptr->next)
1109             {
1110                 if (ptr->type == XML_ELEMENT_NODE &&
1111                     !strcmp((const char *) ptr->name, "explain"))
1112                 {
1113                     *ptr_explain = ptr;
1114                     xmlNodePtr ptr = (*ptr_explain)->children;
1115
1116                     for (; ptr; ptr = ptr->next)
1117                         if (ptr->type == XML_ELEMENT_NODE &&
1118                             !strcmp((const char *) ptr->name, "serverInfo"))
1119                             break;
1120                     if (!ptr)
1121                         continue;
1122                     for (ptr = ptr->children; ptr; ptr = ptr->next)
1123                         if (ptr->type == XML_ELEMENT_NODE &&
1124                             !strcmp((const char *) ptr->name, "database"))
1125                             break;
1126
1127                     if (!ptr)
1128                         continue;
1129                     for (ptr = ptr->children; ptr; ptr = ptr->next)
1130                         if (ptr->type == XML_TEXT_NODE &&
1131                             ptr->content &&
1132                             !strcmp((const char *) ptr->content, db))
1133                             break;
1134                     if (!ptr)
1135                         continue;
1136                     return 1;
1137                 }
1138             }
1139         }
1140     }
1141     return 0;
1142 }
1143 #endif
1144
1145 const char *Yaz_ProxyConfig::get_explain_name(const char *db,
1146                                               const char **backend_db)
1147 {
1148 #if HAVE_XSLT
1149     xmlNodePtr ptr_target, ptr_explain;
1150     if (m_cp->get_explain_ptr(0, db, &ptr_target, &ptr_explain)
1151         && ptr_target)
1152     {
1153         struct _xmlAttr *attr;
1154         const char *name = 0;
1155
1156         for (attr = ptr_target->properties; attr; attr = attr->next)
1157             if (!strcmp((const char *) attr->name, "name")
1158                 && attr->children
1159                 && attr->children->type==XML_TEXT_NODE
1160                 && attr->children->content
1161                 && attr->children->content[0])
1162             {
1163                 name = (const char *)attr->children->content;
1164                 break;
1165             }
1166         if (name)
1167         {
1168             for (attr = ptr_target->properties; attr; attr = attr->next)
1169                 if (!strcmp((const char *) attr->name, "database"))
1170                 {
1171                     if (attr->children
1172                         && attr->children->type==XML_TEXT_NODE
1173                         && attr->children->content)
1174                         *backend_db = (const char *) attr->children->content;
1175                 }
1176             return name;
1177         }
1178     }
1179 #endif
1180     return 0;
1181 }
1182
1183 char *Yaz_ProxyConfig::get_explain_doc(ODR odr, const char *name,
1184                                        const char *db, int *len)
1185 {
1186 #if HAVE_XSLT
1187     xmlNodePtr ptr_target, ptr_explain;
1188     if (m_cp->get_explain_ptr(0 /* host */, db, &ptr_target, &ptr_explain))
1189     {
1190         xmlNodePtr ptr2 = xmlCopyNode(ptr_explain, 1);
1191
1192         xmlDocPtr doc = xmlNewDoc((const xmlChar *) "1.0");
1193
1194         xmlDocSetRootElement(doc, ptr2);
1195
1196         xmlChar *buf_out;
1197         xmlDocDumpMemory(doc, &buf_out, len);
1198         char *content = (char*) odr_malloc(odr, *len);
1199         memcpy(content, buf_out, *len);
1200
1201         xmlFree(buf_out);
1202         xmlFreeDoc(doc);
1203         return content;
1204     }
1205 #endif
1206     return 0;
1207 }
1208
1209 void Yaz_ProxyConfig::get_target_info(const char *name,
1210                                       const char **url,
1211                                       int *limit_bw,
1212                                       int *limit_pdu,
1213                                       int *limit_req,
1214                                       int *limit_search,
1215                                       int *target_idletime,
1216                                       int *client_idletime,
1217                                       int *max_clients,
1218                                       int *keepalive_limit_bw,
1219                                       int *keepalive_limit_pdu,
1220                                       int *pre_init,
1221                                       const char **cql2rpn,
1222                                       const char **negotiation_charset,
1223                                       const char **negotiation_lang,
1224                                       const char **target_charset,
1225                                       const char **default_client_query_charset)
1226 {
1227 #if HAVE_XSLT
1228     xmlNodePtr ptr;
1229     if (!m_cp->m_proxyPtr)
1230     {
1231         url[0] = name;
1232         url[1] = 0;
1233         return;
1234     }
1235     url[0] = 0;
1236     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
1237     {
1238         if (ptr->type == XML_ELEMENT_NODE &&
1239             !strcmp((const char *) ptr->name, "max-clients"))
1240         {
1241             const char *t = m_cp->get_text(ptr);
1242             if (t)
1243             {
1244                 *max_clients = atoi(t);
1245                 if (*max_clients  < 1)
1246                     *max_clients = 1;
1247             }
1248         }
1249     }
1250     ptr = m_cp->find_target_node(name, 0);
1251     if (ptr)
1252     {
1253         if (name)
1254         {
1255             url[0] = name;
1256             url[1] = 0;
1257         }
1258         m_cp->return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
1259                                  limit_search,
1260                                  target_idletime, client_idletime,
1261                                  keepalive_limit_bw, keepalive_limit_pdu,
1262                                  pre_init, cql2rpn,
1263                                  negotiation_charset, negotiation_lang,
1264                                  target_charset,
1265                                  default_client_query_charset);
1266     }
1267 #else
1268     *url = name;
1269     return;
1270 #endif
1271 }
1272
1273
1274 /*
1275  * Local variables:
1276  * c-basic-offset: 4
1277  * indent-tabs-mode: nil
1278  * End:
1279  * vim: shiftwidth=4 tabstop=8 expandtab
1280  */
1281