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