http-req-max per IP MP-632
[metaproxy-moved-to-github.git] / src / util.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) Index Data
3
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include "config.hpp"
20 #include <metaproxy/util.hpp>
21
22 #include <yaz/odr.h>
23 #include <yaz/comstack.h>
24 #include <yaz/pquery.h>
25 #include <yaz/otherinfo.h>
26 #include <yaz/querytowrbuf.h>
27 #include <yaz/oid_db.h>
28 #include <yaz/srw.h>
29 #include <yaz/match_glob.h>
30
31 #include <boost/algorithm/string.hpp>
32
33 #include <iostream>
34
35 namespace mp = metaproxy_1;
36
37 // Doxygen doesn't like mp::util, so we use this instead
38 namespace mp_util = metaproxy_1::util;
39
40 const char *
41 mp_util::record_composition_to_esn(Z_RecordComposition *comp)
42 {
43     if (comp && comp->which == Z_RecordComp_complex)
44     {
45         if (comp->u.complex->generic
46             && comp->u.complex->generic->elementSpec
47             && (comp->u.complex->generic->elementSpec->which ==
48                 Z_ElementSpec_elementSetName))
49             return comp->u.complex->generic->elementSpec->u.elementSetName;
50     }
51     else if (comp && comp->which == Z_RecordComp_simple &&
52              comp->u.simple->which == Z_ElementSetNames_generic)
53         return comp->u.simple->u.generic;
54     return 0;
55 }
56
57
58
59 std::string mp_util::http_header_value(const Z_HTTP_Header* header,
60                                        const std::string name)
61 {
62     while (header && header->name
63            && std::string(header->name) !=  name)
64         header = header->next;
65
66     if (header && header->name && std::string(header->name) == name
67         && header->value)
68         return std::string(header->value);
69
70     return std::string();
71 }
72
73 std::string mp_util::http_headers_debug(const Z_HTTP_Request &http_req)
74 {
75     std::string message("<html>\n<body>\n<h1>"
76                         "Metaproxy SRUtoZ3950 filter"
77                         "</h1>\n");
78
79     message += "<h3>HTTP Info</h3><br/>\n";
80     message += "<p>\n";
81     message += "<b>Method: </b> " + std::string(http_req.method) + "<br/>\n";
82     message += "<b>Version:</b> " + std::string(http_req.version) + "<br/>\n";
83     message += "<b>Path:   </b> " + std::string(http_req.path) + "<br/>\n";
84
85     message += "<b>Content-Type:</b>"
86         + mp_util::http_header_value(http_req.headers, "Content-Type")
87         + "<br/>\n";
88     message += "<b>Content-Length:</b>"
89         + mp_util::http_header_value(http_req.headers, "Content-Length")
90         + "<br/>\n";
91     message += "</p>\n";
92
93     message += "<h3>Headers</h3><br/>\n";
94     message += "<p>\n";
95     Z_HTTP_Header* header = http_req.headers;
96     while (header){
97         message += "<b>Header: </b> <i>"
98             + std::string(header->name) + ":</i> "
99             + std::string(header->value) + "<br/>\n";
100         header = header->next;
101     }
102     message += "</p>\n";
103     message += "</body>\n</html>\n";
104     return message;
105 }
106
107
108 void mp_util::http_response(metaproxy_1::Package &package,
109                      const std::string &content,
110                      int http_code)
111 {
112
113     Z_GDU *zgdu_req = package.request().get();
114     Z_GDU *zgdu_res = 0;
115     mp::odr odr;
116     zgdu_res
117        = odr.create_HTTP_Response(package.session(),
118                                   zgdu_req->u.HTTP_Request,
119                                   http_code);
120
121     zgdu_res->u.HTTP_Response->content_len = content.size();
122     zgdu_res->u.HTTP_Response->content_buf
123         = (char*) odr_malloc(odr, zgdu_res->u.HTTP_Response->content_len);
124
125     strncpy(zgdu_res->u.HTTP_Response->content_buf,
126             content.c_str(),  zgdu_res->u.HTTP_Response->content_len);
127
128     //z_HTTP_header_add(odr, &hres->headers,
129     //                  "Content-Type", content_type.c_str());
130     package.response() = zgdu_res;
131 }
132
133
134 int mp_util::memcmp2(const void *buf1, int len1,
135                      const void *buf2, int len2)
136 {
137     int d = len1 - len2;
138
139     // compare buffer (common length)
140     int c = memcmp(buf1, buf2, d > 0 ? len2 : len1);
141     if (c > 0)
142         return 1;
143     else if (c < 0)
144         return -1;
145
146     // compare (remaining bytes)
147     if (d > 0)
148         return 1;
149     else if (d < 0)
150         return -1;
151     return 0;
152 }
153
154
155 std::string mp_util::database_name_normalize(const std::string &s)
156 {
157     std::string r = s;
158     size_t i;
159     for (i = 0; i < r.length(); i++)
160     {
161         int ch = r[i];
162         if (ch >= 'A' && ch <= 'Z')
163             r[i] = ch + 'a' - 'A';
164     }
165     return r;
166
167 }
168
169 Z_RecordComposition *mp_util::piggyback_to_RecordComposition(
170     ODR odr, Odr_int result_set_size, Z_SearchRequest *sreq)
171 {
172     Z_RecordComposition *comp = 0;
173     Odr_int present_dummy;
174     const char *element_set_name = 0;
175     mp::util::piggyback_sr(sreq, result_set_size,
176                            present_dummy, &element_set_name);
177     if (element_set_name)
178     {
179         comp  = (Z_RecordComposition *) odr_malloc(odr, sizeof(*comp));
180         comp->which = Z_RecordComp_simple;
181         comp->u.simple = (Z_ElementSetNames *)
182             odr_malloc(odr, sizeof(Z_ElementSetNames));
183         comp->u.simple->which = Z_ElementSetNames_generic;
184         comp->u.simple->u.generic = odr_strdup(odr, element_set_name);
185     }
186     return comp;
187 }
188
189 void mp_util::piggyback_sr(Z_SearchRequest *sreq,
190                            Odr_int result_set_size,
191                            Odr_int &number_to_present,
192                            const char **element_set_name)
193 {
194     Z_ElementSetNames *esn;
195     const char *smallSetElementSetNames = 0;
196     const char *mediumSetElementSetNames = 0;
197
198     esn = sreq->smallSetElementSetNames;
199     if (esn && esn->which == Z_ElementSetNames_generic)
200         smallSetElementSetNames = esn->u.generic;
201
202     esn = sreq->mediumSetElementSetNames;
203     if (esn && esn->which == Z_ElementSetNames_generic)
204         mediumSetElementSetNames = esn->u.generic;
205
206     piggyback(*sreq->smallSetUpperBound,
207               *sreq->largeSetLowerBound,
208               *sreq->mediumSetPresentNumber,
209               smallSetElementSetNames,
210               mediumSetElementSetNames,
211               result_set_size,
212               number_to_present,
213               element_set_name);
214 }
215
216 void mp_util::piggyback(int smallSetUpperBound,
217                         int largeSetLowerBound,
218                         int mediumSetPresentNumber,
219                         int result_set_size,
220                         int &number_to_present)
221 {
222     Odr_int tmp = number_to_present;
223     piggyback(smallSetUpperBound, largeSetLowerBound, mediumSetPresentNumber,
224               0, 0, result_set_size, tmp, 0);
225     number_to_present = tmp;
226 }
227
228 void mp_util::piggyback(Odr_int smallSetUpperBound,
229                         Odr_int largeSetLowerBound,
230                         Odr_int mediumSetPresentNumber,
231                         const char *smallSetElementSetNames,
232                         const char *mediumSetElementSetNames,
233                         Odr_int result_set_size,
234                         Odr_int &number_to_present,
235                         const char **element_set_name)
236 {
237     // deal with piggyback
238
239     if (result_set_size < smallSetUpperBound)
240     {
241         // small set . Return all records in set
242         number_to_present = result_set_size;
243         if (element_set_name && smallSetElementSetNames)
244             *element_set_name = smallSetElementSetNames;
245
246     }
247     else if (result_set_size > largeSetLowerBound)
248     {
249         // large set . Return no records
250         number_to_present = 0;
251         if (element_set_name)
252             *element_set_name = 0;
253     }
254     else
255     {
256         // medium set . Return mediumSetPresentNumber records
257         number_to_present = mediumSetPresentNumber;
258         if (number_to_present > result_set_size)
259             number_to_present = result_set_size;
260         if (element_set_name && mediumSetElementSetNames)
261             *element_set_name = mediumSetElementSetNames;
262     }
263 }
264
265 bool mp_util::pqf(ODR odr, Z_APDU *apdu, const std::string &q)
266 {
267     YAZ_PQF_Parser pqf_parser = yaz_pqf_create();
268
269     Z_RPNQuery *rpn = yaz_pqf_parse(pqf_parser, odr, q.c_str());
270     if (!rpn)
271     {
272         yaz_pqf_destroy(pqf_parser);
273         return false;
274     }
275     yaz_pqf_destroy(pqf_parser);
276     Z_Query *query = (Z_Query *) odr_malloc(odr, sizeof(Z_Query));
277     query->which = Z_Query_type_1;
278     query->u.type_1 = rpn;
279
280     apdu->u.searchRequest->query = query;
281     return true;
282 }
283
284
285 std::string mp_util::zQueryToString(Z_Query *query)
286 {
287     std::string query_str = "";
288
289     if (query && query->which == Z_Query_type_1)
290     {
291         Z_RPNQuery *rpn = query->u.type_1;
292
293         if (rpn)
294         {
295             mp::wrbuf w;
296
297             // put query in w
298             yaz_rpnquery_to_wrbuf(w, rpn);
299
300             // from w to std::string
301             query_str = std::string(w.buf(), w.len());
302         }
303     }
304
305 #if 0
306     if (query && query->which == Z_Query_type_1){
307
308         // allocate wrbuf (strings in YAZ!)
309         WRBUF w = wrbuf_alloc();
310
311         // put query in w
312         yaz_query_to_wrbuf(w, query);
313
314         // from w to std::string
315         query_str = std::string(wrbuf_buf(w), wrbuf_len(w));
316
317         // destroy wrbuf
318         wrbuf_free(w, 1);
319     }
320 #endif
321     return query_str;
322 }
323
324 void mp_util::get_default_diag(Z_DefaultDiagFormat *r,
325                                          int &error_code, std::string &addinfo)
326 {
327     error_code = *r->condition;
328     switch (r->which)
329     {
330     case Z_DefaultDiagFormat_v2Addinfo:
331         addinfo = std::string(r->u.v2Addinfo);
332         break;
333     case Z_DefaultDiagFormat_v3Addinfo:
334         addinfo = r->u.v3Addinfo;
335         break;
336     }
337 }
338
339 void mp_util::get_init_diagnostics(
340     Z_InitResponse *initrs, int &error_code, std::string &addinfo)
341 {
342
343     Z_DefaultDiagFormat *df = yaz_decode_init_diag(0, initrs);
344
345     if (df)
346         get_default_diag(df, error_code, addinfo);
347 }
348
349 int mp_util::get_or_remove_vhost_otherinfo(
350     Z_OtherInformation **otherInformation,
351     bool remove_flag,
352     std::list<std::string> &vhosts)
353 {
354     int cat;
355     for (cat = 1; ; cat++)
356     {
357         // check virtual host
358         const char *vhost =
359             yaz_oi_get_string_oid(otherInformation,
360                                   yaz_oid_userinfo_proxy,
361                                   cat /* categoryValue */,
362                                   remove_flag /* delete flag */);
363         if (!vhost)
364             break;
365         vhosts.push_back(std::string(vhost));
366     }
367     --cat;
368     return cat;
369 }
370
371 void mp_util::get_vhost_otherinfo(
372     Z_OtherInformation *otherInformation,
373     std::list<std::string> &vhosts)
374 {
375     get_or_remove_vhost_otherinfo(&otherInformation, false, vhosts);
376 }
377
378 int mp_util::remove_vhost_otherinfo(
379     Z_OtherInformation **otherInformation,
380     std::list<std::string> &vhosts)
381 {
382     return get_or_remove_vhost_otherinfo(otherInformation, true, vhosts);
383 }
384
385 void mp_util::set_vhost_otherinfo(
386     Z_OtherInformation **otherInformation, ODR odr,
387     const std::list<std::string> &vhosts)
388 {
389     int cat;
390     std::list<std::string>::const_iterator it = vhosts.begin();
391
392     for (cat = 1; it != vhosts.end() ; cat++, it++)
393     {
394         yaz_oi_set_string_oid(otherInformation, odr,
395                               yaz_oid_userinfo_proxy, cat, it->c_str());
396     }
397 }
398
399 void mp_util::set_vhost_otherinfo(
400     Z_OtherInformation **otherInformation, ODR odr,
401     const std::string vhost, const int cat)
402 {
403     yaz_oi_set_string_oid(otherInformation, odr,
404                           yaz_oid_userinfo_proxy, cat, vhost.c_str());
405 }
406
407 void mp_util::split_zurl(std::string zurl, std::string &host,
408                          std::list<std::string> &db)
409 {
410     const char *zurl_cstr = zurl.c_str();
411     const char *args = 0;
412     cs_get_host_args(zurl_cstr, &args);
413
414     if (args && *args)
415     {
416         host = std::string(zurl_cstr, args - zurl_cstr);
417
418         const char *cp1 = args;
419         while (1)
420         {
421             const char *cp2 = strchr(cp1, '+');
422             if (cp2)
423                 db.push_back(std::string(cp1, cp2 - cp1));
424             else
425             {
426                 db.push_back(std::string(cp1));
427                 break;
428             }
429             cp1 = cp2+1;
430         }
431     }
432     else
433         host = zurl;
434 }
435
436 bool mp_util::set_databases_from_zurl(
437     ODR odr, std::string zurl,
438     int *db_num, char ***db_strings)
439 {
440     std::string host;
441     std::list<std::string> dblist;
442
443     split_zurl(zurl, host, dblist);
444
445     if (dblist.size() == 0)
446         return false;
447     *db_num = dblist.size();
448     *db_strings = (char **) odr_malloc(odr, sizeof(char*) * (*db_num));
449
450     std::list<std::string>::const_iterator it = dblist.begin();
451     for (int i = 0; it != dblist.end(); it++, i++)
452         (*db_strings)[i] = odr_strdup(odr, it->c_str());
453     return true;
454 }
455
456 mp::odr::odr(int type)
457 {
458     m_odr = odr_createmem(type);
459 }
460
461 mp::odr::odr()
462 {
463     m_odr = odr_createmem(ODR_ENCODE);
464 }
465
466 mp::odr::~odr()
467 {
468     odr_destroy(m_odr);
469 }
470
471 mp::odr::operator ODR() const
472 {
473     return m_odr;
474 }
475
476 Z_APDU *mp::odr::create_close(const Z_APDU *in_apdu,
477                               int reason, const char *addinfo)
478 {
479     Z_APDU *apdu = create_APDU(Z_APDU_close, in_apdu);
480
481     *apdu->u.close->closeReason = reason;
482     if (addinfo)
483         apdu->u.close->diagnosticInformation = odr_strdup(m_odr, addinfo);
484     return apdu;
485 }
486
487 Z_APDU *mp::odr::create_APDU(int type, const Z_APDU *in_apdu)
488 {
489     return mp::util::create_APDU(m_odr, type, in_apdu);
490 }
491
492 Z_APDU *mp_util::create_APDU(ODR odr, int type, const Z_APDU *in_apdu)
493 {
494     Z_APDU *out_apdu = zget_APDU(odr, type);
495     transfer_referenceId(odr, in_apdu, out_apdu);
496     return out_apdu;
497 }
498
499 void mp_util::transfer_referenceId(ODR odr, const Z_APDU *src, Z_APDU *dst)
500 {
501     Z_ReferenceId **id_to = mp::util::get_referenceId(dst);
502     *id_to = 0;
503     if (src)
504     {
505         Z_ReferenceId **id_from = mp::util::get_referenceId(src);
506         if (id_from && *id_from)
507             *id_to = odr_create_Odr_oct(odr, (*id_from)->buf,
508                                         (*id_from)->len);
509     }
510 }
511
512 Z_APDU *mp::odr::create_initResponse(const Z_APDU *in_apdu,
513                                      int error, const char *addinfo)
514 {
515     Z_APDU *apdu = create_APDU(Z_APDU_initResponse, in_apdu);
516     if (error)
517     {
518         apdu->u.initResponse->userInformationField =
519             zget_init_diagnostics(m_odr, error, addinfo);
520         *apdu->u.initResponse->result = 0;
521     }
522     apdu->u.initResponse->implementationName =
523         odr_prepend(m_odr, "Metaproxy",
524                     apdu->u.initResponse->implementationName);
525     apdu->u.initResponse->implementationVersion =
526         odr_prepend(m_odr,
527                     VERSION, apdu->u.initResponse->implementationVersion);
528
529     return apdu;
530 }
531
532 Z_APDU *mp::odr::create_searchResponse(const Z_APDU *in_apdu,
533                                        int error, const char *addinfo)
534 {
535     Z_APDU *apdu = create_APDU(Z_APDU_searchResponse, in_apdu);
536     if (error)
537     {
538         Z_Records *rec = (Z_Records *) odr_malloc(m_odr, sizeof(Z_Records));
539         *apdu->u.searchResponse->searchStatus = 0;
540         apdu->u.searchResponse->records = rec;
541         rec->which = Z_Records_NSD;
542         rec->u.nonSurrogateDiagnostic =
543             zget_DefaultDiagFormat(m_odr, error, addinfo);
544
545     }
546     return apdu;
547 }
548
549 Z_APDU *mp::odr::create_presentResponse(const Z_APDU *in_apdu,
550                                         int error, const char *addinfo)
551 {
552     Z_APDU *apdu = create_APDU(Z_APDU_presentResponse, in_apdu);
553     if (error)
554     {
555         Z_Records *rec = (Z_Records *) odr_malloc(m_odr, sizeof(Z_Records));
556         apdu->u.presentResponse->records = rec;
557
558         rec->which = Z_Records_NSD;
559         rec->u.nonSurrogateDiagnostic =
560             zget_DefaultDiagFormat(m_odr, error, addinfo);
561         *apdu->u.presentResponse->presentStatus = Z_PresentStatus_failure;
562     }
563     return apdu;
564 }
565
566 Z_APDU *mp::odr::create_scanResponse(const Z_APDU *in_apdu,
567                                      int error, const char *addinfo)
568 {
569     Z_APDU *apdu = create_APDU(Z_APDU_scanResponse, in_apdu);
570     Z_ScanResponse *res = apdu->u.scanResponse;
571     res->entries = (Z_ListEntries *) odr_malloc(m_odr, sizeof(*res->entries));
572     res->entries->num_entries = 0;
573     res->entries->entries = 0;
574
575     if (error)
576     {
577         *res->scanStatus = Z_Scan_failure;
578
579         res->entries->num_nonsurrogateDiagnostics = 1;
580         res->entries->nonsurrogateDiagnostics = (Z_DiagRec **)
581             odr_malloc(m_odr, sizeof(Z_DiagRec *));
582         res->entries->nonsurrogateDiagnostics[0] =
583             zget_DiagRec(m_odr, error, addinfo);
584     }
585     else
586     {
587         res->entries->num_nonsurrogateDiagnostics = 0;
588         res->entries->nonsurrogateDiagnostics = 0;
589     }
590     return apdu;
591 }
592
593 Z_GDU *mp::odr::create_HTTP_Response_details(mp::Session &session,
594                                              Z_HTTP_Request *hreq, int code,
595                                              const char *details)
596 {
597     const char *response_version = "1.0";
598     bool keepalive = false;
599     if (!strcmp(hreq->version, "1.0"))
600     {
601         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
602         if (v && !strcmp(v, "Keep-Alive"))
603             keepalive = true;
604         else
605             session.close();
606         response_version = "1.0";
607     }
608     else
609     {
610         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
611         if (v && !strcmp(v, "close"))
612             session.close();
613         else
614             keepalive = true;
615         response_version = "1.1";
616     }
617
618     Z_GDU *gdu = z_get_HTTP_Response_server(
619         m_odr, code, details, "Metaproxy/" VERSION,
620         "http://www.indexdata.com/metaproxy");
621     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
622     hres->version = odr_strdup(m_odr, response_version);
623     if (keepalive)
624         z_HTTP_header_add(m_odr, &hres->headers, "Connection", "Keep-Alive");
625     return gdu;
626 }
627
628 Z_GDU *mp::odr::create_HTTP_Response(mp::Session &session,
629                                      Z_HTTP_Request *hreq, int code)
630 {
631     return create_HTTP_Response_details(session, hreq, code, 0);
632
633 }
634
635 Z_ReferenceId **mp_util::get_referenceId(const Z_APDU *apdu)
636 {
637     switch (apdu->which)
638     {
639     case  Z_APDU_initRequest:
640         return &apdu->u.initRequest->referenceId;
641     case  Z_APDU_initResponse:
642         return &apdu->u.initResponse->referenceId;
643     case  Z_APDU_searchRequest:
644         return &apdu->u.searchRequest->referenceId;
645     case  Z_APDU_searchResponse:
646         return &apdu->u.searchResponse->referenceId;
647     case  Z_APDU_presentRequest:
648         return &apdu->u.presentRequest->referenceId;
649     case  Z_APDU_presentResponse:
650         return &apdu->u.presentResponse->referenceId;
651     case  Z_APDU_deleteResultSetRequest:
652         return &apdu->u.deleteResultSetRequest->referenceId;
653     case  Z_APDU_deleteResultSetResponse:
654         return &apdu->u.deleteResultSetResponse->referenceId;
655     case  Z_APDU_accessControlRequest:
656         return &apdu->u.accessControlRequest->referenceId;
657     case  Z_APDU_accessControlResponse:
658         return &apdu->u.accessControlResponse->referenceId;
659     case  Z_APDU_resourceControlRequest:
660         return &apdu->u.resourceControlRequest->referenceId;
661     case  Z_APDU_resourceControlResponse:
662         return &apdu->u.resourceControlResponse->referenceId;
663     case  Z_APDU_triggerResourceControlRequest:
664         return &apdu->u.triggerResourceControlRequest->referenceId;
665     case  Z_APDU_resourceReportRequest:
666         return &apdu->u.resourceReportRequest->referenceId;
667     case  Z_APDU_resourceReportResponse:
668         return &apdu->u.resourceReportResponse->referenceId;
669     case  Z_APDU_scanRequest:
670         return &apdu->u.scanRequest->referenceId;
671     case  Z_APDU_scanResponse:
672         return &apdu->u.scanResponse->referenceId;
673     case  Z_APDU_sortRequest:
674         return &apdu->u.sortRequest->referenceId;
675     case  Z_APDU_sortResponse:
676         return &apdu->u.sortResponse->referenceId;
677     case  Z_APDU_segmentRequest:
678         return &apdu->u.segmentRequest->referenceId;
679     case  Z_APDU_extendedServicesRequest:
680         return &apdu->u.extendedServicesRequest->referenceId;
681     case  Z_APDU_extendedServicesResponse:
682         return &apdu->u.extendedServicesResponse->referenceId;
683     case  Z_APDU_close:
684         return &apdu->u.close->referenceId;
685     }
686     return 0;
687 }
688
689 std::string mp_util::uri_encode(std::string s)
690 {
691     char *x = (char *) xmalloc(1 + s.length() * 3);
692     yaz_encode_uri_component(x, s.c_str());
693     std::string result(x);
694     xfree(x);
695     return result;
696 }
697
698
699 std::string mp_util::uri_decode(std::string s)
700 {
701     char *x = (char *) xmalloc(1 + s.length());
702     yaz_decode_uri_component(x, s.c_str(), s.length());
703     std::string result(x);
704     xfree(x);
705     return result;
706 }
707
708 mp::wrbuf::wrbuf()
709 {
710     m_wrbuf = wrbuf_alloc();
711 }
712
713 mp::wrbuf::~wrbuf()
714 {
715     wrbuf_destroy(m_wrbuf);
716 }
717
718 mp::wrbuf::operator WRBUF() const
719 {
720     return m_wrbuf;
721 }
722
723 size_t mp::wrbuf::len()
724 {
725     return wrbuf_len(m_wrbuf);
726 }
727
728 const char *mp::wrbuf::buf()
729 {
730     return wrbuf_buf(m_wrbuf);
731 }
732
733 const char *mp::wrbuf::c_str()
734 {
735     return wrbuf_cstr(m_wrbuf);
736 }
737
738 const char *mp::wrbuf::c_str_null()
739 {
740     return wrbuf_cstr_null(m_wrbuf);
741 }
742
743 bool mp::util::match_ip(const std::string &pattern, const std::string &value)
744 {
745     std::vector<std::string> globitems;
746     boost::split(globitems, pattern, boost::is_any_of(" "));
747     std::vector<std::string>::const_iterator it = globitems.begin();
748     bool ret_value = true; // for now (if only empty values)
749     for (; it != globitems.end(); it++)
750     {
751         const char *c_str = (*it).c_str();
752         if (*c_str)
753         {
754             ret_value = false; // at least one non-empty value
755             if (yaz_match_glob(c_str, value.c_str()))
756                 return true;
757         }
758     }
759     return ret_value;
760 }
761
762 /*
763  * Local variables:
764  * c-basic-offset: 4
765  * c-file-style: "Stroustrup"
766  * indent-tabs-mode: nil
767  * End:
768  * vim: shiftwidth=4 tabstop=8 expandtab
769  */
770