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