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