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