Fix attribute type checking
[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.13 2003-10-24 10:21:24 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 ptrl,
217                                              Z_AttributeList *attrs,
218                                              char **addinfo)
219 {
220     int i;
221     for (i = 0; i<attrs->num_attributes; i++)
222     {
223         Z_AttributeElement *el = attrs->attributes[i];
224         
225         if (!el->attributeType)
226             continue;
227         int type = *el->attributeType;
228         int *value = 0;
229         
230         if (el->which == Z_AttributeValue_numeric && el->value.numeric)
231             value = el->value.numeric;
232         
233         xmlNodePtr ptr;
234         for(ptr = ptrl->children; ptr; ptr = ptr->next)
235         {
236             if (ptr->type == XML_ELEMENT_NODE &&
237                 !strcmp((const char *) ptr->name, "attribute"))
238             {
239                 const char *match_type = 0;
240                 const char *match_value = 0;
241                 const char *match_error = 0;
242                 struct _xmlAttr *attr;
243                 for (attr = ptr->properties; attr; attr = attr->next)
244                 {
245                     if (!strcmp((const char *) attr->name, "type") &&
246                         attr->children && attr->children->type == XML_TEXT_NODE)
247                         match_type = (const char *) attr->children->content;
248                     if (!strcmp((const char *) attr->name, "value") &&
249                         attr->children && attr->children->type == XML_TEXT_NODE)
250                         match_value = (const char *) attr->children->content;
251                     if (!strcmp((const char *) attr->name, "error") &&
252                         attr->children && attr->children->type == XML_TEXT_NODE)
253                         match_error = (const char *) attr->children->content;
254                 }
255                 if (match_type && match_value)
256                 {
257                     char addinfo_str[20];
258                     if (!match_list(type, match_type))
259                         continue;
260                     
261                     *addinfo_str = '\0';
262                     if (!strcmp(match_type, "*"))
263                         sprintf (addinfo_str, "%d", type);
264                     else if (value)
265                     {
266                         if (!match_list(*value, match_value))
267                             continue;
268                         sprintf (addinfo_str, "%d", *value);
269                     }
270                     else
271                         continue;
272                     
273                     if (match_error)
274                     {
275                         if (*addinfo_str)
276                             *addinfo = odr_strdup(odr, addinfo_str);
277                         return atoi(match_error);
278                     }
279                     return 0;
280                 }
281             }
282         }
283     }
284     return 0;
285 }
286 #endif
287
288 #if HAVE_XML2
289 int Yaz_ProxyConfig::check_type_1_structure(ODR odr, xmlNodePtr ptr,
290                                             Z_RPNStructure *q,
291                                             char **addinfo)
292 {
293     if (q->which == Z_RPNStructure_complex)
294     {
295         int e = check_type_1_structure(odr, ptr, q->u.complex->s1, addinfo);
296         if (e)
297             return e;
298         e = check_type_1_structure(odr, ptr, q->u.complex->s2, addinfo);
299         return e;
300     }
301     else if (q->which == Z_RPNStructure_simple)
302     {
303         if (q->u.simple->which == Z_Operand_APT)
304         {
305             return check_type_1_attributes(
306                 odr, ptr, q->u.simple->u.attributesPlusTerm->attributes,
307                 addinfo);
308         }
309     }
310     return 0;
311 }
312 #endif
313
314 #if HAVE_XML2
315 int Yaz_ProxyConfig::check_type_1(ODR odr, xmlNodePtr ptr, Z_RPNQuery *query,
316                                   char **addinfo)
317 {
318     // possibly check for Bib-1
319     return check_type_1_structure(odr, ptr, query->RPNStructure, addinfo);
320 }
321 #endif
322
323 int Yaz_ProxyConfig::check_query(ODR odr, const char *name, Z_Query *query,
324                                  char **addinfo)
325 {
326 #if HAVE_XML2
327     xmlNodePtr ptr;
328     
329     ptr = find_target_node(name);
330     if (ptr)
331     {
332         if (query->which == Z_Query_type_1 || query->which == Z_Query_type_101)
333             return check_type_1(odr, ptr, query->u.type_1, addinfo);
334     }
335 #endif
336     return 0;
337 }
338
339 int Yaz_ProxyConfig::check_syntax(ODR odr, const char *name,
340                                   Odr_oid *syntax, char **addinfo)
341 {
342 #if HAVE_XML2
343     xmlNodePtr ptr;
344     
345     ptr = find_target_node(name);
346     if (!ptr)
347         return 0;
348     for(ptr = ptr->children; ptr; ptr = ptr->next)
349     {
350         if (ptr->type == XML_ELEMENT_NODE &&
351             !strcmp((const char *) ptr->name, "syntax"))
352         {
353             int match = 0;  // if we match record syntax
354             const char *match_type = 0;
355             const char *match_error = 0;
356             const char *match_marcxml = 0;
357             struct _xmlAttr *attr;
358             for (attr = ptr->properties; attr; attr = attr->next)
359             {
360                 if (!strcmp((const char *) attr->name, "type") &&
361                     attr->children && attr->children->type == XML_TEXT_NODE)
362                     match_type = (const char *) attr->children->content;
363                 if (!strcmp((const char *) attr->name, "error") &&
364                     attr->children && attr->children->type == XML_TEXT_NODE)
365                     match_error = (const char *) attr->children->content;
366                 if (!strcmp((const char *) attr->name, "marcxml") &&
367                     attr->children && attr->children->type == XML_TEXT_NODE)
368                     match_marcxml = (const char *) attr->children->content;
369             }
370             if (match_type)
371             {
372                 if (!strcmp(match_type, "*"))
373                     match = 1;
374                 else if (!strcmp(match_type, "none"))
375                 {
376                     if (syntax == 0)
377                         match = 1;
378                 }
379                 else if (syntax)
380                 {
381                     int match_oid[OID_SIZE];
382                     oid_name_to_oid(CLASS_RECSYN, match_type, match_oid);
383                     if (oid_oidcmp(match_oid, syntax) == 0)
384                         match = 1;
385                 }
386             }
387             if (match)
388             {
389                 if (match_marcxml)
390                 {
391                     return -1;
392                 }
393                 if (match_error)
394                 {
395                     if (syntax)
396                     {
397                         char dotoid_str[100];
398                         oid_to_dotstring(syntax, dotoid_str);
399                         *addinfo = odr_strdup(odr, dotoid_str);
400                     }
401                     return atoi(match_error);
402                 }
403                 return 0;
404             }
405         }
406     }
407 #endif
408     return 0;
409 }
410
411 #if HAVE_XML2
412 xmlNodePtr Yaz_ProxyConfig::find_target_node(const char *name)
413 {
414     xmlNodePtr ptr;
415     if (!m_proxyPtr)
416         return 0;
417     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
418     {
419         if (ptr->type == XML_ELEMENT_NODE &&
420             !strcmp((const char *) ptr->name, "target"))
421         {
422             // default one ? 
423             if (!name)
424             {
425                 // <target default="1"> ?
426                 struct _xmlAttr *attr;
427                 for (attr = ptr->properties; attr; attr = attr->next)
428                     if (!strcmp((const char *) attr->name, "default") &&
429                         attr->children && attr->children->type == XML_TEXT_NODE)
430                     {
431                         xmlChar *t = attr->children->content;
432                         if (!t || *t == '1')
433                             return ptr;
434                     }
435             }
436             else
437             {
438                 // <target name="name"> ?
439                 struct _xmlAttr *attr;
440                 for (attr = ptr->properties; attr; attr = attr->next)
441                     if (!strcmp((const char *) attr->name, "name"))
442                     {
443                         if (attr->children
444                             && attr->children->type==XML_TEXT_NODE
445                             && attr->children->content 
446                             && (!strcmp((const char *) attr->children->content,
447                                         name)
448                                 || !strcmp((const char *) attr->children->content,
449                                            "*")))
450                         {
451                             return ptr;
452                         }
453                     }
454             }
455         }
456     }
457     return 0;
458 }
459 #endif
460
461 int Yaz_ProxyConfig::get_target_no(int no,
462                                    const char **name,
463                                    const char **url,
464                                    int *limit_bw,
465                                    int *limit_pdu,
466                                    int *limit_req,
467                                    int *target_idletime,
468                                    int *client_idletime,
469                                    int *max_clients,
470                                    int *keepalive_limit_bw,
471                                    int *keepalive_limit_pdu,
472                                    int *pre_init)
473 {
474 #if HAVE_XML2
475     xmlNodePtr ptr;
476     if (!m_proxyPtr)
477         return 0;
478     int i = 0;
479     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
480         if (ptr->type == XML_ELEMENT_NODE &&
481             !strcmp((const char *) ptr->name, "target"))
482         {
483             if (i == no)
484             {
485                 struct _xmlAttr *attr;
486                 for (attr = ptr->properties; attr; attr = attr->next)
487                     if (!strcmp((const char *) attr->name, "name"))
488                     {
489                         if (attr->children
490                             && attr->children->type==XML_TEXT_NODE
491                             && attr->children->content)
492                             *name = (const char *) attr->children->content;
493                     }
494                 return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
495                                    target_idletime, client_idletime,
496                                    keepalive_limit_bw, keepalive_limit_pdu,
497                                    pre_init);
498                 return 1;
499             }
500             i++;
501         }
502 #endif
503     return 0;
504 }
505
506 int Yaz_ProxyConfig::mycmp(const char *hay, const char *item, size_t len)
507 {
508     if (len == strlen(item) && memcmp(hay, item, len) == 0)
509         return 1;
510     return 0;
511 }
512
513 void Yaz_ProxyConfig::get_generic_info(int *log_mask,
514                                        int *max_clients)
515 {
516 #if HAVE_XML2
517     xmlNodePtr ptr;
518     if (!m_proxyPtr)
519         return;
520     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
521     {
522         if (ptr->type == XML_ELEMENT_NODE 
523             && !strcmp((const char *) ptr->name, "log"))
524         {
525             const char *v = get_text(ptr);
526             *log_mask = 0;
527             while (v && *v)
528             {
529                 const char *cp = v;
530                 while (*cp && *cp != ',' && !isspace(*cp))
531                     cp++;
532                 size_t len = cp - v;
533                 if (mycmp(v, "client-apdu", len))
534                     *log_mask |= PROXY_LOG_APDU_CLIENT;
535                 if (mycmp(v, "server-apdu", len))
536                     *log_mask |= PROXY_LOG_APDU_SERVER;
537                 if (mycmp(v, "client-requests", len))
538                     *log_mask |= PROXY_LOG_REQ_CLIENT;
539                 if (mycmp(v, "server-requests", len))
540                     *log_mask |= PROXY_LOG_REQ_SERVER;
541                 if (isdigit(*v))
542                     *log_mask |= atoi(v);
543                 if (*cp == ',')
544                     cp++;
545                 while (*cp && isspace(*cp))
546                     cp++;
547                 v = cp;
548             }
549         }
550         if (ptr->type == XML_ELEMENT_NODE &&
551             !strcmp((const char *) ptr->name, "max-clients"))
552         {
553             const char *t = get_text(ptr);
554             if (t)
555             {
556                 *max_clients = atoi(t);
557                 if (*max_clients  < 1)
558                     *max_clients = 1;
559             }
560         }
561     }
562 #endif
563 }
564
565 void Yaz_ProxyConfig::get_target_info(const char *name,
566                                       const char **url,
567                                       int *limit_bw,
568                                       int *limit_pdu,
569                                       int *limit_req,
570                                       int *target_idletime,
571                                       int *client_idletime,
572                                       int *max_clients,
573                                       int *keepalive_limit_bw,
574                                       int *keepalive_limit_pdu,
575                                       int *pre_init)
576 {
577 #if HAVE_XML2
578     xmlNodePtr ptr;
579     if (!m_proxyPtr)
580     {
581         url[0] = name;
582         url[1] = 0;
583         return;
584     }
585     url[0] = 0;
586     for (ptr = m_proxyPtr->children; ptr; ptr = ptr->next)
587     {
588         if (ptr->type == XML_ELEMENT_NODE &&
589             !strcmp((const char *) ptr->name, "max-clients"))
590         {
591             const char *t = get_text(ptr);
592             if (t)
593             {
594                 *max_clients = atoi(t);
595                 if (*max_clients  < 1)
596                     *max_clients = 1;
597             }
598         }
599     }
600     ptr = find_target_node(name);
601     if (ptr)
602     {
603         if (name)
604         {
605             url[0] = name;
606             url[1] = 0;
607         }
608         return_target_info(ptr, url, limit_bw, limit_pdu, limit_req,
609                            target_idletime, client_idletime,
610                            keepalive_limit_bw, keepalive_limit_pdu,
611                            pre_init);
612     }
613 #else
614     *url = name;
615     return;
616 #endif
617 }
618
619