Better reporting of statistics. Handling of ODR decode improved
[yazpp-moved-to-github.git] / src / yaz-proxy-config.cpp
1 /*
2  * Copyright (c) 1998-2003, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Id: yaz-proxy-config.cpp,v 1.12 2003-10-20 18:31:44 adam Exp $
6  */
7
8 #include <ctype.h>
9 #include <yaz/log.h>
10 #include <yaz++/proxy.h>
11
12 Yaz_ProxyConfig::Yaz_ProxyConfig()
13 {
14     m_copy = 0;
15 #if HAVE_XML2
16     m_docPtr = 0;
17     m_proxyPtr = 0;
18 #endif
19 }
20
21 Yaz_ProxyConfig::~Yaz_ProxyConfig()
22 {
23 #if HAVE_XML2
24     if (!m_copy && m_docPtr)
25         xmlFreeDoc(m_docPtr);
26 #endif
27 }
28
29 int Yaz_ProxyConfig::read_xml(const char *fname)
30 {
31 #if HAVE_XML2
32     xmlDocPtr ndoc = xmlParseFile(fname);
33
34     if (!ndoc)
35     {
36         yaz_log(LOG_WARN, "Config file %s not found or parse error", fname);
37         return -1;  // no good
38     }
39     xmlNodePtr proxyPtr = xmlDocGetRootElement(ndoc);
40     if (!proxyPtr || proxyPtr->type != XML_ELEMENT_NODE ||
41         strcmp((const char *) proxyPtr->name, "proxy"))
42     {
43         yaz_log(LOG_WARN, "No proxy element in %s", fname);
44         xmlFreeDoc(ndoc);
45         return -1;
46     }
47     m_proxyPtr = proxyPtr;
48
49     // OK: release previous and make it the current one.
50     if (m_docPtr)
51         xmlFreeDoc(m_docPtr);
52     m_docPtr = ndoc;
53     return 0;
54 #else
55     return -2;
56 #endif
57 }
58
59 #if HAVE_XML2
60 const char *Yaz_ProxyConfig::get_text(xmlNodePtr ptr)
61 {
62     for(ptr = ptr->children; ptr; ptr = ptr->next)
63         if (ptr->type == XML_TEXT_NODE)
64         {
65             xmlChar *t = ptr->content;
66             if (t)
67             {
68                 while (*t == ' ')
69                     t++;
70                 return (const char *) t;
71             }
72         }
73     return 0;
74 }
75 #endif
76
77 #if HAVE_XML2
78 void Yaz_ProxyConfig::return_limit(xmlNodePtr ptr,
79                                    int *limit_bw,
80                                    int *limit_pdu,
81                                    int *limit_req)
82 {
83     for (ptr = ptr->children; ptr; ptr = ptr->next)
84     {
85         if (ptr->type == XML_ELEMENT_NODE 
86             && !strcmp((const char *) ptr->name, "bandwidth"))
87         {
88             const char *t = get_text(ptr);
89             if (t)
90                 *limit_bw = atoi(t);
91         }
92         if (ptr->type == XML_ELEMENT_NODE 
93             && !strcmp((const char *) ptr->name, "retrieve"))
94         {
95             const char *t = get_text(ptr);
96             if (t)
97                 *limit_req = atoi(t);
98         }
99         if (ptr->type == XML_ELEMENT_NODE 
100             && !strcmp((const char *) ptr->name, "pdu"))
101         {
102             const char *t = get_text(ptr);
103             if (t)
104                 *limit_pdu = atoi(t);
105         }
106     }
107 }
108 #endif
109
110 #if HAVE_XML2
111 void Yaz_ProxyConfig::return_target_info(xmlNodePtr ptr,
112                                          const char **url,
113                                          int *limit_bw,
114                                          int *limit_pdu,
115                                          int *limit_req,
116                                          int *target_idletime,
117                                          int *client_idletime,
118                                          int *keepalive_limit_bw,
119                                          int *keepalive_limit_pdu,
120                                          int *pre_init)
121 {
122     *pre_init = 0;
123     int no_url = 0;
124     ptr = ptr->children;
125     for (; ptr; ptr = ptr->next)
126     {
127         if (ptr->type == XML_ELEMENT_NODE 
128             && !strcmp((const char *) ptr->name, "preinit"))
129         {
130             const char *v = get_text(ptr);
131             *pre_init = v ? atoi(v) : 1;
132         }
133         if (ptr->type == XML_ELEMENT_NODE 
134             && !strcmp((const char *) ptr->name, "url"))
135         {
136             const char *t = get_text(ptr);
137             if (t && no_url < MAX_ZURL_PLEX)
138             {
139                 url[no_url++] = t;
140                 url[no_url] = 0;
141             }
142         }
143         if (ptr->type == XML_ELEMENT_NODE 
144             && !strcmp((const char *) ptr->name, "keepalive"))
145         {
146             int dummy;
147             *keepalive_limit_bw = 500000;
148             *keepalive_limit_pdu = 1000;
149             return_limit(ptr, keepalive_limit_bw, keepalive_limit_pdu,
150                          &dummy);
151         }
152         if (ptr->type == XML_ELEMENT_NODE 
153             && !strcmp((const char *) ptr->name, "limit"))
154             return_limit(ptr, limit_bw, limit_pdu, limit_req);
155         if (ptr->type == XML_ELEMENT_NODE 
156             && !strcmp((const char *) ptr->name, "target-timeout"))
157         {
158             const char *t = get_text(ptr);
159             if (t)
160             {
161                 *target_idletime = atoi(t);
162                 if (*target_idletime < 0)
163                     *target_idletime = 0;
164             }
165         }
166         if (ptr->type == XML_ELEMENT_NODE 
167             && !strcmp((const char *) ptr->name, "client-timeout"))
168         {
169             const char *t = get_text(ptr);
170             if (t)
171             {
172                 *client_idletime = atoi(t);
173                 if (*client_idletime < 0)
174                     *client_idletime = 0;
175             }
176         }
177     }
178 }
179 #endif
180
181 int Yaz_ProxyConfig::atoi_l(const char **cp)
182 {
183     int v = 0;
184     while (**cp && isdigit(**cp))
185     {
186         v = v*10 + (**cp - '0');
187         (*cp)++;
188     }
189     return v;
190 }
191
192 int Yaz_ProxyConfig::match_list(int v, const char *m)
193 {
194   while(m && *m)
195   {
196       while(*m && isspace(*m))
197           m++;
198       if (*m == '*')
199           return 1;
200       int l = atoi_l(&m);
201       int h = l;
202       if (*m == '-')
203       {
204           ++m;
205           h = atoi_l(&m);
206       }
207       if (v >= l && v <= h)
208           return 1;
209       if (*m == ',')
210           m++;
211   }
212   return 0;
213 }
214
215 #if HAVE_XML2
216 int Yaz_ProxyConfig::check_type_1_attributes(ODR odr, xmlNodePtr ptr,
217                                              Z_AttributeList *attrs,
218                                              char **addinfo)
219 {
220     for(ptr = ptr->children; ptr; ptr = ptr->next)
221     {
222         if (ptr->type == XML_ELEMENT_NODE &&
223             !strcmp((const char *) ptr->name, "attribute"))
224         {
225             const char *match_type = 0;
226             const char *match_value = 0;
227             const char *match_error = 0;
228             struct _xmlAttr *attr;
229             for (attr = ptr->properties; attr; attr = attr->next)
230             {
231                 if (!strcmp((const char *) attr->name, "type") &&
232                     attr->children && attr->children->type == XML_TEXT_NODE)
233                     match_type = (const char *) attr->children->content;
234                 if (!strcmp((const char *) attr->name, "value") &&
235                     attr->children && attr->children->type == XML_TEXT_NODE)
236                     match_value = (const char *) attr->children->content;
237                 if (!strcmp((const char *) attr->name, "error") &&
238                     attr->children && attr->children->type == XML_TEXT_NODE)
239                     match_error = (const char *) attr->children->content;
240             }
241             int i;
242
243             if (match_type && match_value)
244             {
245                 for (i = 0; i<attrs->num_attributes; i++)
246                 {
247                     Z_AttributeElement *el = attrs->attributes[i];
248                     char value_str[20];
249                     
250                     value_str[0] = '\0';
251                     if (!el->attributeType)
252                         continue;
253                     int type = *el->attributeType;
254
255                     if (!match_list(type, match_type))
256                         continue;
257                     if (el->which == Z_AttributeValue_numeric && 
258                         el->value.numeric)
259                     {
260                         if (!match_list(*el->value.numeric, match_value))
261                             continue;
262                         sprintf (value_str, "%d", *el->value.numeric);
263                     }
264                     else
265                         continue;
266                     if (match_error)
267                     {
268                         if (*value_str)
269                             *addinfo = odr_strdup(odr, value_str);
270                         return atoi(match_error);
271                     }
272                     return 0;
273                 }
274             }
275         }
276     }
277     return 0;
278 }
279 #endif
280
281 #if HAVE_XML2
282 int Yaz_ProxyConfig::check_type_1_structure(ODR odr, xmlNodePtr ptr,
283                                             Z_RPNStructure *q,
284                                             char **addinfo)
285 {
286     if (q->which == Z_RPNStructure_complex)
287     {
288         int e = check_type_1_structure(odr, ptr, q->u.complex->s1, addinfo);
289         if (e)
290             return e;
291         e = check_type_1_structure(odr, ptr, q->u.complex->s2, addinfo);
292         return e;
293     }
294     else if (q->which == Z_RPNStructure_simple)
295     {
296         if (q->u.simple->which == Z_Operand_APT)
297         {
298             return check_type_1_attributes(
299                 odr, ptr, q->u.simple->u.attributesPlusTerm->attributes,
300                 addinfo);
301         }
302     }
303     return 0;
304 }
305 #endif
306
307 #if HAVE_XML2
308 int Yaz_ProxyConfig::check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
309                                   char **addinfo)
310 {
311     // possibly check for Bib-1
312     return check_type_1_structure(odr, ptr, query->RPNStructure, addinfo);
313 }
314 #endif
315
316 int Yaz_ProxyConfig::check_query(ODR odr, const char *name, Z_Query *query,
317                                  char **addinfo)
318 {
319 #if HAVE_XML2
320     xmlNodePtr ptr;
321     
322     ptr = find_target_node(name);
323     if (ptr)
324     {
325         if (query->which == Z_Query_type_1 || query->which == Z_Query_type_101)
326             return check_type_1(odr, ptr, query->u.type_1, addinfo);
327     }
328 #endif
329     return 0;
330 }
331
332 int Yaz_ProxyConfig::check_syntax(ODR odr, const char *name,
333                                   Odr_oid *syntax, char **addinfo)
334 {
335 #if HAVE_XML2
336     xmlNodePtr ptr;
337     
338     ptr = find_target_node(name);
339     if (!ptr)
340         return 0;
341     for(ptr = ptr->children; ptr; ptr = ptr->next)
342     {
343         if (ptr->type == XML_ELEMENT_NODE &&
344             !strcmp((const char *) ptr->name, "syntax"))
345         {
346             int match = 0;  // if we match record syntax
347             const char *match_type = 0;
348             const char *match_error = 0;
349             const char *match_marcxml = 0;
350             struct _xmlAttr *attr;
351             for (attr = ptr->properties; attr; attr = attr->next)
352             {
353                 if (!strcmp((const char *) attr->name, "type") &&
354                     attr->children && attr->children->type == XML_TEXT_NODE)
355                     match_type = (const char *) attr->children->content;
356                 if (!strcmp((const char *) attr->name, "error") &&
357                     attr->children && attr->children->type == XML_TEXT_NODE)
358                     match_error = (const char *) attr->children->content;
359                 if (!strcmp((const char *) attr->name, "marcxml") &&
360                     attr->children && attr->children->type == XML_TEXT_NODE)
361                     match_marcxml = (const char *) attr->children->content;
362             }
363             if (match_type)
364             {
365                 if (!strcmp(match_type, "*"))
366                     match = 1;
367                 else if (!strcmp(match_type, "none"))
368                 {
369                     if (syntax == 0)
370                         match = 1;
371                 }
372                 else if (syntax)
373                 {
374                     int match_oid[OID_SIZE];
375                     oid_name_to_oid(CLASS_RECSYN, match_type, match_oid);
376                     if (oid_oidcmp(match_oid, syntax) == 0)
377                         match = 1;
378                 }
379             }
380             if (match)
381             {
382                 if (match_marcxml)
383                 {
384                     return -1;
385                 }
386                 if (match_error)
387                 {
388                     if (syntax)
389                     {
390                         char dotoid_str[100];
391                         oid_to_dotstring(syntax, dotoid_str);
392                         *addinfo = odr_strdup(odr, dotoid_str);
393                     }
394                     return atoi(match_error);
395                 }
396                 return 0;
397             }
398         }
399     }
400 #endif
401     return 0;
402 }
403
404 #if HAVE_XML2
405 xmlNodePtr Yaz_ProxyConfig::find_target_node(const char *name)
406 {
407     xmlNodePtr ptr;
408     if (!m_proxyPtr)
409         return 0;
410     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
411     {
412         if (ptr->type == XML_ELEMENT_NODE &&
413             !strcmp((const char *) ptr->name, "target"))
414         {
415             // default one ? 
416             if (!name)
417             {
418                 // <target default="1"> ?
419                 struct _xmlAttr *attr;
420                 for (attr = ptr->properties; attr; attr = attr->next)
421                     if (!strcmp((const char *) attr->name, "default") &&
422                         attr->children && attr->children->type == XML_TEXT_NODE)
423                     {
424                         xmlChar *t = attr->children->content;
425                         if (!t || *t == '1')
426                             return ptr;
427                     }
428             }
429             else
430             {
431                 // <target name="name"> ?
432                 struct _xmlAttr *attr;
433                 for (attr = ptr->properties; attr; attr = attr->next)
434                     if (!strcmp((const char *) attr->name, "name"))
435                     {
436                         if (attr->children
437                             && attr->children->type==XML_TEXT_NODE
438                             && attr->children->content 
439                             && (!strcmp((const char *) attr->children->content,
440                                         name)
441                                 || !strcmp((const char *) attr->children->content,
442                                            "*")))
443                         {
444                             return ptr;
445                         }
446                     }
447             }
448         }
449     }
450     return 0;
451 }
452 #endif
453
454 int Yaz_ProxyConfig::get_target_no(int no,
455                                    const char **name,
456                                    const char **url,
457                                    int *limit_bw,
458                                    int *limit_pdu,
459                                    int *limit_req,
460                                    int *target_idletime,
461                                    int *client_idletime,
462                                    int *max_clients,
463                                    int *keepalive_limit_bw,
464                                    int *keepalive_limit_pdu,
465                                    int *pre_init)
466 {
467 #if HAVE_XML2
468     xmlNodePtr ptr;
469     if (!m_proxyPtr)
470         return 0;
471     int i = 0;
472     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
473         if (ptr->type == XML_ELEMENT_NODE &&
474             !strcmp((const char *) ptr->name, "target"))
475         {
476             if (i == no)
477             {
478                 struct _xmlAttr *attr;
479                 for (attr = ptr->properties; attr; attr = attr->next)
480                     if (!strcmp((const char *) attr->name, "name"))
481                     {
482                         if (attr->children
483                             && attr->children->type==XML_TEXT_NODE
484                             && attr->children->content)
485                             *name = (const char *) attr->children->content;
486                     }
487                 return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
488                                    target_idletime, client_idletime,
489                                    keepalive_limit_bw, keepalive_limit_pdu,
490                                    pre_init);
491                 return 1;
492             }
493             i++;
494         }
495 #endif
496     return 0;
497 }
498
499 int Yaz_ProxyConfig::mycmp(const char *hay, const char *item, size_t len)
500 {
501     if (len == strlen(item) && memcmp(hay, item, len) == 0)
502         return 1;
503     return 0;
504 }
505
506 void Yaz_ProxyConfig::get_generic_info(int *log_mask,
507                                        int *max_clients)
508 {
509 #if HAVE_XML2
510     xmlNodePtr ptr;
511     if (!m_proxyPtr)
512         return;
513     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
514     {
515         if (ptr->type == XML_ELEMENT_NODE 
516             && !strcmp((const char *) ptr->name, "log"))
517         {
518             const char *v = get_text(ptr);
519             *log_mask = 0;
520             while (v && *v)
521             {
522                 const char *cp = v;
523                 while (*cp && *cp != ',' && !isspace(*cp))
524                     cp++;
525                 size_t len = cp - v;
526                 if (mycmp(v, "client-apdu", len))
527                     *log_mask |= PROXY_LOG_APDU_CLIENT;
528                 if (mycmp(v, "server-apdu", len))
529                     *log_mask |= PROXY_LOG_APDU_SERVER;
530                 if (mycmp(v, "client-requests", len))
531                     *log_mask |= PROXY_LOG_REQ_CLIENT;
532                 if (mycmp(v, "server-requests", len))
533                     *log_mask |= PROXY_LOG_REQ_SERVER;
534                 if (isdigit(*v))
535                     *log_mask |= atoi(v);
536                 if (*cp == ',')
537                     cp++;
538                 while (*cp && isspace(*cp))
539                     cp++;
540                 v = cp;
541             }
542         }
543         if (ptr->type == XML_ELEMENT_NODE &&
544             !strcmp((const char *) ptr->name, "max-clients"))
545         {
546             const char *t = get_text(ptr);
547             if (t)
548             {
549                 *max_clients = atoi(t);
550                 if (*max_clients  < 1)
551                     *max_clients = 1;
552             }
553         }
554     }
555 #endif
556 }
557
558 void Yaz_ProxyConfig::get_target_info(const char *name,
559                                       const char **url,
560                                       int *limit_bw,
561                                       int *limit_pdu,
562                                       int *limit_req,
563                                       int *target_idletime,
564                                       int *client_idletime,
565                                       int *max_clients,
566                                       int *keepalive_limit_bw,
567                                       int *keepalive_limit_pdu,
568                                       int *pre_init)
569 {
570 #if HAVE_XML2
571     xmlNodePtr ptr;
572     if (!m_proxyPtr)
573     {
574         url[0] = name;
575         url[1] = 0;
576         return;
577     }
578     url[0] = 0;
579     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
580     {
581         if (ptr->type == XML_ELEMENT_NODE &&
582             !strcmp((const char *) ptr->name, "max-clients"))
583         {
584             const char *t = get_text(ptr);
585             if (t)
586             {
587                 *max_clients = atoi(t);
588                 if (*max_clients  < 1)
589                     *max_clients = 1;
590             }
591         }
592     }
593     ptr = find_target_node(name);
594     if (ptr)
595     {
596         if (name)
597         {
598             url[0] = name;
599             url[1] = 0;
600         }
601         return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
602                            target_idletime, client_idletime,
603                            keepalive_limit_bw, keepalive_limit_pdu,
604                            pre_init);
605     }
606 #else
607     *url = name;
608     return;
609 #endif
610 }
611
612