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