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