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