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