more work on SRU explain filters
[metaproxy-moved-to-github.git] / src / sru_util.cpp
1 /* $Id: sru_util.cpp,v 1.5 2007-01-07 00:41:18 marc Exp $
2    Copyright (c) 2005-2006, Index Data.
3
4    See the LICENSE file for details
5 */
6
7 #include "sru_util.hpp"
8 #include "util.hpp"
9
10 //#include <yaz/wrbuf.h>
11 //#include <yaz/querytowrbuf.h>
12
13 #include <iostream>
14 #include <string>
15
16 namespace mp = metaproxy_1;
17
18 // Doxygen doesn't like mp::gdu, so we use this instead
19 namespace mp_util = metaproxy_1::util;
20
21 const std::string xmlns_explain("http://explain.z3950.org/dtd/2.0/");
22
23 bool mp_util::build_sru_debug_package(mp::Package &package)
24 {
25     Z_GDU *zgdu_req = package.request().get();
26     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
27     {    
28         Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
29         std::string content = mp_util::http_headers_debug(*http_req);
30         int http_code = 400;    
31         mp_util::http_response(package, content, http_code);
32         return true;
33     }
34     package.session().close();
35     return false;
36 }
37
38 mp_util::SRUServerInfo mp_util::get_sru_server_info(mp::Package &package)
39     //Z_SRW_explainRequest const *er_req) 
40 {
41     mp_util::SRUServerInfo sruinfo;
42
43     // getting database info
44     //if (er_req && er_req->database)
45     //    sruinfo.database = er_req->database;
46
47     // getting host and port info
48     sruinfo.host = package.origin().listen_host();
49     sruinfo.port = mp_util::to_string(package.origin().listen_port());
50
51     // overwriting host and port info if set from HTTP Host header
52     Z_GDU *zgdu_req = package.request().get();
53     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
54     {
55         Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
56         if (http_req)
57         {
58
59             //std::string http_method = http_req->method;
60             //std::string http_version = http_req->version;
61             std::string http_path = http_req->path;
62             if (http_path.size() > 1)
63                 sruinfo.database.assign(http_path, 1, std::string::npos);
64
65             std::string http_host_address
66                 = mp_util::http_header_value(http_req->headers, "Host");
67
68             std::string::size_type i = http_host_address.rfind(":");
69             if (i != std::string::npos)
70             {
71                 sruinfo.host.assign(http_host_address, 0, i);
72                 sruinfo.port.assign(http_host_address, i + 1, 
73                                     std::string::npos);
74             }
75         }
76     }
77     
78     //std::cout << "sruinfo.database " << sruinfo.database << "\n";
79     //std::cout << "sruinfo.host " << sruinfo.host << "\n";
80     //std::cout << "sruinfo.port " << sruinfo.port << "\n";
81
82     return sruinfo;
83 }
84
85
86 // bool mp_util::build_simple_explain(mp::Package &package, 
87 //                                    mp::odr &odr_en,
88 //                                    Z_SRW_PDU *sru_pdu_res,
89 //                                    SRUServerInfo sruinfo,
90 //                                    Z_SRW_explainRequest const *er_req) 
91 // {
92 //     // z3950'fy recordPacking
93 //     int record_packing = Z_SRW_recordPacking_XML;
94 //     if (er_req && er_req->recordPacking && 's' == *(er_req->recordPacking))
95 //         record_packing = Z_SRW_recordPacking_string;
96
97 //     // building SRU explain record
98 //     std::string explain_xml 
99 //         = mp_util::to_string(
100 //             "<explain  xmlns=\"" + xmlns_explain + "\">\n"
101 //             "  <serverInfo protocol='SRU'>\n"
102 //             "    <host>")
103 //         + sruinfo.host
104 //         + mp_util::to_string("</host>\n"
105 //             "    <port>")
106 //         + sruinfo.port
107 //         + mp_util::to_string("</port>\n"
108 //             "    <database>")
109 //         + sruinfo.database
110 //         + mp_util::to_string("</database>\n"
111 //             "  </serverInfo>\n"
112 //             "</explain>\n");
113     
114     
115 //     // preparing explain record insert
116 //     Z_SRW_explainResponse *sru_res = sru_pdu_res->u.explain_response;
117     
118 //     // inserting one and only explain record
119     
120 //     sru_res->record.recordPosition = odr_intdup(odr_en, 1);
121 //     sru_res->record.recordPacking = record_packing;
122 //     sru_res->record.recordSchema = (char *)xmlns_explain.c_str();
123 //     sru_res->record.recordData_len = 1 + explain_xml.size();
124 //     sru_res->record.recordData_buf
125 //         = odr_strdupn(odr_en, (const char *)explain_xml.c_str(), 
126 //                       1 + explain_xml.size());
127
128 //     return true;
129 // };
130
131         
132 bool mp_util::build_sru_explain(metaproxy_1::Package &package, 
133                                 metaproxy_1::odr &odr_en,
134                                 Z_SRW_PDU *sru_pdu_res,
135                                 SRUServerInfo sruinfo,
136                                 const xmlNode *explain,
137                                 Z_SRW_explainRequest const *er_req)
138 {
139
140     // building SRU explain record
141     std::string explain_xml;    
142
143     if (explain == 0){
144         explain_xml 
145             = mp_util::to_string(
146                 "<explain  xmlns=\"" + xmlns_explain + "\">\n"
147                 "  <serverInfo protocol='SRU'>\n"
148                 "    <host>")
149             + sruinfo.host
150             + mp_util::to_string("</host>\n"
151                                  "    <port>")
152             + sruinfo.port
153             + mp_util::to_string("</port>\n"
154                                  "    <database>")
155             + sruinfo.database
156             + mp_util::to_string("</database>\n"
157                                  "  </serverInfo>\n"
158                                  "</explain>\n");
159     }
160     else {
161         explain_xml = "<need_to_dump_XML_dom_tree/>";
162     }
163
164
165     // z3950'fy recordPacking
166     int record_packing = Z_SRW_recordPacking_XML;
167     if (er_req && er_req->recordPacking && 's' == *(er_req->recordPacking))
168         record_packing = Z_SRW_recordPacking_string;    
169     
170     // preparing explain record insert
171     Z_SRW_explainResponse *sru_res = sru_pdu_res->u.explain_response;
172     
173     // inserting one and only explain record
174     
175     sru_res->record.recordPosition = odr_intdup(odr_en, 1);
176     sru_res->record.recordPacking = record_packing;
177     sru_res->record.recordSchema = (char *)xmlns_explain.c_str();
178     sru_res->record.recordData_len = 1 + explain_xml.size();
179     sru_res->record.recordData_buf
180         = odr_strdupn(odr_en, (const char *)explain_xml.c_str(), 
181                       1 + explain_xml.size());
182
183     return true;
184 };
185
186
187 bool mp_util::build_sru_response(mp::Package &package, 
188                                              mp::odr &odr_en,
189                                              Z_SOAP *soap,
190                                              const Z_SRW_PDU *sru_pdu_res,
191                                              char *charset,
192                                              const char *stylesheet) 
193 {
194
195     // SRU request package translation to Z3950 package
196     //if (sru_pdu_res)
197     //    std::cout << *(const_cast<Z_SRW_PDU *>(sru_pdu_res)) << "\n";
198     //else
199     //    std::cout << "SRU empty\n";
200
201     
202     Z_GDU *zgdu_req = package.request().get();
203     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
204     {    
205         Z_GDU *zgdu_res //= z_get_HTTP_Response(odr_en, 200);
206             = odr_en.create_HTTP_Response(package.session(), 
207                                           zgdu_req->u.HTTP_Request, 
208                                           200);
209
210         // adding HTTP response code and headers
211         Z_HTTP_Response * http_res = zgdu_res->u.HTTP_Response;
212         //http_res->code = http_code;
213         
214         std::string ctype("text/xml");
215         if (charset){
216             ctype += "; charset=";
217             ctype += charset;
218         }
219
220         z_HTTP_header_add(odr_en, 
221                           &http_res->headers, "Content-Type", ctype.c_str());
222
223          // packaging Z_SOAP into HTML response
224          static Z_SOAP_Handler soap_handlers[4] = {
225               {"http://www.loc.gov/zing/srw/", 0,
226                (Z_SOAP_fun) yaz_srw_codec},
227               {"http://www.loc.gov/zing/srw/v1.0/", 0,
228                (Z_SOAP_fun) yaz_srw_codec},
229               {"http://www.loc.gov/zing/srw/update/", 0,
230                (Z_SOAP_fun) yaz_ucp_codec},
231               {0, 0, 0}
232           };
233
234
235          // empty stylesheet means NO stylesheet
236          if (stylesheet && *stylesheet == '\0')
237              stylesheet = 0;
238          
239          // encoding SRU package
240          
241          soap->u.generic->p  = (void*) sru_pdu_res;         
242          //int ret = 
243          z_soap_codec_enc_xsl(odr_en, &soap, 
244                               &http_res->content_buf, &http_res->content_len,
245                               soap_handlers, charset, stylesheet);
246          
247
248          package.response() = zgdu_res;
249          return true;
250     }
251     package.session().close();
252     return false;
253 }
254
255
256
257  Z_SRW_PDU * mp_util::decode_sru_request(mp::Package &package,
258                                                      mp::odr &odr_de,
259                                                      mp::odr &odr_en,
260                                                      Z_SRW_PDU *sru_pdu_res,
261                                                      Z_SOAP *&soap,
262                                                      char *charset,
263                                                      char *stylesheet) 
264 {
265     Z_GDU *zgdu_req = package.request().get();
266     Z_SRW_PDU *sru_pdu_req = 0;
267
268     //assert((zgdu_req->which == Z_GDU_HTTP_Request));
269     
270     //ignoring all non HTTP_Request packages
271     if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
272         return 0;
273     }
274     
275     Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
276     if (! http_req)
277         return 0;
278
279     // checking if we got a SRU GET/POST/SOAP HTTP package
280     // closing connection if we did not ...
281     if (0 == yaz_sru_decode(http_req, &sru_pdu_req, &soap, 
282                             odr_de, &charset, 
283                             &(sru_pdu_res->u.response->diagnostics), 
284                             &(sru_pdu_res->u.response->num_diagnostics)))
285     {
286         if (sru_pdu_res->u.response->num_diagnostics)
287         {
288             //sru_pdu_res = sru_pdu_res_exp;
289             package.session().close();
290             return 0;
291         }
292         return sru_pdu_req;
293     }
294     else if (0 == yaz_srw_decode(http_req, &sru_pdu_req, &soap, 
295                                  odr_de, &charset))
296         return sru_pdu_req;
297     else 
298     {
299         //sru_pdu_res = sru_pdu_res_exp;
300         package.session().close();
301         return 0;
302     }
303     return 0;
304 }
305
306
307 bool 
308 mp_util::check_sru_query_exists(mp::Package &package, 
309                                 mp::odr &odr_en,
310                                 Z_SRW_PDU *sru_pdu_res, 
311                                 Z_SRW_searchRetrieveRequest const *sr_req)
312 {
313     if( (sr_req->query_type == Z_SRW_query_type_cql && !sr_req->query.cql) )
314     {
315         yaz_add_srw_diagnostic(odr_en,
316                                &(sru_pdu_res->u.response->diagnostics), 
317                                &(sru_pdu_res->u.response->num_diagnostics), 
318                                7, "query");
319         yaz_add_srw_diagnostic(odr_en,
320                                &(sru_pdu_res->u.response->diagnostics), 
321                                &(sru_pdu_res->u.response->num_diagnostics), 
322                                10, "CQL query is empty");
323         return false;
324     }
325     if( (sr_req->query_type == Z_SRW_query_type_xcql && !sr_req->query.xcql) )
326     {
327          yaz_add_srw_diagnostic(odr_en,
328                                &(sru_pdu_res->u.response->diagnostics), 
329                                &(sru_pdu_res->u.response->num_diagnostics), 
330                                10, "XCQL query is empty");
331          return false;
332    }
333     if( (sr_req->query_type == Z_SRW_query_type_pqf && !sr_req->query.pqf) )
334     {
335         yaz_add_srw_diagnostic(odr_en,
336                                &(sru_pdu_res->u.response->diagnostics), 
337                                &(sru_pdu_res->u.response->num_diagnostics), 
338                                10, "PQF query is empty");
339         return false;
340     }
341     return true;
342 };
343
344
345
346
347 Z_ElementSetNames * 
348 mp_util::build_esn_from_schema(mp::odr &odr_en, 
349                                const char *schema)
350 {
351   if (!schema)
352         return 0;
353   
354     Z_ElementSetNames *esn 
355         = (Z_ElementSetNames *) odr_malloc(odr_en, sizeof(Z_ElementSetNames));
356     esn->which = Z_ElementSetNames_generic;
357     esn->u.generic = odr_strdup(odr_en, schema);
358     return esn; 
359 }
360
361
362 std::ostream& std::operator<<(std::ostream& os, Z_SRW_PDU& srw_pdu) 
363 {
364     os << "SRU";
365     
366     switch(srw_pdu.which) {
367     case  Z_SRW_searchRetrieve_request:
368         os << " " << "searchRetrieveRequest";
369         {
370             Z_SRW_searchRetrieveRequest *sr = srw_pdu.u.request;
371             if (sr)
372             {
373                 if (sr->database)
374                     os << " " << (sr->database);
375                 else
376                     os << " -";
377                 if (sr->startRecord)
378                     os << " " << *(sr->startRecord);
379                 else
380                     os << " -";
381                 if (sr->maximumRecords)
382                     os << " " << *(sr->maximumRecords);
383                 else
384                     os << " -";
385                 if (sr->recordPacking)
386                     os << " " << (sr->recordPacking);
387                 else
388                     os << " -";
389
390                 if (sr->recordSchema)
391                     os << " " << (sr->recordSchema);
392                 else
393                     os << " -";
394                 
395                 switch (sr->query_type){
396                 case Z_SRW_query_type_cql:
397                     os << " CQL";
398                     if (sr->query.cql)
399                         os << " " << sr->query.cql;
400                     break;
401                 case Z_SRW_query_type_xcql:
402                     os << " XCQL";
403                     break;
404                 case Z_SRW_query_type_pqf:
405                     os << " PQF";
406                     if (sr->query.pqf)
407                         os << " " << sr->query.pqf;
408                     break;
409                 }
410             }
411         }
412         break;
413     case  Z_SRW_searchRetrieve_response:
414         os << " " << "searchRetrieveResponse";
415         {
416             Z_SRW_searchRetrieveResponse *sr = srw_pdu.u.response;
417             if (sr)
418             {
419                 if (! (sr->num_diagnostics))
420                 {
421                     os << " OK";
422                     if (sr->numberOfRecords)
423                         os << " " << *(sr->numberOfRecords);
424                     else
425                         os << " -";
426                     //if (sr->num_records)
427                     os << " " << (sr->num_records);
428                     //else
429                     //os << " -";
430                     if (sr->nextRecordPosition)
431                         os << " " << *(sr->nextRecordPosition);
432                     else
433                         os << " -";
434                 } 
435                 else
436                 {
437                     os << " DIAG";
438                     if (sr->diagnostics && sr->diagnostics->uri)
439                         os << " " << (sr->diagnostics->uri);
440                     else
441                         os << " -";
442                     if (sr->diagnostics && sr->diagnostics->message)
443                         os << " " << (sr->diagnostics->message);
444                     else
445                         os << " -";
446                     if (sr->diagnostics && sr->diagnostics->details)
447                         os << " " << (sr->diagnostics->details);
448                     else
449                         os << " -";
450                 }
451                 
452                     
453             }
454         }
455         break;
456     case  Z_SRW_explain_request:
457         os << " " << "explainRequest";
458         break;
459     case  Z_SRW_explain_response:
460         os << " " << "explainResponse";
461         break;
462     case  Z_SRW_scan_request:
463         os << " " << "scanRequest";
464         break;
465     case  Z_SRW_scan_response:
466         os << " " << "scanResponse";
467         break;
468     case  Z_SRW_update_request:
469         os << " " << "updateRequest";
470         break;
471     case  Z_SRW_update_response:
472         os << " " << "updateResponse";
473         break;
474     default: 
475         os << " " << "UNKNOWN";    
476     }
477
478     return os;    
479 }
480
481
482
483
484 // mp_util::SRU::SRU_protocol_type
485 // mp_util::SRU::protocol(const Z_HTTP_Request &http_req) const
486 // {
487 //     const std::string mime_urlencoded("application/x-www-form-urlencoded");
488 //     const std::string mime_text_xml("text/xml");
489 //     const std::string mime_soap_xml("application/soap+xml");
490
491 //     const std::string http_method(http_req.method);
492 //     const std::string http_type 
493 //         =  mp_util::http_header_value(http_req.headers, "Content-Type");
494
495 //     if (http_method == "GET")
496 //         return SRU_GET;
497
498 //     if (http_method == "POST"
499 //               && http_type  == mime_urlencoded)
500 //         return SRU_POST;
501     
502 //     if ( http_method == "POST"
503 //          && (http_type  == mime_text_xml
504 //              || http_type  == mime_soap_xml))
505 //         return SRU_SOAP;
506
507 //     return SRU_NONE;
508 // }
509
510 // std::string 
511 // mp_util::sru_protocol(const Z_HTTP_Request &http_req) const
512 // {
513 //     const std::string mime_urlencoded("application/x-www-form-urlencoded");
514 //     const std::string mime_text_xml("text/xml");
515 //     const std::string mime_soap_xml("application/soap+xml");
516
517 //     const std::string http_method(http_req.method);
518 //     const std::string http_type 
519 //         =  mp_util::http_header_value(http_req.headers, "Content-Type");
520
521 //     if (http_method == "GET")
522 //         return "SRU GET";
523
524 //     if (http_method == "POST"
525 //               && http_type  == mime_urlencoded)
526 //         return "SRU POST";
527     
528 //     if ( http_method == "POST"
529 //          && (http_type  == mime_text_xml
530 //              || http_type  == mime_soap_xml))
531 //         return "SRU SOAP";
532
533 //     return "HTTP";
534 // }
535
536 // std::string 
537 // mp_util::debug_http(const Z_HTTP_Request &http_req) const
538 // {
539 //     std::string message("<html>\n<body>\n<h1>"
540 //                         "Metaproxy SRUtoZ3950 filter"
541 //                         "</h1>\n");
542     
543 //     message += "<h3>HTTP Info</h3><br/>\n";
544 //     message += "<p>\n";
545 //     message += "<b>Method: </b> " + std::string(http_req.method) + "<br/>\n";
546 //     message += "<b>Version:</b> " + std::string(http_req.version) + "<br/>\n";
547 //     message += "<b>Path:   </b> " + std::string(http_req.path) + "<br/>\n";
548
549 //     message += "<b>Content-Type:</b>"
550 //         + mp_util::http_header_value(http_req.headers, "Content-Type")
551 //         + "<br/>\n";
552 //     message += "<b>Content-Length:</b>"
553 //         + mp_util::http_header_value(http_req.headers, "Content-Length")
554 //         + "<br/>\n";
555 //     message += "</p>\n";    
556     
557 //     message += "<h3>Headers</h3><br/>\n";
558 //     message += "<p>\n";    
559 //     Z_HTTP_Header* header = http_req.headers;
560 //     while (header){
561 //         message += "<b>Header: </b> <i>" 
562 //             + std::string(header->name) + ":</i> "
563 //             + std::string(header->value) + "<br/>\n";
564 //         header = header->next;
565 //     }
566 //     message += "</p>\n";    
567 //     message += "</body>\n</html>\n";
568 //     return message;
569 // }
570
571 // void mp_util::http_response(metaproxy_1::Package &package, 
572 //                                         const std::string &content, 
573 //                                         int http_code) const
574 // {
575
576 //     Z_GDU *zgdu_req = package.request().get(); 
577 //     Z_GDU *zgdu_res = 0; 
578 //     mp::odr odr;
579 //     zgdu_res 
580 //        = odr.create_HTTP_Response(package.session(), 
581 //                                   zgdu_req->u.HTTP_Request, 
582 //                                   http_code);
583         
584 //     zgdu_res->u.HTTP_Response->content_len = content.size();
585 //     zgdu_res->u.HTTP_Response->content_buf 
586 //         = (char*) odr_malloc(odr, zgdu_res->u.HTTP_Response->content_len);
587     
588 //     strncpy(zgdu_res->u.HTTP_Response->content_buf, 
589 //             content.c_str(),  zgdu_res->u.HTTP_Response->content_len);
590     
591 //     //z_HTTP_header_add(odr, &hres->headers,
592 //     //                  "Content-Type", content_type.c_str());
593 //     package.response() = zgdu_res;
594 // }
595
596 /*
597  * Local variables:
598  * c-basic-offset: 4
599  * indent-tabs-mode: nil
600  * c-file-style: "stroustrup"
601  * End:
602  * vim: shiftwidth=4 tabstop=8 expandtab
603  */