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