more diagnostics added
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
1 /* $Id: filter_sru_to_z3950.cpp,v 1.21 2006-10-03 12:31:26 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 #include <algorithm>
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         // we do not do scan at the moment, therefore issuing a diagnostic
222         yaz_add_srw_diagnostic(odr_en,
223                                &(sru_pdu_res->u.scan_response->diagnostics), 
224                                &(sru_pdu_res->u.scan_response->num_diagnostics), 
225                                4, "scan");
226  
227         // to be used when we do scan
228         if (false && z3950_init_request(package))
229         {
230             z3950_scan_request(package, odr_en, sru_pdu_res, sr_req);    
231             z3950_close_request(package);
232         }        
233     }
234     else
235     {
236         std::cout << "SRU OPERATION NOT SUPPORTED \n";
237         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
238         
239         // TODO: make nice diagnostic return package 
240         package.session().close();
241         return;
242     }
243
244     //build_sru_debug_package(package);
245     build_sru_response(package, odr_en, soap, 
246                        sru_pdu_res, charset, stylesheet);
247     return;
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     // z3950'fy recordPacking
258     int record_packing = Z_SRW_recordPacking_XML;
259     if (er_req && er_req->recordPacking && 's' == *(er_req->recordPacking))
260         record_packing = Z_SRW_recordPacking_string;
261
262     // getting database info
263     std::string database("Default");
264     if (er_req && er_req->database)
265         database = er_req->database;
266
267     // getting host and port info
268     std::string host = package.origin().listen_host();
269     std::string port = mp_util::to_string(package.origin().listen_port());
270
271     // overwriting host and port info if set from HTTP Host header
272     Z_GDU *zgdu_req = package.request().get();
273     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
274     {
275         Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
276         if (http_req)
277         {
278             std::string http_host_address
279                 = mp_util::http_header_value(http_req->headers, "Host");
280
281             std::string::size_type i = http_host_address.rfind(":");
282             if (i != std::string::npos)
283             {
284                 host.assign(http_host_address, 0, i);
285                 port.assign(http_host_address, i + 1, std::string::npos);
286             }
287         }
288     }
289
290     // building SRU explain record
291     std::string explain_xml 
292         = mp_util::to_string(
293             "<explain>\n"
294             "  <serverInfo protocol='SRU'>\n"
295             "  <host>")
296         + host
297         + mp_util::to_string("</host>\n"
298             "  <port>")
299         + port
300         + mp_util::to_string("</port>\n"
301             "  <database>")
302         + database
303         + mp_util::to_string("</database>\n"
304             "  </serverInfo>\n"
305             "</explain>\n");
306     
307     
308     // preparing explain record insert
309     Z_SRW_explainResponse *sru_res = sru_pdu_res->u.explain_response;
310     //sru_res->record 
311     //    = (Z_SRW_record *) odr_malloc(odr_en, sizeof(Z_SRW_record));
312     
313     // inserting one and only explain record
314     
315     sru_res->record.recordPosition = odr_intdup(odr_en, 1);
316     sru_res->record.recordPacking = record_packing;
317     sru_res->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
318     sru_res->record.recordData_len = 1 + explain_xml.size();
319     sru_res->record.recordData_buf
320         = odr_strdupn(odr_en, (const char *)explain_xml.c_str(), 
321                       1 + explain_xml.size());
322
323     return true;
324 };
325
326
327 bool yf::SRUtoZ3950::Impl::build_sru_debug_package(mp::Package &package) const
328 {
329     Z_GDU *zgdu_req = package.request().get();
330     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
331     {    
332         Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
333         std::string content = mp_util::http_headers_debug(*http_req);
334         int http_code = 400;    
335         mp_util::http_response(package, content, http_code);
336         return true;
337     }
338     package.session().close();
339     return false;
340 }
341
342
343 bool yf::SRUtoZ3950::Impl::build_sru_response(mp::Package &package, 
344                                              mp::odr &odr_en,
345                                              Z_SOAP *soap,
346                                              const Z_SRW_PDU *sru_pdu_res,
347                                              char *charset,
348                                              const char *stylesheet) 
349     const
350 {
351
352     // SRU request package translation to Z3950 package
353     if (sru_pdu_res)
354         std::cout << *(const_cast<Z_SRW_PDU *>(sru_pdu_res)) << "\n";
355     else
356         std::cout << "SRU empty\n";
357
358     
359     Z_GDU *zgdu_req = package.request().get();
360     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
361     {    
362         Z_GDU *zgdu_res //= z_get_HTTP_Response(odr_en, 200);
363             = odr_en.create_HTTP_Response(package.session(), 
364                                           zgdu_req->u.HTTP_Request, 
365                                           200);
366
367         // adding HTTP response code and headers
368         Z_HTTP_Response * http_res = zgdu_res->u.HTTP_Response;
369         //http_res->code = http_code;
370         
371         std::string ctype("text/xml");
372         if (charset){
373             ctype += "; charset=";
374             ctype += charset;
375         }
376
377         z_HTTP_header_add(odr_en, 
378                           &http_res->headers, "Content-Type", ctype.c_str());
379
380          // packaging Z_SOAP into HTML response
381          static Z_SOAP_Handler soap_handlers[4] = {
382               {"http://www.loc.gov/zing/srw/", 0,
383                (Z_SOAP_fun) yaz_srw_codec},
384               {"http://www.loc.gov/zing/srw/v1.0/", 0,
385                (Z_SOAP_fun) yaz_srw_codec},
386               {"http://www.loc.gov/zing/srw/update/", 0,
387                (Z_SOAP_fun) yaz_ucp_codec},
388               {0, 0, 0}
389           };
390
391
392          // empty stylesheet means NO stylesheet
393          if (stylesheet && *stylesheet == '\0')
394              stylesheet = 0;
395          
396          // encoding SRU package
397          
398          soap->u.generic->p  = (void*) sru_pdu_res;         
399          //int ret = 
400          z_soap_codec_enc_xsl(odr_en, &soap, 
401                               &http_res->content_buf, &http_res->content_len,
402                               soap_handlers, charset, stylesheet);
403          
404
405          package.response() = zgdu_res;
406          return true;
407     }
408     package.session().close();
409     return false;
410 }
411
412
413
414  Z_SRW_PDU * yf::SRUtoZ3950::Impl::decode_sru_request(mp::Package &package,
415                                                      mp::odr &odr_de,
416                                                      mp::odr &odr_en,
417                                                      Z_SRW_PDU *sru_pdu_res,
418                                                      Z_SOAP *&soap,
419                                                      char *charset,
420                                                      char *stylesheet) 
421      const
422 {
423     Z_GDU *zgdu_req = package.request().get();
424     Z_SRW_PDU *sru_pdu_req = 0;
425
426     assert((zgdu_req->which == Z_GDU_HTTP_Request));
427     
428     //ignoring all non HTTP_Request packages
429     //if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
430     //    return 0;
431     //}
432     
433     Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
434     if (! http_req)
435         return 0;
436
437     //Z_SRW_PDU *sru_pdu_res_exp = yaz_srw_get(odr_en, Z_SRW_explain_response);
438     
439     if (0 == yaz_sru_decode(http_req, &sru_pdu_req, &soap, 
440                             odr_de, &charset, 
441                             &(sru_pdu_res->u.response->diagnostics), 
442                             &(sru_pdu_res->u.response->num_diagnostics)))
443     {
444         if (sru_pdu_res->u.response->num_diagnostics)
445         {
446             //sru_pdu_res = sru_pdu_res_exp;
447             package.session().close();
448             return 0;
449         }
450         return sru_pdu_req;
451     }
452     else if (0 == yaz_srw_decode(http_req, &sru_pdu_req, &soap, 
453                                  odr_de, &charset))
454         return sru_pdu_req;
455     else 
456     {
457         //sru_pdu_res = sru_pdu_res_exp;
458         package.session().close();
459         return 0;
460     }
461     return 0;
462 }
463
464 bool 
465 yf::SRUtoZ3950::Impl::check_sru_query_exists(mp::Package &package, 
466                                             mp::odr &odr_en,
467                                             Z_SRW_PDU *sru_pdu_res, 
468                                             Z_SRW_searchRetrieveRequest 
469                                                               const *sr_req)
470     const
471 {
472     if( (sr_req->query_type == Z_SRW_query_type_cql && !sr_req->query.cql) )
473     {
474         yaz_add_srw_diagnostic(odr_en,
475                                &(sru_pdu_res->u.response->diagnostics), 
476                                &(sru_pdu_res->u.response->num_diagnostics), 
477                                7, "query");
478         yaz_add_srw_diagnostic(odr_en,
479                                &(sru_pdu_res->u.response->diagnostics), 
480                                &(sru_pdu_res->u.response->num_diagnostics), 
481                                10, "CQL query is empty");
482         return false;
483     }
484     if( (sr_req->query_type == Z_SRW_query_type_xcql && !sr_req->query.xcql) )
485     {
486          yaz_add_srw_diagnostic(odr_en,
487                                &(sru_pdu_res->u.response->diagnostics), 
488                                &(sru_pdu_res->u.response->num_diagnostics), 
489                                10, "XCQL query is empty");
490          return false;
491    }
492     if( (sr_req->query_type == Z_SRW_query_type_pqf && !sr_req->query.pqf) )
493     {
494         yaz_add_srw_diagnostic(odr_en,
495                                &(sru_pdu_res->u.response->diagnostics), 
496                                &(sru_pdu_res->u.response->num_diagnostics), 
497                                10, "PQF query is empty");
498         return false;
499     }
500     return true;
501 };
502
503
504
505 bool 
506 yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package, 
507                                              const std::string &database) const
508 {
509     // prepare Z3950 package
510     //Session s;
511     //Package z3950_package(s, package.origin());
512     Package z3950_package(package.session(), package.origin());
513     z3950_package.copy_filter(package);
514
515     // set initRequest APDU
516     mp::odr odr_en(ODR_ENCODE);
517     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
518     Z_InitRequest *init_req = apdu->u.initRequest;
519     //TODO: add user name in apdu
520     //TODO: add user passwd in apdu
521     //init_req->idAuthentication = org_init->idAuthentication;
522     //init_req->implementationId = "IDxyz";
523     //init_req->implementationName = "NAMExyz";
524     //init_req->implementationVersion = "VERSIONxyz";
525
526     ODR_MASK_SET(init_req->options, Z_Options_search);
527     ODR_MASK_SET(init_req->options, Z_Options_present);
528     ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
529     ODR_MASK_SET(init_req->options, Z_Options_scan);
530
531     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
532     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
533     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
534
535     z3950_package.request() = apdu;
536
537     // send Z3950 package
538     // std::cout << "z3950_init_request " << *apdu <<"\n";
539     z3950_package.move();
540
541     // dead Z3950 backend detection
542     if (z3950_package.session().is_closed()){
543         package.session().close();
544         return false;
545     }
546
547     // check successful initResponse
548     Z_GDU *z3950_gdu = z3950_package.response().get();
549
550     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
551         && z3950_gdu->u.z3950->which == Z_APDU_initResponse)
552          return true;
553  
554     return false;
555 }
556
557 bool 
558 yf::SRUtoZ3950::Impl::z3950_close_request(mp::Package &package) const
559 {
560     // close SRU package
561     package.session().close();
562
563     // prepare and close Z3950 package 
564     Package z3950_package(package.session(), package.origin());
565     z3950_package.copy_filter(package);
566     z3950_package.session().close();
567
568     // set close APDU
569     //mp::odr odr_en(ODR_ENCODE);
570     //Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_close);
571     //z3950_package.request() = apdu;
572
573     z3950_package.move();
574
575     // check successful close response
576     //Z_GDU *z3950_gdu = z3950_package.response().get();
577     //if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
578     //    && z3950_gdu->u.z3950->which == Z_APDU_close)
579     //    return true;
580
581     if (z3950_package.session().is_closed()){
582         return true;
583     }
584     return false;
585 }
586
587 bool 
588 yf::SRUtoZ3950::Impl::z3950_search_request(mp::Package &package,  
589                                           mp::odr &odr_en,
590                                           Z_SRW_PDU *sru_pdu_res,
591                                           Z_SRW_searchRetrieveRequest 
592                                           const *sr_req) const
593 {
594
595     assert(sru_pdu_res->u.response);
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         yaz_add_srw_diagnostic(odr_en,
626                                &(sru_pdu_res->u.response->diagnostics), 
627                                &(sru_pdu_res->u.response->num_diagnostics), 
628                                7, "query");
629         return false;
630     }
631
632     z3950_package.request() = apdu;
633     //std::cout << "z3950_search_request " << *apdu << "\n";
634         
635     z3950_package.move();
636
637
638     Z_GDU *z3950_gdu = z3950_package.response().get();
639     //std::cout << "z3950_search_request " << *z3950_gdu << "\n";
640
641     //TODO: check success condition
642
643     //int yaz_diag_bib1_to_srw (int bib1_code);
644     //int yaz_diag_srw_to_bib1(int srw_code);
645     //Se kode i src/seshigh.c (srw_bend_search, srw_bend_init).
646
647     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
648         && z3950_gdu->u.z3950->which == Z_APDU_searchResponse
649         && z3950_gdu->u.z3950->u.searchResponse->searchStatus)
650     {
651
652         Z_SearchResponse *sr = z3950_gdu->u.z3950->u.searchResponse;
653         if (sr)
654         {
655             // srw'fy number of records
656             sru_pdu_res->u.response->numberOfRecords 
657                 = (int *) odr_malloc(odr_en, sizeof(int *));
658             *(sru_pdu_res->u.response->numberOfRecords) = *(sr->resultCount);
659
660             // srw'fy nextRecordPosition
661             //sru_pdu_res->u.response->nextRecordPosition 
662             //    = (int *) odr_malloc(odr_en, sizeof(int *));
663             //*(sru_pdu_res->u.response->nextRecordPosition) = 1;
664
665         }
666
667         return true;
668     }
669     
670     return false;
671 }
672
673 bool 
674 yf::SRUtoZ3950::Impl::z3950_present_request(mp::Package &package, 
675                                            mp::odr &odr_en,
676                                            Z_SRW_PDU *sru_pdu_res,
677                                            Z_SRW_searchRetrieveRequest 
678                                            const *sr_req)
679     const
680 {
681     assert(sru_pdu_res->u.response);
682
683     if (!sr_req)
684         return false;
685
686     
687     // no need to work if nobody wants record ..
688     if (!(sr_req->maximumRecords) || 0 == *(sr_req->maximumRecords))
689         return true;
690
691     bool send_z3950_present = true;
692
693     // recordXPath unsupported.
694     if (sr_req->recordXPath)
695     {
696         send_z3950_present = false;
697         yaz_add_srw_diagnostic(odr_en,
698                                &(sru_pdu_res->u.response->diagnostics), 
699                                &(sru_pdu_res->u.response->num_diagnostics), 
700                                72, 0);
701     }
702     
703     // resultSetTTL unsupported.
704     // resultSetIdleTime in response
705     if (sr_req->resultSetTTL)
706     {
707         send_z3950_present = false;
708         yaz_add_srw_diagnostic(odr_en,
709                                &(sru_pdu_res->u.response->diagnostics), 
710                                &(sru_pdu_res->u.response->num_diagnostics), 
711                                50, 0);
712     }
713     
714     // sort unsupported
715     if (sr_req->sort_type != Z_SRW_sort_type_none)
716     {
717         send_z3950_present = false;
718         yaz_add_srw_diagnostic(odr_en,
719                                &(sru_pdu_res->u.response->diagnostics), 
720                                &(sru_pdu_res->u.response->num_diagnostics), 
721                                80, 0);
722     }
723     
724     // start record requested negative, or larger than number of records
725     if (sr_req->startRecord 
726         && 
727         ((*(sr_req->startRecord) < 0)       // negative
728          ||
729          (sru_pdu_res->u.response->numberOfRecords  //out of range
730           && *(sr_req->startRecord) 
731           > *(sru_pdu_res->u.response->numberOfRecords))
732         ))
733     {
734         send_z3950_present = false;
735         yaz_add_srw_diagnostic(odr_en,
736                                &(sru_pdu_res->u.response->diagnostics), 
737                                &(sru_pdu_res->u.response->num_diagnostics), 
738                                61, 0);
739     }    
740
741     // maximumRecords requested negative
742     if (sr_req->maximumRecords
743         && *(sr_req->maximumRecords) < 0) 
744           
745     {
746         send_z3950_present = false;
747         yaz_add_srw_diagnostic(odr_en,
748                                &(sru_pdu_res->u.response->diagnostics), 
749                                &(sru_pdu_res->u.response->num_diagnostics), 
750                                6, "maximumRecords");
751     }    
752
753     // exit on all these above diagnostics
754     if (!send_z3950_present)
755         return false;
756
757     // now packaging the z3950 present request
758     Package z3950_package(package.session(), package.origin());
759     z3950_package.copy_filter(package); 
760     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
761
762     assert(apdu->u.presentRequest);
763
764     // z3950'fy start record position
765     if (sr_req->startRecord)
766         *(apdu->u.presentRequest->resultSetStartPoint) 
767             = *(sr_req->startRecord);
768     else 
769         *(apdu->u.presentRequest->resultSetStartPoint) = 1;
770     
771     // z3950'fy number of records requested 
772     // protect against requesting records out of range
773     if (sr_req->maximumRecords)
774         *(apdu->u.presentRequest->numberOfRecordsRequested) 
775             = std::min(*(sr_req->maximumRecords), 
776                   *(sru_pdu_res->u.response->numberOfRecords)
777                   - *(apdu->u.presentRequest->resultSetStartPoint)
778                   + 1);
779      
780     // z3950'fy recordPacking
781     int record_packing = Z_SRW_recordPacking_XML;
782     if (sr_req->recordPacking && 's' == *(sr_req->recordPacking))
783         record_packing = Z_SRW_recordPacking_string;
784
785     // RecordSyntax will always be XML
786     (apdu->u.presentRequest->preferredRecordSyntax)
787         = yaz_oidval_to_z3950oid (odr_en, CLASS_RECSYN, VAL_TEXT_XML);
788
789     // z3950'fy record schema
790      if (sr_req->recordSchema)
791      {
792          apdu->u.presentRequest->recordComposition 
793              = (Z_RecordComposition *) 
794                odr_malloc(odr_en, sizeof(Z_RecordComposition));
795          apdu->u.presentRequest->recordComposition->which 
796              = Z_RecordComp_simple;
797          apdu->u.presentRequest->recordComposition->u.simple 
798              = build_esn_from_schema(odr_en, 
799                                      (const char *) sr_req->recordSchema); 
800      }
801
802     // z3950'fy time to live - flagged as diagnostics above
803     //if (sr_req->resultSetTTL)
804
805     // attaching Z3950 package to filter chain
806     z3950_package.request() = apdu;
807
808     //std::cout << "z3950_present_request " << *apdu << "\n";   
809     z3950_package.move();
810
811     //check successful Z3950 present response
812     Z_GDU *z3950_gdu = z3950_package.response().get();
813     if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950 
814         || z3950_gdu->u.z3950->which != Z_APDU_presentResponse
815         || !z3950_gdu->u.z3950->u.presentResponse)
816
817     {
818         yaz_add_srw_diagnostic(odr_en,
819                                &(sru_pdu_res->u.response->diagnostics), 
820                                &(sru_pdu_res->u.response->num_diagnostics), 
821                                2, 0);
822         package.session().close();
823         return false;
824     }
825     
826
827     // everything fine, continuing
828     //std::cout << "z3950_present_request OK\n";
829
830     Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
831     Z_SRW_searchRetrieveResponse *sru_res = sru_pdu_res->u.response;
832         
833     // checking non surrogate dioagnostics in Z3950 present response package
834     if (pr->records 
835         && pr->records->which == Z_Records_NSD
836         && pr->records->u.nonSurrogateDiagnostic)
837     {
838         //int http_code =
839         z3950_to_srw_diag(odr_en, sru_res, 
840                           pr->records->u.nonSurrogateDiagnostic);
841         return false;
842     }
843     
844     // copy all records if existing
845     if (pr->records && pr->records->which == Z_Records_DBOSD)
846     {
847         // srw'fy number of returned records
848         sru_res->num_records
849             = pr->records->u.databaseOrSurDiagnostics->num_records;
850         
851         sru_res->records 
852             = (Z_SRW_record *) odr_malloc(odr_en, 
853                                           sru_res->num_records 
854                                              * sizeof(Z_SRW_record));
855         
856
857         // srw'fy nextRecordPosition
858         // next position never zero or behind the last z3950 record 
859         if (pr->nextResultSetPosition
860             && *(pr->nextResultSetPosition) > 0 
861             && *(pr->nextResultSetPosition) 
862                <= *(sru_pdu_res->u.response->numberOfRecords))
863             sru_res->nextRecordPosition 
864                 = odr_intdup(odr_en, *(pr->nextResultSetPosition));
865         
866         // inserting all records
867         for (int i = 0; i < sru_res->num_records; i++)
868         {
869             Z_NamePlusRecord *npr 
870                 = pr->records->u.databaseOrSurDiagnostics->records[i];
871             
872             sru_res->records[i].recordPosition 
873                 = odr_intdup(odr_en, 
874                              i + *(apdu->u.presentRequest->resultSetStartPoint));
875             
876             sru_res->records[i].recordPacking = record_packing;
877             
878             if (npr->which != Z_NamePlusRecord_databaseRecord)
879             {
880                 sru_res->records[i].recordSchema = "diagnostic";
881                 sru_res->records[i].recordData_buf = "67";
882                 sru_res->records[i].recordData_len = 2;
883             }
884             else
885             {
886                 Z_External *r = npr->u.databaseRecord;
887                 oident *ent = oid_getentbyoid(r->direct_reference);
888                 if (r->which == Z_External_octet 
889                     && ent->value == VAL_TEXT_XML)
890                 {
891                     sru_res->records[i].recordSchema = "dc";
892                     sru_res->records[i].recordData_buf
893                         = odr_strdupn(odr_en, 
894                                       (const char *)r->u.octet_aligned->buf, 
895                                       r->u.octet_aligned->len);
896                     sru_res->records[i].recordData_len 
897                         = r->u.octet_aligned->len;
898                 }
899                 else
900                 {
901                     sru_res->records[i].recordSchema = "diagnostic";
902                     sru_res->records[i].recordData_buf = "67";
903                     sru_res->records[i].recordData_len = 2;
904                 }
905             }   
906         }    
907     }
908     
909     return true;
910 }
911
912 bool 
913 yf::SRUtoZ3950::Impl::z3950_scan_request(mp::Package &package,
914                                         mp::odr &odr_en,
915                                         Z_SRW_PDU *sru_pdu_res,
916                                         Z_SRW_scanRequest const *sr_req) const 
917 {
918     assert(sru_pdu_res->u.scan_response);
919
920     Package z3950_package(package.session(), package.origin());
921     z3950_package.copy_filter(package); 
922     //mp::odr odr_en(ODR_ENCODE);
923     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
924
925     //TODO: add stuff in apdu
926     Z_ScanRequest *z_scanRequest = apdu->u.scanRequest;
927
928     // database repackaging
929     z_scanRequest->num_databaseNames = 1;
930     z_scanRequest->databaseNames = (char**)
931         odr_malloc(odr_en, sizeof(char *));
932     if (sr_req->database)
933         z_scanRequest->databaseNames[0] 
934             = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
935     else
936         z_scanRequest->databaseNames[0] 
937             = odr_strdup(odr_en, "Default");
938
939
940     // query repackaging
941     // CQL or XCQL scan is not possible in Z3950, flagging a diagnostic
942     if (sr_req->query_type != Z_SRW_query_type_pqf)
943     {        
944         //send_to_srw_client_error(7, "query");
945         return false;
946     }
947
948     // PQF query repackaging
949     // need to use Z_AttributesPlusTerm structure, not Z_Query
950     // this can be digget out of a 
951     // Z_query->type1(Z_RPNQuery)->RPNStructure(Z_RPNStructure)
952     //   ->u.simple(Z_Operand)->u.attributesPlusTerm(Z_AttributesPlusTerm )
953
954     //Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
955     //z_searchRequest->query = z_query;
956
957     //if (!z3950_build_query(odr_en, z_query, 
958     //                       (const SRW_query&)sr_req->query, 
959     //                       sr_req->query_type))
960     //{    
961         //send_to_srw_client_error(7, "query");
962     //    return false;
963     //}
964
965     // TODO: 
966
967     z3950_package.request() = apdu;
968     std::cout << "z3950_scan_request " << *apdu << "\n";   
969
970     z3950_package.move();
971     //TODO: check success condition
972     return true;
973     return false;
974 }
975
976 bool yf::SRUtoZ3950::Impl::z3950_build_query(mp::odr &odr_en, Z_Query *z_query, 
977                                             const SRW_query &query, 
978                                             SRW_query_type query_type) const
979 {        
980     if (query_type == Z_SRW_query_type_cql)
981     {
982         Z_External *ext = (Z_External *) 
983             odr_malloc(odr_en, sizeof(*ext));
984         ext->direct_reference = 
985             odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
986         ext->indirect_reference = 0;
987         ext->descriptor = 0;
988         ext->which = Z_External_CQL;
989         ext->u.cql = const_cast<char *>(query.cql);
990         
991         z_query->which = Z_Query_type_104;
992         z_query->u.type_104 =  ext;
993         return true;
994     }
995
996     if (query_type == Z_SRW_query_type_pqf)
997     {
998         Z_RPNQuery *RPNquery;
999         YAZ_PQF_Parser pqf_parser;
1000         
1001         pqf_parser = yaz_pqf_create ();
1002         
1003         RPNquery = yaz_pqf_parse (pqf_parser, odr_en, query.pqf);
1004         if (!RPNquery)
1005         {
1006             std::cout << "TODO: Handeling of bad PQF\n";
1007             std::cout << "TODO: Diagnostic to be send\n";
1008         }
1009         z_query->which = Z_Query_type_1;
1010         z_query->u.type_1 =  RPNquery;
1011         
1012         yaz_pqf_destroy(pqf_parser);
1013         return true;
1014     }
1015     return false;
1016 }
1017
1018
1019 // std::string 
1020 // yf::SRUtoZ3950::Impl::sru_protocol(const Z_HTTP_Request &http_req) const
1021 // {
1022 //     const std::string mime_urlencoded("application/x-www-form-urlencoded");
1023 //     const std::string mime_text_xml("text/xml");
1024 //     const std::string mime_soap_xml("application/soap+xml");
1025
1026 //     const std::string http_method(http_req.method);
1027 //     const std::string http_type 
1028 //         =  mp_util::http_header_value(http_req.headers, "Content-Type");
1029
1030 //     if (http_method == "GET")
1031 //         return "SRU GET";
1032
1033 //     if (http_method == "POST"
1034 //               && http_type  == mime_urlencoded)
1035 //         return "SRU POST";
1036     
1037 //     if ( http_method == "POST"
1038 //          && (http_type  == mime_text_xml
1039 //              || http_type  == mime_soap_xml))
1040 //         return "SRU SOAP";
1041
1042 //     return "HTTP";
1043 // }
1044
1045 // std::string 
1046 // yf::SRUtoZ3950::Impl::debug_http(const Z_HTTP_Request &http_req) const
1047 // {
1048 //     std::string message("<html>\n<body>\n<h1>"
1049 //                         "Metaproxy SRUtoZ3950 filter"
1050 //                         "</h1>\n");
1051     
1052 //     message += "<h3>HTTP Info</h3><br/>\n";
1053 //     message += "<p>\n";
1054 //     message += "<b>Method: </b> " + std::string(http_req.method) + "<br/>\n";
1055 //     message += "<b>Version:</b> " + std::string(http_req.version) + "<br/>\n";
1056 //     message += "<b>Path:   </b> " + std::string(http_req.path) + "<br/>\n";
1057
1058 //     message += "<b>Content-Type:</b>"
1059 //         + mp_util::http_header_value(http_req.headers, "Content-Type")
1060 //         + "<br/>\n";
1061 //     message += "<b>Content-Length:</b>"
1062 //         + mp_util::http_header_value(http_req.headers, "Content-Length")
1063 //         + "<br/>\n";
1064 //     message += "</p>\n";    
1065     
1066 //     message += "<h3>Headers</h3><br/>\n";
1067 //     message += "<p>\n";    
1068 //     Z_HTTP_Header* header = http_req.headers;
1069 //     while (header){
1070 //         message += "<b>Header: </b> <i>" 
1071 //             + std::string(header->name) + ":</i> "
1072 //             + std::string(header->value) + "<br/>\n";
1073 //         header = header->next;
1074 //     }
1075 //     message += "</p>\n";    
1076 //     message += "</body>\n</html>\n";
1077 //     return message;
1078 // }
1079
1080 // void yf::SRUtoZ3950::Impl::http_response(metaproxy_1::Package &package, 
1081 //                                         const std::string &content, 
1082 //                                         int http_code) const
1083 // {
1084
1085 //     Z_GDU *zgdu_req = package.request().get(); 
1086 //     Z_GDU *zgdu_res = 0; 
1087 //     mp::odr odr;
1088 //     zgdu_res 
1089 //        = odr.create_HTTP_Response(package.session(), 
1090 //                                   zgdu_req->u.HTTP_Request, 
1091 //                                   http_code);
1092         
1093 //     zgdu_res->u.HTTP_Response->content_len = content.size();
1094 //     zgdu_res->u.HTTP_Response->content_buf 
1095 //         = (char*) odr_malloc(odr, zgdu_res->u.HTTP_Response->content_len);
1096     
1097 //     strncpy(zgdu_res->u.HTTP_Response->content_buf, 
1098 //             content.c_str(),  zgdu_res->u.HTTP_Response->content_len);
1099     
1100 //     //z_HTTP_header_add(odr, &hres->headers,
1101 //     //                  "Content-Type", content_type.c_str());
1102 //     package.response() = zgdu_res;
1103 // }
1104
1105
1106 Z_ElementSetNames * 
1107 yf::SRUtoZ3950::Impl::build_esn_from_schema(mp::odr &odr_en, 
1108                                            const char *schema) const
1109 {
1110   if (!schema)
1111         return 0;
1112   
1113     Z_ElementSetNames *esn 
1114         = (Z_ElementSetNames *) odr_malloc(odr_en, sizeof(Z_ElementSetNames));
1115     esn->which = Z_ElementSetNames_generic;
1116     esn->u.generic = odr_strdup(odr_en, schema);
1117     return esn; 
1118 }
1119
1120 int 
1121 yf::SRUtoZ3950::Impl::z3950_to_srw_diag(mp::odr &odr_en, 
1122                                        Z_SRW_searchRetrieveResponse *sru_res,
1123                                        Z_DefaultDiagFormat *ddf) const
1124 {
1125     int bib1_code = *ddf->condition;
1126     if (bib1_code == 109)
1127         return 404;
1128     sru_res->num_diagnostics = 1;
1129     sru_res->diagnostics = (Z_SRW_diagnostic *)
1130         odr_malloc(odr_en, sizeof(*sru_res->diagnostics));
1131     yaz_mk_std_diagnostic(odr_en, sru_res->diagnostics,
1132                           yaz_diag_bib1_to_srw(*ddf->condition), 
1133                           ddf->u.v2Addinfo);
1134     return 0;
1135 }
1136
1137
1138
1139 static mp::filter::Base* filter_creator()
1140 {
1141     return new mp::filter::SRUtoZ3950;
1142 }
1143
1144 extern "C" {
1145     struct metaproxy_1_filter_struct metaproxy_1_filter_sru_to_z3950 = {
1146         0,
1147         "SRUtoZ3950",
1148         filter_creator
1149     };
1150 }
1151
1152
1153 /*
1154  * Local variables:
1155  * c-basic-offset: 4
1156  * indent-tabs-mode: nil
1157  * c-file-style: "stroustrup"
1158  * End:
1159  * vim: shiftwidth=4 tabstop=8 expandtab
1160  */