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