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