24fa1b0be785f48b729b5a7571c4839e12ea453a
[yazproxy-moved-to-github.git] / src / yaz-proxy-config.cpp
1 /* $Id: yaz-proxy-config.cpp,v 1.32 2006-06-09 09:35:14 adam Exp $
2    Copyright (c) 1998-2006, Index Data.
3
4 This file is part of the yazproxy.
5
6 YAZ proxy is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with YAZ proxy; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #include <ctype.h>
23
24 #include <yaz/log.h>
25 #include "proxyp.h"
26
27 class Yaz_ProxyConfigP {
28     friend class Yaz_ProxyConfig;
29
30     Yaz_ProxyModules m_modules;
31     int mycmp(const char *hay, const char *item, size_t len);
32     int match_list(int v, const char *m);
33     int atoi_l(const char **cp);
34 #if HAVE_XSLT
35     void load_modules(void);
36     int check_schema(xmlNodePtr ptr, Z_RecordComposition *comp,
37                      const char *schema_identifier);
38     xmlDocPtr m_docPtr;
39     xmlNodePtr m_proxyPtr;
40     void return_target_info(xmlNodePtr ptr, const char **url,
41                             int *limit_bw, int *limit_pdu, int *limit_req,
42                             int *limit_search,
43                             int *target_idletime, int *client_idletime,
44                             int *keepalive_limit_bw, int *keepalive_limit_pdu,
45                             int *pre_init, const char **cql2rpn,
46                             const char **negotiation_charset,
47                             const char **negotiation_lang,
48                             const char **target_charset,
49                             const char **default_client_query_charset);
50     void return_limit(xmlNodePtr ptr,
51                       int *limit_bw, int *limit_pdu, int *limit_req,
52                       int *limit_search);
53     int check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
54                      char **addinfo);
55     xmlNodePtr find_target_node(const char *name, const char *db);
56     xmlNodePtr find_target_db(xmlNodePtr ptr, const char *db);
57     const char *get_text(xmlNodePtr ptr);
58     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 HAVE_XSLT
74     m_docPtr = 0;
75     m_proxyPtr = 0;
76 #endif
77 }
78
79 Yaz_ProxyConfigP::~Yaz_ProxyConfigP()
80 {
81 #if 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 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 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 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 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 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 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 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 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 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 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 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 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::check_syntax(ODR odr, const char *name,
680                                   Odr_oid *syntax, Z_RecordComposition *comp,
681                                   char **addinfo,
682                                   char **stylesheet, char **schema,
683                                   char **backend_type,
684                                   char **backend_charset,
685                                   char **usemarcon_ini_stage1,
686                                   char **usemarcon_ini_stage2
687                                   )
688 {
689     if (stylesheet)
690     {
691         xfree (*stylesheet);
692         *stylesheet = 0;
693     }
694     if (schema)
695     {
696         xfree (*schema);
697         *schema = 0;
698     }
699     if (backend_type)
700     {
701         xfree (*backend_type);
702         *backend_type = 0;
703     }
704     if (backend_charset)
705     {
706         xfree (*backend_charset);
707         *backend_charset = 0;
708     }
709     if (usemarcon_ini_stage1)
710     {
711         xfree (*usemarcon_ini_stage1);
712         *usemarcon_ini_stage1 = 0;
713     }
714     if (usemarcon_ini_stage2)
715     {
716         xfree (*usemarcon_ini_stage2);
717         *usemarcon_ini_stage2 = 0;
718     }
719 #if HAVE_XSLT
720     int syntax_has_matched = 0;
721     xmlNodePtr ptr;
722
723     ptr = m_cp->find_target_node(name, 0);
724     if (!ptr)
725         return 0;
726     for(ptr = ptr->children; ptr; ptr = ptr->next)
727     {
728         if (ptr->type == XML_ELEMENT_NODE &&
729             !strcmp((const char *) ptr->name, "syntax"))
730         {
731             int match = 0;  // if we match record syntax
732             const char *match_type = 0;
733             const char *match_error = 0;
734             const char *match_marcxml = 0;
735             const char *match_stylesheet = 0;
736             const char *match_identifier = 0;
737             const char *match_backend_type = 0;
738             const char *match_backend_charset = 0;
739             const char *match_usemarcon_ini_stage1 = 0;
740             const char *match_usemarcon_ini_stage2 = 0;
741             struct _xmlAttr *attr;
742             for (attr = ptr->properties; attr; attr = attr->next)
743             {
744                 if (!strcmp((const char *) attr->name, "type") &&
745                     attr->children && attr->children->type == XML_TEXT_NODE)
746                     match_type = (const char *) attr->children->content;
747                 if (!strcmp((const char *) attr->name, "error") &&
748                     attr->children && attr->children->type == XML_TEXT_NODE)
749                     match_error = (const char *) attr->children->content;
750                 if (!strcmp((const char *) attr->name, "marcxml") &&
751                     attr->children && attr->children->type == XML_TEXT_NODE)
752                     match_marcxml = (const char *) attr->children->content;
753                 if (!strcmp((const char *) attr->name, "stylesheet") &&
754                     attr->children && attr->children->type == XML_TEXT_NODE)
755                     match_stylesheet = (const char *) attr->children->content;
756                 if (!strcmp((const char *) attr->name, "identifier") &&
757                     attr->children && attr->children->type == XML_TEXT_NODE)
758                     match_identifier = (const char *) attr->children->content;
759                 if (!strcmp((const char *) attr->name, "backendtype") &&
760                     attr->children && attr->children->type == XML_TEXT_NODE)
761                     match_backend_type = (const char *)
762                         attr->children->content;
763                 if (!strcmp((const char *) attr->name, "backendcharset") &&
764                     attr->children && attr->children->type == XML_TEXT_NODE)
765                     match_backend_charset = (const char *)
766                         attr->children->content;
767                 if (!strcmp((const char *) attr->name, "usemarconstage1") &&
768                     attr->children && attr->children->type == XML_TEXT_NODE)
769                     match_usemarcon_ini_stage1 = (const char *)
770                         attr->children->content;
771                 if (!strcmp((const char *) attr->name, "usemarconstage2") &&
772                     attr->children && attr->children->type == XML_TEXT_NODE)
773                     match_usemarcon_ini_stage2 = (const char *)
774                         attr->children->content;
775             }
776             if (match_type)
777             {
778                 if (!strcmp(match_type, "*"))
779                     match = 1;
780                 else if (!strcmp(match_type, "none"))
781                 {
782                     if (syntax == 0)
783                         match = 1;
784                 }
785                 else if (syntax)
786                 {
787                     int match_oid[OID_SIZE];
788                     oid_name_to_oid(CLASS_RECSYN, match_type, match_oid);
789                     if (oid_oidcmp(match_oid, syntax) == 0)
790                         match = 1;
791                 }
792             }
793             if (match)
794             {
795                 if (!match_error)
796                     syntax_has_matched = 1;
797                 match = m_cp->check_schema(ptr->children, comp,
798                                            match_identifier);
799             }
800             if (match)
801             {
802                 if (stylesheet && match_stylesheet)
803                 {
804                     xfree(*stylesheet);
805                     *stylesheet = xstrdup(match_stylesheet);
806                 }
807                 if (schema && match_identifier)
808                 {
809                     xfree(*schema);
810                     *schema = xstrdup(match_identifier);
811                 }
812                 if (backend_type && match_backend_type)
813                 {
814                     xfree(*backend_type);
815                     *backend_type = xstrdup(match_backend_type);
816                 }
817                 if (backend_charset && match_backend_charset)
818                 {
819                     xfree(*backend_charset);
820                     *backend_charset = xstrdup(match_backend_charset);
821                 }
822                 if (usemarcon_ini_stage1 && match_usemarcon_ini_stage1)
823                 {
824                     xfree(*usemarcon_ini_stage1);
825                     *usemarcon_ini_stage1 = xstrdup(match_usemarcon_ini_stage1);
826                 }
827                 if (usemarcon_ini_stage1 && match_usemarcon_ini_stage2)
828                 {
829                     xfree(*usemarcon_ini_stage2);
830                     *usemarcon_ini_stage2 = xstrdup(match_usemarcon_ini_stage2);
831                 }
832                 if (match_marcxml)
833                 {
834                     return -1;
835                 }
836                 if (match_error)
837                 {
838                     if (syntax_has_matched)  // if syntax OK, bad schema/ESN
839                         return 25;
840                     if (syntax)
841                     {
842                         char dotoid_str[100];
843                         oid_to_dotstring(syntax, dotoid_str);
844                         *addinfo = odr_strdup(odr, dotoid_str);
845                     }
846                     return atoi(match_error);
847                 }
848                 return 0;
849             }
850         }
851     }
852 #endif
853     return 0;
854 }
855
856 #if HAVE_XSLT
857 xmlNodePtr Yaz_ProxyConfigP::find_target_db(xmlNodePtr ptr, const char *db)
858 {
859     xmlNodePtr dptr;
860     if (!db)
861         return ptr;
862     if (!ptr)
863         return 0;
864     for (dptr = ptr->children; dptr; dptr = dptr->next)
865         if (dptr->type == XML_ELEMENT_NODE &&
866             !strcmp((const char *) dptr->name, "database"))
867         {
868             struct _xmlAttr *attr;
869             for (attr = dptr->properties; attr; attr = attr->next)
870                 if (!strcmp((const char *) attr->name, "name"))
871                 {
872                     if (attr->children
873                         && attr->children->type==XML_TEXT_NODE
874                         && attr->children->content
875                         && (!strcmp((const char *) attr->children->content, db)
876                             || !strcmp((const char *) attr->children->content,
877                                        "*")))
878                         return dptr;
879                 }
880         }
881     return ptr;
882 }
883
884 xmlNodePtr Yaz_ProxyConfigP::find_target_node(const char *name, const char *db)
885 {
886     xmlNodePtr ptr;
887     if (!m_proxyPtr)
888         return 0;
889     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
890     {
891         if (ptr->type == XML_ELEMENT_NODE &&
892             !strcmp((const char *) ptr->name, "target"))
893         {
894             // default one ?
895             if (!name)
896             {
897                 // <target default="1"> ?
898                 struct _xmlAttr *attr;
899                 for (attr = ptr->properties; attr; attr = attr->next)
900                     if (!strcmp((const char *) attr->name, "default") &&
901                         attr->children && attr->children->type == XML_TEXT_NODE)
902                     {
903                         xmlChar *t = attr->children->content;
904                         if (!t || *t == '1')
905                         {
906                             return find_target_db(ptr, db);
907                         }
908                     }
909             }
910             else
911             {
912                 // <target name="name"> ?
913                 struct _xmlAttr *attr;
914                 for (attr = ptr->properties; attr; attr = attr->next)
915                     if (!strcmp((const char *) attr->name, "name"))
916                     {
917                         if (attr->children
918                             && attr->children->type==XML_TEXT_NODE
919                             && attr->children->content
920                             && (!strcmp((const char *) attr->children->content,
921                                         name)
922                                 || !strcmp((const char *) attr->children->content,
923                                            "*")))
924                         {
925                             return find_target_db(ptr, db);
926                         }
927                     }
928             }
929         }
930     }
931     return 0;
932 }
933 #endif
934
935 int Yaz_ProxyConfig::get_target_no(int no,
936                                    const char **name,
937                                    const char **url,
938                                    int *limit_bw,
939                                    int *limit_pdu,
940                                    int *limit_req,
941                                    int *limit_search,
942                                    int *target_idletime,
943                                    int *client_idletime,
944                                    int *max_clients,
945                                    int *keepalive_limit_bw,
946                                    int *keepalive_limit_pdu,
947                                    int *pre_init,
948                                    const char **cql2rpn,
949                                    const char **authentication,
950                                    const char **negotiation_charset,
951                                    const char **negotiation_lang,
952                                    const char **target_charset,
953                                    const char **default_client_query_charset)
954 {
955 #if HAVE_XSLT
956     xmlNodePtr ptr;
957     if (!m_cp->m_proxyPtr)
958         return 0;
959     int i = 0;
960     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
961         if (ptr->type == XML_ELEMENT_NODE &&
962             !strcmp((const char *) ptr->name, "target"))
963         {
964             if (i == no)
965             {
966                 struct _xmlAttr *attr;
967                 for (attr = ptr->properties; attr; attr = attr->next)
968                     if (!strcmp((const char *) attr->name, "name"))
969                     {
970                         if (attr->children
971                             && attr->children->type==XML_TEXT_NODE
972                             && attr->children->content)
973                             *name = (const char *) attr->children->content;
974                     }
975                 m_cp->return_target_info(
976                     ptr, url,
977                     limit_bw, limit_pdu, limit_req,
978                     limit_search,
979                     target_idletime, client_idletime,
980                     keepalive_limit_bw, keepalive_limit_pdu,
981                     pre_init, cql2rpn,
982                     negotiation_charset, negotiation_lang, target_charset,
983                     default_client_query_charset);
984                 return 1;
985             }
986             i++;
987         }
988 #endif
989     return 0;
990 }
991
992 int Yaz_ProxyConfigP::mycmp(const char *hay, const char *item, size_t len)
993 {
994     if (len == strlen(item) && memcmp(hay, item, len) == 0)
995         return 1;
996     return 0;
997 }
998
999 int Yaz_ProxyConfig::get_file_access_info(const char *path)
1000 {
1001 #if HAVE_XSLT
1002     xmlNodePtr ptr;
1003     if (!m_cp->m_proxyPtr)
1004         return 0;
1005     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
1006     {
1007         if (ptr->type == XML_ELEMENT_NODE
1008             && !strcmp((const char *) ptr->name, "docpath"))
1009         {
1010             const char *docpath = m_cp->get_text(ptr);
1011             size_t docpath_len = strlen(docpath);
1012             if (docpath_len < strlen(path) && path[docpath_len] == '/'
1013                 && !memcmp(docpath, path, docpath_len))
1014                 return 1;
1015         }
1016     }
1017 #endif
1018     return 0;
1019 }
1020
1021 void Yaz_ProxyConfig::get_generic_info(int *log_mask,
1022                                        int *max_clients,
1023                                        int *max_connect,
1024                                        int *limit_connect,
1025                                        int *period_connect,
1026                                        int *num_msg_threads)
1027 {
1028     *max_connect = 0;
1029     *limit_connect = 0;
1030     *num_msg_threads = 0;
1031 #if HAVE_XSLT
1032     xmlNodePtr ptr;
1033     if (!m_cp->m_proxyPtr)
1034         return;
1035     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
1036     {
1037         if (ptr->type == XML_ELEMENT_NODE
1038             && !strcmp((const char *) ptr->name, "log"))
1039         {
1040             const char *v = m_cp->get_text(ptr);
1041             *log_mask = 0;
1042             while (v && *v)
1043             {
1044                 const char *cp = v;
1045                 while (*cp && *cp != ',' && !isspace(*cp))
1046                     cp++;
1047                 size_t len = cp - v;
1048                 if (m_cp->mycmp(v, "client-apdu", len))
1049                     *log_mask |= PROXY_LOG_APDU_CLIENT;
1050                 if (m_cp->mycmp(v, "server-apdu", len))
1051                     *log_mask |= PROXY_LOG_APDU_SERVER;
1052                 if (m_cp->mycmp(v, "client-requests", len))
1053                     *log_mask |= PROXY_LOG_REQ_CLIENT;
1054                 if (m_cp->mycmp(v, "server-requests", len))
1055                     *log_mask |= PROXY_LOG_REQ_SERVER;
1056                 if (m_cp->mycmp(v, "client-ip", len))
1057                     *log_mask |= PROXY_LOG_IP_CLIENT;
1058                 if (isdigit(*v))
1059                     *log_mask |= atoi(v);
1060                 if (*cp == ',')
1061                     cp++;
1062                 while (*cp && isspace(*cp))
1063                     cp++;
1064                 v = cp;
1065             }
1066         }
1067         else if (ptr->type == XML_ELEMENT_NODE &&
1068             !strcmp((const char *) ptr->name, "max-clients"))
1069         {
1070             const char *t = m_cp->get_text(ptr);
1071             if (t)
1072             {
1073                 *max_clients = atoi(t);
1074                 if (*max_clients  < 1)
1075                     *max_clients = 1;
1076             }
1077         }
1078         else if (ptr->type == XML_ELEMENT_NODE &&
1079             !strcmp((const char *) ptr->name, "period-connect"))
1080         {
1081             const char *t = m_cp->get_text(ptr);
1082             if (t)
1083                 *period_connect = atoi(t);
1084         }
1085         else if (ptr->type == XML_ELEMENT_NODE &&
1086             !strcmp((const char *) ptr->name, "max-connect"))
1087         {
1088             const char *t = m_cp->get_text(ptr);
1089             if (t)
1090             {
1091                 *max_connect = atoi(t);
1092             }
1093         }
1094         else if (ptr->type == XML_ELEMENT_NODE &&
1095             !strcmp((const char *) ptr->name, "limit-connect"))
1096         {
1097             const char *t = m_cp->get_text(ptr);
1098             if (t)
1099             {
1100                 *limit_connect = atoi(t);
1101             }
1102         }
1103         else if (ptr->type == XML_ELEMENT_NODE &&
1104             !strcmp((const char *) ptr->name, "target"))
1105             ;
1106         else if (ptr->type == XML_ELEMENT_NODE &&
1107             !strcmp((const char *) ptr->name, "docpath"))
1108             ;
1109         else if (ptr->type == XML_ELEMENT_NODE &&
1110             !strcmp((const char *) ptr->name, "module"))
1111             ;
1112         else if (ptr->type == XML_ELEMENT_NODE &&
1113             !strcmp((const char *) ptr->name, "threads"))
1114         {
1115             const char *t = m_cp->get_text(ptr);
1116             if (t)
1117             {
1118                 *num_msg_threads = atoi(t);
1119             }
1120         }
1121         else if (ptr->type == XML_ELEMENT_NODE)
1122         {
1123             yaz_log(YLOG_WARN, "0 Unknown element %s in yazproxy config",
1124                     ptr->name);
1125         }
1126     }
1127 #endif
1128 }
1129
1130 #if HAVE_XSLT
1131 int Yaz_ProxyConfigP::get_explain_ptr(const char *host, const char *db,
1132                                       xmlNodePtr *ptr_target,
1133                                       xmlNodePtr *ptr_explain)
1134 {
1135     xmlNodePtr ptr;
1136     if (!m_proxyPtr)
1137         return 0;
1138     if (!db)
1139         return 0;
1140     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
1141     {
1142         if (ptr->type == XML_ELEMENT_NODE &&
1143             !strcmp((const char *) ptr->name, "target"))
1144         {
1145             *ptr_target = ptr;
1146             xmlNodePtr ptr = (*ptr_target)->children;
1147             for (; ptr; ptr = ptr->next)
1148             {
1149                 if (ptr->type == XML_ELEMENT_NODE &&
1150                     !strcmp((const char *) ptr->name, "explain"))
1151                 {
1152                     *ptr_explain = ptr;
1153                     xmlNodePtr ptr = (*ptr_explain)->children;
1154
1155                     for (; ptr; ptr = ptr->next)
1156                         if (ptr->type == XML_ELEMENT_NODE &&
1157                             !strcmp((const char *) ptr->name, "serverInfo"))
1158                             break;
1159                     if (!ptr)
1160                         continue;
1161                     for (ptr = ptr->children; ptr; ptr = ptr->next)
1162                         if (ptr->type == XML_ELEMENT_NODE &&
1163                             !strcmp((const char *) ptr->name, "database"))
1164                             break;
1165
1166                     if (!ptr)
1167                         continue;
1168                     for (ptr = ptr->children; ptr; ptr = ptr->next)
1169                         if (ptr->type == XML_TEXT_NODE &&
1170                             ptr->content &&
1171                             !strcmp((const char *) ptr->content, db))
1172                             break;
1173                     if (!ptr)
1174                         continue;
1175                     return 1;
1176                 }
1177             }
1178         }
1179     }
1180     return 0;
1181 }
1182 #endif
1183
1184 const char *Yaz_ProxyConfig::get_explain_name(const char *db,
1185                                               const char **backend_db)
1186 {
1187 #if HAVE_XSLT
1188     xmlNodePtr ptr_target, ptr_explain;
1189     if (m_cp->get_explain_ptr(0, db, &ptr_target, &ptr_explain)
1190         && ptr_target)
1191     {
1192         struct _xmlAttr *attr;
1193         const char *name = 0;
1194
1195         for (attr = ptr_target->properties; attr; attr = attr->next)
1196             if (!strcmp((const char *) attr->name, "name")
1197                 && attr->children
1198                 && attr->children->type==XML_TEXT_NODE
1199                 && attr->children->content
1200                 && attr->children->content[0])
1201             {
1202                 name = (const char *)attr->children->content;
1203                 break;
1204             }
1205         if (name)
1206         {
1207             for (attr = ptr_target->properties; attr; attr = attr->next)
1208                 if (!strcmp((const char *) attr->name, "database"))
1209                 {
1210                     if (attr->children
1211                         && attr->children->type==XML_TEXT_NODE
1212                         && attr->children->content)
1213                         *backend_db = (const char *) attr->children->content;
1214                 }
1215             return name;
1216         }
1217     }
1218 #endif
1219     return 0;
1220 }
1221
1222 char *Yaz_ProxyConfig::get_explain_doc(ODR odr, const char *name,
1223                                        const char *db, int *len)
1224 {
1225 #if HAVE_XSLT
1226     xmlNodePtr ptr_target, ptr_explain;
1227     if (m_cp->get_explain_ptr(0 /* host */, db, &ptr_target, &ptr_explain))
1228     {
1229         xmlNodePtr ptr2 = xmlCopyNode(ptr_explain, 1);
1230
1231         xmlDocPtr doc = xmlNewDoc((const xmlChar *) "1.0");
1232
1233         xmlDocSetRootElement(doc, ptr2);
1234
1235         xmlChar *buf_out;
1236         xmlDocDumpMemory(doc, &buf_out, len);
1237         char *content = (char*) odr_malloc(odr, *len);
1238         memcpy(content, buf_out, *len);
1239
1240         xmlFree(buf_out);
1241         xmlFreeDoc(doc);
1242         return content;
1243     }
1244 #endif
1245     return 0;
1246 }
1247
1248 void Yaz_ProxyConfig::get_target_info(const char *name,
1249                                       const char **url,
1250                                       int *limit_bw,
1251                                       int *limit_pdu,
1252                                       int *limit_req,
1253                                       int *limit_search,
1254                                       int *target_idletime,
1255                                       int *client_idletime,
1256                                       int *max_clients,
1257                                       int *keepalive_limit_bw,
1258                                       int *keepalive_limit_pdu,
1259                                       int *pre_init,
1260                                       const char **cql2rpn,
1261                                       const char **negotiation_charset,
1262                                       const char **negotiation_lang,
1263                                       const char **target_charset,
1264                                       const char **default_client_query_charset)
1265 {
1266 #if HAVE_XSLT
1267     xmlNodePtr ptr;
1268     if (!m_cp->m_proxyPtr)
1269     {
1270         url[0] = name;
1271         url[1] = 0;
1272         return;
1273     }
1274     url[0] = 0;
1275     for (ptr = m_cp->m_proxyPtr->children; ptr; ptr = ptr->next)
1276     {
1277         if (ptr->type == XML_ELEMENT_NODE &&
1278             !strcmp((const char *) ptr->name, "max-clients"))
1279         {
1280             const char *t = m_cp->get_text(ptr);
1281             if (t)
1282             {
1283                 *max_clients = atoi(t);
1284                 if (*max_clients  < 1)
1285                     *max_clients = 1;
1286             }
1287         }
1288     }
1289     ptr = m_cp->find_target_node(name, 0);
1290     if (ptr)
1291     {
1292         if (name)
1293         {
1294             url[0] = name;
1295             url[1] = 0;
1296         }
1297         m_cp->return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
1298                                  limit_search,
1299                                  target_idletime, client_idletime,
1300                                  keepalive_limit_bw, keepalive_limit_pdu,
1301                                  pre_init, cql2rpn,
1302                                  negotiation_charset, negotiation_lang,
1303                                  target_charset,
1304                                  default_client_query_charset);
1305     }
1306 #else
1307     *url = name;
1308     return;
1309 #endif
1310 }
1311
1312
1313 /*
1314  * Local variables:
1315  * c-basic-offset: 4
1316  * indent-tabs-mode: nil
1317  * End:
1318  * vim: shiftwidth=4 tabstop=8 expandtab
1319  */
1320