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