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