sending explain even when decoding of SRU package failed.
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
1 /* $Id: filter_sru_to_z3950.cpp,v 1.18 2006-10-02 12:17:54 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 "filter.hpp"
9 #include "package.hpp"
10 #include "util.hpp"
11 #include "gduutil.hpp"
12 #include "sru_util.hpp"
13 #include "filter_sru_to_z3950.hpp"
14
15 #include <yaz/zgdu.h>
16 #include <yaz/z-core.h>
17 #include <yaz/srw.h>
18 #include <yaz/pquery.h>
19
20 #include <boost/thread/mutex.hpp>
21
22 #include <iostream>
23 #include <sstream>
24 #include <string>
25
26
27 namespace mp = metaproxy_1;
28 namespace mp_util = metaproxy_1::util;
29 namespace yf = mp::filter;
30
31
32 namespace metaproxy_1 {
33     namespace filter {
34         class SRUtoZ3950::Impl {
35         public:
36             void configure(const xmlNode *xmlnode);
37             void process(metaproxy_1::Package &package) const;
38         private:
39             union SRW_query {char * cql; char * xcql; char * pqf;};
40             typedef const int& SRW_query_type;
41         private:
42             std::string sru_protocol(const Z_HTTP_Request &http_req) const;
43             std::string debug_http(const Z_HTTP_Request &http_req) const;
44             void http_response(mp::Package &package, 
45                                const std::string &content, 
46                                int http_code = 200) const;
47             bool build_sru_debug_package(mp::Package &package) const;
48             bool build_simple_explain(mp::Package &package, 
49                                       mp::odr &odr_en,
50                                       Z_SRW_PDU *sru_pdu_res,
51                                       Z_SRW_explainRequest const *er_req) 
52                 const;
53             bool build_sru_response(mp::Package &package, 
54                                     mp::odr &odr_en,
55                                     Z_SOAP *soap,
56                                     const Z_SRW_PDU *sru_pdu_res,
57                                     char *charset,
58                                     const char *stylesheet) const;
59             Z_SRW_PDU * decode_sru_request(mp::Package &package,   
60                                            mp::odr &odr_de,
61                                            mp::odr &odr_en,
62                                            Z_SRW_PDU *sru_pdu_res,
63                                            Z_SOAP *&soap,
64                                            char *charset,
65                                            char *stylesheet) const;
66             bool check_sru_query_exists(mp::Package &package,
67                                        mp::odr &odr_en,
68                                        Z_SRW_PDU *sru_pdu_res,
69                                        Z_SRW_searchRetrieveRequest 
70                                        const *sr_req) const;
71             bool z3950_build_query(mp::odr &odr_en, Z_Query *z_query, 
72                                    const SRW_query &query, 
73                                    SRW_query_type query_type) const;
74             bool z3950_init_request(mp::Package &package, 
75                                          const std::string 
76                                          &database = "Default") const;
77             bool z3950_close_request(mp::Package &package) const;
78             bool z3950_search_request(mp::Package &package,
79                                       mp::odr &odr_en,
80                                       Z_SRW_PDU *sru_pdu_res,
81                                       Z_SRW_searchRetrieveRequest 
82                                           const *sr_req) const;
83             bool z3950_present_request(mp::Package &package,
84                                        mp::odr &odr_en,
85                                        Z_SRW_PDU *sru_pdu_res,
86                                        Z_SRW_searchRetrieveRequest 
87                                        const *sr_req) const;
88             bool z3950_scan_request(mp::Package &package,
89                                     mp::odr &odr_en,
90                                     Z_SRW_PDU *sru_pdu_res,
91                                     Z_SRW_scanRequest 
92                                     const *sr_req) const;
93             Z_ElementSetNames * build_esn_from_schema(mp::odr &odr_en, 
94                                                       const char *schema) 
95                 const;
96             int z3950_to_srw_diag(mp::odr &odr_en, 
97                                   Z_SRW_searchRetrieveResponse *srw_res,
98                                   Z_DefaultDiagFormat *ddf) const;
99         };
100     }
101 }
102
103 yf::SRUtoZ3950::SRUtoZ3950() : m_p(new Impl)
104 {
105 }
106
107 yf::SRUtoZ3950::~SRUtoZ3950()
108 {  // must have a destructor because of boost::scoped_ptr
109 }
110
111 void yf::SRUtoZ3950::configure(const xmlNode *xmlnode)
112 {
113     m_p->configure(xmlnode);
114 }
115
116 void yf::SRUtoZ3950::process(mp::Package &package) const
117 {
118     m_p->process(package);
119 }
120
121 void yf::SRUtoZ3950::Impl::configure(const xmlNode *xmlnode)
122 {
123 }
124
125 void yf::SRUtoZ3950::Impl::process(mp::Package &package) const
126 {
127     Z_GDU *zgdu_req = package.request().get();
128
129     // ignoring all non HTTP_Request packages
130     if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
131         package.move();
132         return;
133     }
134     
135     // only working on  HTTP_Request packages now
136
137
138     // TODO: Z3950 response parsing and translation to SRU package
139     bool ok = true;    
140
141     mp::odr odr_de(ODR_DECODE);
142     Z_SRW_PDU *sru_pdu_req = 0;
143
144     mp::odr odr_en(ODR_ENCODE);
145     //Z_SRW_PDU *sru_pdu_res = 0;
146     Z_SRW_PDU *sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
147
148     Z_SOAP *soap = 0;
149     char *charset = 0;
150     char *stylesheet = 0;
151
152     if (! (sru_pdu_req = decode_sru_request(package, odr_de, odr_en, 
153                                             sru_pdu_res, soap,
154                                             charset, stylesheet)))
155     {
156         build_simple_explain(package, odr_en, sru_pdu_res, 0);
157         build_sru_response(package, odr_en, soap, 
158                            sru_pdu_res, charset, stylesheet);
159         package.session().close();
160         return;
161     }
162     
163     
164     // SRU request package translation to Z3950 package
165     if (sru_pdu_req)
166         std::cout << *sru_pdu_req << "\n";
167     else
168         std::cout << "SRU empty\n";
169     
170
171     // explain
172     if (sru_pdu_req && sru_pdu_req->which == Z_SRW_explain_request)
173     {
174         Z_SRW_explainRequest *er_req = sru_pdu_req->u.explain_request;
175         //sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
176
177         build_simple_explain(package, odr_en, sru_pdu_res, er_req);
178     }
179
180     // searchRetrieve
181     else if (sru_pdu_req 
182         && sru_pdu_req->which == Z_SRW_searchRetrieve_request
183         && sru_pdu_req->u.request)
184     {
185         Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;   
186         
187         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_searchRetrieve_response);
188
189         // checking that we have a query
190         ok = check_sru_query_exists(package, odr_en, sru_pdu_res, sr_req);
191
192         if (ok && z3950_init_request(package))
193         {
194             {
195                 ok = z3950_search_request(package, odr_en,
196                                           sru_pdu_res, sr_req);
197
198                 if (ok 
199                     && sru_pdu_res->u.response->numberOfRecords
200                     && *(sru_pdu_res->u.response->numberOfRecords)
201                     && sr_req->maximumRecords
202                     && *(sr_req->maximumRecords))
203                     
204                     ok = z3950_present_request(package, odr_en,
205                                                sru_pdu_res,
206                                                sr_req);
207                 z3950_close_request(package);
208             }
209         }
210     }
211
212     // scan
213     else if (sru_pdu_req 
214              && sru_pdu_req->which == Z_SRW_scan_request
215              && sru_pdu_req->u.scan_request)
216     {
217         Z_SRW_scanRequest  *sr_req = sru_pdu_req->u.scan_request;   
218
219         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_scan_response);
220         
221         if (z3950_init_request(package))
222         {
223             z3950_scan_request(package, odr_en, sru_pdu_res, sr_req);    
224             z3950_close_request(package);
225         }        
226     }
227     else
228     {
229         std::cout << "SRU OPERATION NOT SUPPORTED \n";
230         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
231         
232         // TODO: make nice diagnostic return package 
233         package.session().close();
234         return;
235     }
236
237     //build_sru_debug_package(package);
238     build_sru_response(package, odr_en, soap, 
239                        sru_pdu_res, charset, stylesheet);
240     return;
241
242
243
244
245
246
247
248 }
249
250
251 bool yf::SRUtoZ3950::Impl::build_simple_explain(mp::Package &package, 
252                                                mp::odr &odr_en,
253                                                Z_SRW_PDU *sru_pdu_res,
254                                                Z_SRW_explainRequest 
255                                                const *er_req) const
256 {
257
258  
259
260     // z3950'fy recordPacking
261     int record_packing = Z_SRW_recordPacking_XML;
262     if (er_req && er_req->recordPacking && 's' == *(er_req->recordPacking))
263         record_packing = Z_SRW_recordPacking_string;
264
265     // getting database info
266     std::string database("Default");
267     if (er_req && er_req->database)
268         database = er_req->database;
269
270     // getting host and port info
271     std::string host = package.origin().listen_host();
272     std::string port = mp_util::to_string(package.origin().listen_port());
273
274     // overwriting host and port info if set from HTTP Host header
275     Z_GDU *zgdu_req = package.request().get();
276     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
277     {
278         Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
279         if (http_req)
280         {
281             std::string http_host_address
282                 = mp_util::http_header_value(http_req->headers, "Host");
283
284             std::string::size_type i = http_host_address.rfind(":");
285             if (i != std::string::npos)
286             {
287                 host.assign(http_host_address, 0, i);
288                 port.assign(http_host_address, i + 1, std::string::npos);
289             }
290         }
291     }
292
293     // building SRU explain record
294     std::string explain_xml 
295         = mp_util::to_string(
296             "<explain>\n"
297             "  <serverInfo protocol='SRU'>\n"
298             "  <host>")
299         + host
300         + mp_util::to_string("</host>\n"
301             "  <port>")
302         + port
303         + mp_util::to_string("</port>\n"
304             "  <database>")
305         + database
306         + mp_util::to_string("</database>\n"
307             "  </serverInfo>\n"
308             "</explain>\n");
309     
310     
311     // preparing explain record insert
312     Z_SRW_explainResponse *sru_res = sru_pdu_res->u.explain_response;
313     //sru_res->record 
314     //    = (Z_SRW_record *) odr_malloc(odr_en, sizeof(Z_SRW_record));
315     
316     // inserting one and only explain record
317     
318     sru_res->record.recordPosition = odr_intdup(odr_en, 1);
319     sru_res->record.recordPacking = record_packing;
320     sru_res->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
321     sru_res->record.recordData_len = 1 + explain_xml.size();
322     sru_res->record.recordData_buf
323         = odr_strdupn(odr_en, (const char *)explain_xml.c_str(), 
324                       1 + explain_xml.size());
325
326     return true;
327 };
328
329
330 bool yf::SRUtoZ3950::Impl::build_sru_debug_package(mp::Package &package) const
331 {
332     Z_GDU *zgdu_req = package.request().get();
333     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
334     {    
335         Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
336         std::string content = debug_http(*http_req);
337         int http_code = 400;    
338         http_response(package, content, http_code);
339         return true;
340     }
341     package.session().close();
342     return false;
343 }
344
345
346 bool yf::SRUtoZ3950::Impl::build_sru_response(mp::Package &package, 
347                                              mp::odr &odr_en,
348                                              Z_SOAP *soap,
349                                              const Z_SRW_PDU *sru_pdu_res,
350                                              char *charset,
351                                              const char *stylesheet) 
352     const
353 {
354
355     // SRU request package translation to Z3950 package
356     if (sru_pdu_res)
357         std::cout << *(const_cast<Z_SRW_PDU *>(sru_pdu_res)) << "\n";
358     else
359         std::cout << "SRU empty\n";
360
361     
362     Z_GDU *zgdu_req = package.request().get();
363     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
364     {    
365         Z_GDU *zgdu_res //= z_get_HTTP_Response(odr_en, 200);
366             = odr_en.create_HTTP_Response(package.session(), 
367                                           zgdu_req->u.HTTP_Request, 
368                                           200);
369
370         // adding HTTP response code and headers
371         Z_HTTP_Response * http_res = zgdu_res->u.HTTP_Response;
372         //http_res->code = http_code;
373         
374         std::string ctype("text/xml");
375         if (charset){
376             ctype += "; charset=";
377             ctype += charset;
378         }
379
380         z_HTTP_header_add(odr_en, 
381                           &http_res->headers, "Content-Type", ctype.c_str());
382
383          // packaging Z_SOAP into HTML response
384          static Z_SOAP_Handler soap_handlers[4] = {
385               {"http://www.loc.gov/zing/srw/", 0,
386                (Z_SOAP_fun) yaz_srw_codec},
387               {"http://www.loc.gov/zing/srw/v1.0/", 0,
388                (Z_SOAP_fun) yaz_srw_codec},
389               {"http://www.loc.gov/zing/srw/update/", 0,
390                (Z_SOAP_fun) yaz_ucp_codec},
391               {0, 0, 0}
392           };
393
394
395          // empty stylesheet means NO stylesheet
396          if (stylesheet && *stylesheet == '\0')
397              stylesheet = 0;
398          
399          // encoding SRU package
400          
401          soap->u.generic->p  = (void*) sru_pdu_res;         
402          //int ret = 
403          z_soap_codec_enc_xsl(odr_en, &soap, 
404                               &http_res->content_buf, &http_res->content_len,
405                               soap_handlers, charset, stylesheet);
406          
407
408          package.response() = zgdu_res;
409          return true;
410     }
411     package.session().close();
412     return false;
413 }
414
415
416
417  Z_SRW_PDU * yf::SRUtoZ3950::Impl::decode_sru_request(mp::Package &package,
418                                                      mp::odr &odr_de,
419                                                      mp::odr &odr_en,
420                                                      Z_SRW_PDU *sru_pdu_res,
421                                                      Z_SOAP *&soap,
422                                                      char *charset,
423                                                      char *stylesheet) 
424      const
425 {
426     Z_GDU *zgdu_req = package.request().get();
427     Z_SRW_PDU *sru_pdu_req = 0;
428
429     assert((zgdu_req->which == Z_GDU_HTTP_Request));
430     
431     //ignoring all non HTTP_Request packages
432     //if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
433     //    return 0;
434     //}
435     
436     Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
437     if (! http_req)
438         return 0;
439
440     //Z_SRW_PDU *sru_pdu_res_exp = yaz_srw_get(odr_en, Z_SRW_explain_response);
441     
442     if (0 == yaz_sru_decode(http_req, &sru_pdu_req, &soap, 
443                             odr_de, &charset, 
444                             &(sru_pdu_res->u.response->diagnostics), 
445                             &(sru_pdu_res->u.response->num_diagnostics)))
446     {
447         if (sru_pdu_res->u.response->num_diagnostics)
448         {
449             //sru_pdu_res = sru_pdu_res_exp;
450             package.session().close();
451             return 0;
452         }
453         return sru_pdu_req;
454     }
455     else if (0 == yaz_srw_decode(http_req, &sru_pdu_req, &soap, 
456                                  odr_de, &charset))
457         return sru_pdu_req;
458     else 
459     {
460         //sru_pdu_res = sru_pdu_res_exp;
461         package.session().close();
462         return 0;
463     }
464     return 0;
465 }
466
467 bool 
468 yf::SRUtoZ3950::Impl::check_sru_query_exists(mp::Package &package, 
469                                             mp::odr &odr_en,
470                                             Z_SRW_PDU *sru_pdu_res, 
471                                             Z_SRW_searchRetrieveRequest 
472                                                               const *sr_req)
473     const
474 {
475     if( (sr_req->query_type == Z_SRW_query_type_cql && !sr_req->query.cql) )
476     {
477         yaz_add_srw_diagnostic(odr_en,
478                                &(sru_pdu_res->u.response->diagnostics), 
479                                &(sru_pdu_res->u.response->num_diagnostics), 
480                                7, "query");
481         yaz_add_srw_diagnostic(odr_en,
482                                &(sru_pdu_res->u.response->diagnostics), 
483                                &(sru_pdu_res->u.response->num_diagnostics), 
484                                10, "CQL query is empty");
485         return false;
486     }
487     if( (sr_req->query_type == Z_SRW_query_type_xcql && !sr_req->query.xcql) )
488     {
489          yaz_add_srw_diagnostic(odr_en,
490                                &(sru_pdu_res->u.response->diagnostics), 
491                                &(sru_pdu_res->u.response->num_diagnostics), 
492                                10, "XCQL query is empty");
493          return false;
494    }
495     if( (sr_req->query_type == Z_SRW_query_type_pqf && !sr_req->query.pqf) )
496     {
497         yaz_add_srw_diagnostic(odr_en,
498                                &(sru_pdu_res->u.response->diagnostics), 
499                                &(sru_pdu_res->u.response->num_diagnostics), 
500                                10, "PQF query is empty");
501         return false;
502     }
503     return true;
504 };
505
506
507
508 bool 
509 yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package, 
510                                              const std::string &database) const
511 {
512     // prepare Z3950 package
513     //Session s;
514     //Package z3950_package(s, package.origin());
515     Package z3950_package(package.session(), package.origin());
516     z3950_package.copy_filter(package);
517
518     // set initRequest APDU
519     mp::odr odr_en(ODR_ENCODE);
520     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
521     Z_InitRequest *init_req = apdu->u.initRequest;
522     //TODO: add user name in apdu
523     //TODO: add user passwd in apdu
524     //init_req->idAuthentication = org_init->idAuthentication;
525     //init_req->implementationId = "IDxyz";
526     //init_req->implementationName = "NAMExyz";
527     //init_req->implementationVersion = "VERSIONxyz";
528
529     ODR_MASK_SET(init_req->options, Z_Options_search);
530     ODR_MASK_SET(init_req->options, Z_Options_present);
531     ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
532     ODR_MASK_SET(init_req->options, Z_Options_scan);
533
534     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
535     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
536     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
537
538     z3950_package.request() = apdu;
539
540     // send Z3950 package
541     // std::cout << "z3950_init_request " << *apdu <<"\n";
542     z3950_package.move();
543
544     // dead Z3950 backend detection
545     if (z3950_package.session().is_closed()){
546         package.session().close();
547         return false;
548     }
549
550     // check successful initResponse
551     Z_GDU *z3950_gdu = z3950_package.response().get();
552
553     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
554         && z3950_gdu->u.z3950->which == Z_APDU_initResponse)
555          return true;
556  
557     return false;
558 }
559
560 bool 
561 yf::SRUtoZ3950::Impl::z3950_close_request(mp::Package &package) const
562 {
563     // close SRU package
564     package.session().close();
565
566     // prepare and close Z3950 package 
567     Package z3950_package(package.session(), package.origin());
568     z3950_package.copy_filter(package);
569     z3950_package.session().close();
570
571     // set close APDU
572     //mp::odr odr_en(ODR_ENCODE);
573     //Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_close);
574     //z3950_package.request() = apdu;
575
576     z3950_package.move();
577
578     // check successful close response
579     //Z_GDU *z3950_gdu = z3950_package.response().get();
580     //if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
581     //    && z3950_gdu->u.z3950->which == Z_APDU_close)
582     //    return true;
583
584     if (z3950_package.session().is_closed()){
585         return true;
586     }
587     return false;
588 }
589
590 bool 
591 yf::SRUtoZ3950::Impl::z3950_search_request(mp::Package &package,  
592                                           mp::odr &odr_en,
593                                           Z_SRW_PDU *sru_pdu_res,
594                                           Z_SRW_searchRetrieveRequest 
595                                           const *sr_req) const
596 {
597     Package z3950_package(package.session(), package.origin());
598     z3950_package.copy_filter(package);
599
600     //add stuff in z3950 apdu
601     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
602     Z_SearchRequest *z_searchRequest = apdu->u.searchRequest;
603
604     // z3950'fy database
605     z_searchRequest->num_databaseNames = 1;
606     z_searchRequest->databaseNames = (char**)
607         odr_malloc(odr_en, sizeof(char *));
608
609     if (sr_req->database)
610         z_searchRequest->databaseNames[0] 
611             = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
612     else
613         z_searchRequest->databaseNames[0] 
614             = odr_strdup(odr_en, "Default");
615
616
617     // z3950'fy query
618     Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
619     z_searchRequest->query = z_query;
620  
621     if (!z3950_build_query(odr_en, z_query, 
622                            (const SRW_query&)sr_req->query, 
623                            sr_req->query_type))
624     {    
625         //send_to_srw_client_error(7, "query");
626         return false;
627     }
628
629     z3950_package.request() = apdu;
630     //std::cout << "z3950_search_request " << *apdu << "\n";
631         
632     z3950_package.move();
633
634
635     Z_GDU *z3950_gdu = z3950_package.response().get();
636     //std::cout << "z3950_search_request " << *z3950_gdu << "\n";
637
638     //TODO: check success condition
639
640     //int yaz_diag_bib1_to_srw (int bib1_code);
641     //int yaz_diag_srw_to_bib1(int srw_code);
642     //Se kode i src/seshigh.c (srw_bend_search, srw_bend_init).
643
644     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
645         && z3950_gdu->u.z3950->which == Z_APDU_searchResponse
646         && z3950_gdu->u.z3950->u.searchResponse->searchStatus)
647     {
648
649         Z_SearchResponse *sr = z3950_gdu->u.z3950->u.searchResponse;
650         if (sr)
651         {
652             // srw'fy number of records
653             sru_pdu_res->u.response->numberOfRecords 
654                 = (int *) odr_malloc(odr_en, sizeof(int *));
655             *(sru_pdu_res->u.response->numberOfRecords) = *(sr->resultCount);
656
657             // srw'fy nextRecordPosition
658             //sru_pdu_res->u.response->nextRecordPosition 
659             //    = (int *) odr_malloc(odr_en, sizeof(int *));
660             //*(sru_pdu_res->u.response->nextRecordPosition) = 1;
661
662         }
663
664         return true;
665     }
666     
667     return false;
668 }
669
670 bool 
671 yf::SRUtoZ3950::Impl::z3950_present_request(mp::Package &package, 
672                                            mp::odr &odr_en,
673                                            Z_SRW_PDU *sru_pdu_res,
674                                            Z_SRW_searchRetrieveRequest 
675                                            const *sr_req)
676     const
677 {
678
679     if (!sr_req)
680         return false;
681
682     
683     // no need to work if nobody wants record ..
684     if (!(sr_req->maximumRecords) || 0 == *(sr_req->maximumRecords))
685         return true;
686
687     // creating Z3950 package
688     Package z3950_package(package.session(), package.origin());
689     z3950_package.copy_filter(package); 
690     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
691
692     assert(apdu->u.presentRequest);
693
694     bool send_z3950_present = true;
695
696     // recordXPath unsupported.
697     if (sr_req->recordXPath)
698     {
699         send_z3950_present = false;
700         yaz_add_srw_diagnostic(odr_en,
701                                &(sru_pdu_res->u.response->diagnostics), 
702                                &(sru_pdu_res->u.response->num_diagnostics), 
703                                72, 0);
704     }
705     
706     // resultSetTTL unsupported.
707     // resultSetIdleTime in response
708     if (sr_req->resultSetTTL)
709     {
710         send_z3950_present = false;
711         yaz_add_srw_diagnostic(odr_en,
712                                &(sru_pdu_res->u.response->diagnostics), 
713                                &(sru_pdu_res->u.response->num_diagnostics), 
714                                50, 0);
715     }
716     
717     // sort unsupported
718     if (sr_req->sort_type != Z_SRW_sort_type_none)
719     {
720         send_z3950_present = false;
721         yaz_add_srw_diagnostic(odr_en,
722                                &(sru_pdu_res->u.response->diagnostics), 
723                                &(sru_pdu_res->u.response->num_diagnostics), 
724                                80, 0);
725     }
726     
727     // start record requested larger than number of records
728     if (sr_req->startRecord 
729         && sru_pdu_res->u.response->numberOfRecords
730         && *(sr_req->startRecord) > *(sru_pdu_res->u.response->numberOfRecords))
731     {
732         //          = *(sr_req->startRecord);
733         send_z3950_present = false;
734         yaz_add_srw_diagnostic(odr_en,
735                                &(sru_pdu_res->u.response->diagnostics), 
736                                &(sru_pdu_res->u.response->num_diagnostics), 
737                                61, 0);
738     }    
739
740     // exit on all these above diagnostics
741     if (!send_z3950_present)
742         return false;
743
744     // now packaging the z3950 present request
745
746     // z3950'fy start record position
747     if (sr_req->startRecord)
748         *(apdu->u.presentRequest->resultSetStartPoint) 
749             = *(sr_req->startRecord);
750     else 
751         *(apdu->u.presentRequest->resultSetStartPoint) = 1;
752     
753     // z3950'fy number of records requested 
754     if (sr_req->maximumRecords)
755         *(apdu->u.presentRequest->numberOfRecordsRequested) 
756             = *(sr_req->maximumRecords);
757      
758     // z3950'fy recordPacking
759     int record_packing = Z_SRW_recordPacking_XML;
760     if (sr_req->recordPacking && 's' == *(sr_req->recordPacking))
761         record_packing = Z_SRW_recordPacking_string;
762
763     // RecordSyntax will always be XML
764     (apdu->u.presentRequest->preferredRecordSyntax)
765         = yaz_oidval_to_z3950oid (odr_en, CLASS_RECSYN, VAL_TEXT_XML);
766
767     // z3950'fy record schema
768      if (sr_req->recordSchema)
769      {
770          apdu->u.presentRequest->recordComposition 
771              = (Z_RecordComposition *) odr_malloc(odr_en, sizeof(Z_RecordComposition));
772          apdu->u.presentRequest->recordComposition->which 
773              = Z_RecordComp_simple;
774          apdu->u.presentRequest->recordComposition->u.simple 
775              = build_esn_from_schema(odr_en, (const char *) sr_req->recordSchema); 
776      }
777
778     // z3950'fy time to live - flagged as diagnostics above
779     //if (sr_req->resultSetTTL)
780
781     // attaching Z3950 package to filter chain
782     z3950_package.request() = apdu;
783
784     //std::cout << "z3950_present_request " << *apdu << "\n";   
785     z3950_package.move();
786
787     //check successful Z3950 present response
788     Z_GDU *z3950_gdu = z3950_package.response().get();
789     if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950 
790         || z3950_gdu->u.z3950->which != Z_APDU_presentResponse
791         || !z3950_gdu->u.z3950->u.presentResponse)
792
793     {
794         yaz_add_srw_diagnostic(odr_en,
795                                &(sru_pdu_res->u.response->diagnostics), 
796                                &(sru_pdu_res->u.response->num_diagnostics), 
797                                2, 0);
798         package.session().close();
799         return false;
800     }
801     
802
803     // everything fine, continuing
804     //std::cout << "z3950_present_request OK\n";
805
806     Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
807     Z_SRW_searchRetrieveResponse *sru_res = sru_pdu_res->u.response;
808         
809     // checking non surrogate dioagnostics in Z3950 present response package
810     if (pr->records 
811         && pr->records->which == Z_Records_NSD
812         && pr->records->u.nonSurrogateDiagnostic)
813     {
814         //int http_code =
815         z3950_to_srw_diag(odr_en, sru_res, pr->records->u.nonSurrogateDiagnostic);
816         return false;
817     }
818     
819     // copy all records if existing
820     if (pr->records && pr->records->which == Z_Records_DBOSD)
821     {
822         // srw'fy number of returned records
823         sru_res->num_records
824             = pr->records->u.databaseOrSurDiagnostics->num_records;
825         
826         sru_res->records 
827             = (Z_SRW_record *) odr_malloc(odr_en, 
828                                           sru_res->num_records *sizeof(Z_SRW_record));
829         
830         // srw'fy nextRecordPosition
831         if (pr->nextResultSetPosition)
832             sru_res->nextRecordPosition 
833                 = odr_intdup(odr_en, *(pr->nextResultSetPosition));
834
835         // inserting all records
836         for (int i = 0; i < sru_res->num_records; i++)
837         {
838             Z_NamePlusRecord *npr 
839                 = pr->records->u.databaseOrSurDiagnostics->records[i];
840             
841             sru_res->records[i].recordPosition 
842                 = odr_intdup(odr_en, i + *(apdu->u.presentRequest->resultSetStartPoint));
843             
844             sru_res->records[i].recordPacking = record_packing;
845             
846             if (npr->which != Z_NamePlusRecord_databaseRecord)
847             {
848                 sru_res->records[i].recordSchema = "diagnostic";
849                 sru_res->records[i].recordData_buf = "67";
850                 sru_res->records[i].recordData_len = 2;
851             }
852             else
853             {
854                 Z_External *r = npr->u.databaseRecord;
855                 oident *ent = oid_getentbyoid(r->direct_reference);
856                 if (r->which == Z_External_octet 
857                     && ent->value == VAL_TEXT_XML)
858                 {
859                     sru_res->records[i].recordSchema = "dc";
860                     sru_res->records[i].recordData_buf
861                         = odr_strdupn(odr_en, 
862                                       (const char *)r->u.octet_aligned->buf, 
863                                       r->u.octet_aligned->len);
864                     sru_res->records[i].recordData_len 
865                         = r->u.octet_aligned->len;
866                 }
867                 else
868                 {
869                     sru_res->records[i].recordSchema = "diagnostic";
870                     sru_res->records[i].recordData_buf = "67";
871                     sru_res->records[i].recordData_len = 2;
872                 }
873             }   
874         }    
875     }
876     
877     return true;
878 }
879
880 bool 
881 yf::SRUtoZ3950::Impl::z3950_scan_request(mp::Package &package,
882                                         mp::odr &odr_en,
883                                         Z_SRW_PDU *sru_pdu_res,
884                                         Z_SRW_scanRequest const *sr_req) const 
885 {
886     Package z3950_package(package.session(), package.origin());
887     z3950_package.copy_filter(package); 
888     //mp::odr odr_en(ODR_ENCODE);
889     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
890
891     //TODO: add stuff in apdu
892     Z_ScanRequest *z_scanRequest = apdu->u.scanRequest;
893
894     // database repackaging
895     z_scanRequest->num_databaseNames = 1;
896     z_scanRequest->databaseNames = (char**)
897         odr_malloc(odr_en, sizeof(char *));
898     if (sr_req->database)
899         z_scanRequest->databaseNames[0] 
900             = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
901     else
902         z_scanRequest->databaseNames[0] 
903             = odr_strdup(odr_en, "Default");
904
905
906     // query repackaging
907     // CQL or XCQL scan is not possible in Z3950, flagging a diagnostic
908     if (sr_req->query_type != Z_SRW_query_type_pqf)
909     {        
910         //send_to_srw_client_error(7, "query");
911         return false;
912     }
913
914     // PQF query repackaging
915     // need to use Z_AttributesPlusTerm structure, not Z_Query
916     // this can be digget out of a 
917     // Z_query->type1(Z_RPNQuery)->RPNStructure(Z_RPNStructure)
918     //   ->u.simple(Z_Operand)->u.attributesPlusTerm(Z_AttributesPlusTerm )
919
920     //Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
921     //z_searchRequest->query = z_query;
922
923     //if (!z3950_build_query(odr_en, z_query, 
924     //                       (const SRW_query&)sr_req->query, 
925     //                       sr_req->query_type))
926     //{    
927         //send_to_srw_client_error(7, "query");
928     //    return false;
929     //}
930
931     // TODO: 
932
933     z3950_package.request() = apdu;
934     std::cout << "z3950_scan_request " << *apdu << "\n";   
935
936     z3950_package.move();
937     //TODO: check success condition
938     return true;
939     return false;
940 }
941
942 bool yf::SRUtoZ3950::Impl::z3950_build_query(mp::odr &odr_en, Z_Query *z_query, 
943                                             const SRW_query &query, 
944                                             SRW_query_type query_type) const
945 {        
946     if (query_type == Z_SRW_query_type_cql)
947     {
948         Z_External *ext = (Z_External *) 
949             odr_malloc(odr_en, sizeof(*ext));
950         ext->direct_reference = 
951             odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
952         ext->indirect_reference = 0;
953         ext->descriptor = 0;
954         ext->which = Z_External_CQL;
955         ext->u.cql = const_cast<char *>(query.cql);
956         
957         z_query->which = Z_Query_type_104;
958         z_query->u.type_104 =  ext;
959         return true;
960     }
961
962     if (query_type == Z_SRW_query_type_pqf)
963     {
964         Z_RPNQuery *RPNquery;
965         YAZ_PQF_Parser pqf_parser;
966         
967         pqf_parser = yaz_pqf_create ();
968         
969         RPNquery = yaz_pqf_parse (pqf_parser, odr_en, query.pqf);
970         if (!RPNquery)
971         {
972             std::cout << "TODO: Handeling of bad PQF\n";
973             std::cout << "TODO: Diagnostic to be send\n";
974         }
975         z_query->which = Z_Query_type_1;
976         z_query->u.type_1 =  RPNquery;
977         
978         yaz_pqf_destroy(pqf_parser);
979         return true;
980     }
981     return false;
982 }
983
984
985 std::string 
986 yf::SRUtoZ3950::Impl::sru_protocol(const Z_HTTP_Request &http_req) const
987 {
988     const std::string mime_urlencoded("application/x-www-form-urlencoded");
989     const std::string mime_text_xml("text/xml");
990     const std::string mime_soap_xml("application/soap+xml");
991
992     const std::string http_method(http_req.method);
993     const std::string http_type 
994         =  mp_util::http_header_value(http_req.headers, "Content-Type");
995
996     if (http_method == "GET")
997         return "SRU GET";
998
999     if (http_method == "POST"
1000               && http_type  == mime_urlencoded)
1001         return "SRU POST";
1002     
1003     if ( http_method == "POST"
1004          && (http_type  == mime_text_xml
1005              || http_type  == mime_soap_xml))
1006         return "SRU SOAP";
1007
1008     return "HTTP";
1009 }
1010
1011 std::string 
1012 yf::SRUtoZ3950::Impl::debug_http(const Z_HTTP_Request &http_req) const
1013 {
1014     std::string message("<html>\n<body>\n<h1>"
1015                         "Metaproxy SRUtoZ3950 filter"
1016                         "</h1>\n");
1017     
1018     message += "<h3>HTTP Info</h3><br/>\n";
1019     message += "<p>\n";
1020     message += "<b>Method: </b> " + std::string(http_req.method) + "<br/>\n";
1021     message += "<b>Version:</b> " + std::string(http_req.version) + "<br/>\n";
1022     message += "<b>Path:   </b> " + std::string(http_req.path) + "<br/>\n";
1023
1024     message += "<b>Content-Type:</b>"
1025         + mp_util::http_header_value(http_req.headers, "Content-Type")
1026         + "<br/>\n";
1027     message += "<b>Content-Length:</b>"
1028         + mp_util::http_header_value(http_req.headers, "Content-Length")
1029         + "<br/>\n";
1030     message += "</p>\n";    
1031     
1032     message += "<h3>Headers</h3><br/>\n";
1033     message += "<p>\n";    
1034     Z_HTTP_Header* header = http_req.headers;
1035     while (header){
1036         message += "<b>Header: </b> <i>" 
1037             + std::string(header->name) + ":</i> "
1038             + std::string(header->value) + "<br/>\n";
1039         header = header->next;
1040     }
1041     message += "</p>\n";    
1042     message += "</body>\n</html>\n";
1043     return message;
1044 }
1045
1046 void yf::SRUtoZ3950::Impl::http_response(metaproxy_1::Package &package, 
1047                                         const std::string &content, 
1048                                         int http_code) const
1049 {
1050
1051     Z_GDU *zgdu_req = package.request().get(); 
1052     Z_GDU *zgdu_res = 0; 
1053     mp::odr odr;
1054     zgdu_res 
1055        = odr.create_HTTP_Response(package.session(), 
1056                                   zgdu_req->u.HTTP_Request, 
1057                                   http_code);
1058         
1059     zgdu_res->u.HTTP_Response->content_len = content.size();
1060     zgdu_res->u.HTTP_Response->content_buf 
1061         = (char*) odr_malloc(odr, zgdu_res->u.HTTP_Response->content_len);
1062     
1063     strncpy(zgdu_res->u.HTTP_Response->content_buf, 
1064             content.c_str(),  zgdu_res->u.HTTP_Response->content_len);
1065     
1066     //z_HTTP_header_add(odr, &hres->headers,
1067     //                  "Content-Type", content_type.c_str());
1068     package.response() = zgdu_res;
1069 }
1070
1071
1072 Z_ElementSetNames * 
1073 yf::SRUtoZ3950::Impl::build_esn_from_schema(mp::odr &odr_en, 
1074                                            const char *schema) const
1075 {
1076   if (!schema)
1077         return 0;
1078   
1079     Z_ElementSetNames *esn 
1080         = (Z_ElementSetNames *) odr_malloc(odr_en, sizeof(Z_ElementSetNames));
1081     esn->which = Z_ElementSetNames_generic;
1082     esn->u.generic = odr_strdup(odr_en, schema);
1083     return esn; 
1084 }
1085
1086 int 
1087 yf::SRUtoZ3950::Impl::z3950_to_srw_diag(mp::odr &odr_en, 
1088                                        Z_SRW_searchRetrieveResponse *sru_res,
1089                                        Z_DefaultDiagFormat *ddf) const
1090 {
1091     int bib1_code = *ddf->condition;
1092     if (bib1_code == 109)
1093         return 404;
1094     sru_res->num_diagnostics = 1;
1095     sru_res->diagnostics = (Z_SRW_diagnostic *)
1096         odr_malloc(odr_en, sizeof(*sru_res->diagnostics));
1097     yaz_mk_std_diagnostic(odr_en, sru_res->diagnostics,
1098                           yaz_diag_bib1_to_srw(*ddf->condition), 
1099                           ddf->u.v2Addinfo);
1100     return 0;
1101 }
1102
1103
1104
1105 static mp::filter::Base* filter_creator()
1106 {
1107     return new mp::filter::SRUtoZ3950;
1108 }
1109
1110 extern "C" {
1111     struct metaproxy_1_filter_struct metaproxy_1_filter_sru_to_z3950 = {
1112         0,
1113         "SRUtoZ3950",
1114         filter_creator
1115     };
1116 }
1117
1118
1119 /*
1120  * Local variables:
1121  * c-basic-offset: 4
1122  * indent-tabs-mode: nil
1123  * c-file-style: "stroustrup"
1124  * End:
1125  * vim: shiftwidth=4 tabstop=8 expandtab
1126  */