Fix for bug #948. Other problem is that frontend session was terminated
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
1 /* $Id: filter_sru_to_z3950.cpp,v 1.30 2007-03-08 09:43:37 adam Exp $
2    Copyright (c) 2005-2007, Index Data.
3
4    See the LICENSE file for details
5 */
6
7 #include "config.hpp"
8 #include "filter.hpp"
9 #include "package.hpp"
10 #include "util.hpp"
11 #include "gduutil.hpp"
12 #include "sru_util.hpp"
13 #include "filter_sru_to_z3950.hpp"
14
15 #include <yaz/zgdu.h>
16 #include <yaz/z-core.h>
17 #include <yaz/srw.h>
18 #include <yaz/pquery.h>
19
20 #include <boost/thread/mutex.hpp>
21
22 #include <iostream>
23 #include <sstream>
24 #include <string>
25 #include <algorithm>
26 #include <map>
27
28 namespace mp = metaproxy_1;
29 namespace mp_util = metaproxy_1::util;
30 namespace yf = mp::filter;
31
32
33 namespace metaproxy_1 {
34     namespace filter {
35         class SRUtoZ3950::Impl {
36         public:
37             void configure(const xmlNode *xmlnode);
38             void process(metaproxy_1::Package &package);
39         private:
40             union SRW_query {char * cql; char * xcql; char * pqf;};
41             typedef const int& SRW_query_type;
42             std::map<std::string, const xmlNode *> m_database_explain;
43         private:
44
45             bool z3950_build_query(mp::odr &odr_en, Z_Query *z_query, 
46                                    const SRW_query &query, 
47                                    SRW_query_type query_type) const;
48
49             bool z3950_init_request(mp::Package &package, 
50                                     mp::odr &odr_en,
51                                     Z_SRW_PDU *sru_pdu_res,
52                                     const std::string 
53                                     &database = "Default") const;
54
55             bool z3950_close_request(mp::Package &package) const;
56
57             bool z3950_search_request(mp::Package &package,
58                                       mp::odr &odr_en,
59                                       Z_SRW_PDU *sru_pdu_res,
60                                       Z_SRW_searchRetrieveRequest 
61                                           const *sr_req) const;
62
63             bool z3950_present_request(mp::Package &package,
64                                        mp::odr &odr_en,
65                                        Z_SRW_PDU *sru_pdu_res,
66                                        Z_SRW_searchRetrieveRequest 
67                                        const *sr_req) const;
68
69             bool z3950_scan_request(mp::Package &package,
70                                     mp::odr &odr_en,
71                                     Z_SRW_PDU *sru_pdu_res,
72                                     Z_SRW_scanRequest 
73                                     const *sr_req) const;
74
75             bool z3950_to_srw_diagnostics_ok(mp::odr &odr_en, 
76                                   Z_SRW_searchRetrieveResponse *srw_res,
77                                   Z_Records *records) const;
78
79             int z3950_to_srw_diag(mp::odr &odr_en, 
80                                   Z_SRW_searchRetrieveResponse *srw_res,
81                                   Z_DefaultDiagFormat *ddf) const;
82         };
83     }
84 }
85
86 yf::SRUtoZ3950::SRUtoZ3950() : m_p(new Impl)
87 {
88 }
89
90 yf::SRUtoZ3950::~SRUtoZ3950()
91 {  // must have a destructor because of boost::scoped_ptr
92 }
93
94 void yf::SRUtoZ3950::configure(const xmlNode *xmlnode)
95 {
96     m_p->configure(xmlnode);
97 }
98
99 void yf::SRUtoZ3950::process(mp::Package &package) const
100 {
101     m_p->process(package);
102 }
103
104 void yf::SRUtoZ3950::Impl::configure(const xmlNode *confignode)
105 {
106     const xmlNode * dbnode;
107     
108     for (dbnode = confignode->children; dbnode; dbnode = dbnode->next){
109         if (dbnode->type != XML_ELEMENT_NODE)
110             continue;
111         
112         std::string database;
113         mp::xml::check_element_mp(dbnode, "database");
114
115         for (struct _xmlAttr *attr = dbnode->properties; 
116              attr; attr = attr->next){
117             
118             mp::xml::check_attribute(attr, "", "name");
119             database = mp::xml::get_text(attr);
120              
121             const xmlNode *explainnode;
122             for (explainnode = dbnode->children; 
123                  explainnode; explainnode = explainnode->next){
124                 if (explainnode->type != XML_ELEMENT_NODE)
125                     continue;
126                 if (explainnode)
127                     break;
128             }
129             // assigning explain node to database name - no check yet 
130             m_database_explain.insert(std::make_pair(database, explainnode));
131          }
132     }
133 }
134
135 void yf::SRUtoZ3950::Impl::process(mp::Package &package)
136 {
137     Z_GDU *zgdu_req = package.request().get();
138
139     // ignoring all non HTTP_Request packages
140     if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
141         package.move();
142         return;
143     }
144     
145     // only working on  HTTP_Request packages now
146
147     bool ok = true;    
148
149     mp::odr odr_de(ODR_DECODE);
150     Z_SRW_PDU *sru_pdu_req = 0;
151
152     mp::odr odr_en(ODR_ENCODE);
153     Z_SRW_PDU *sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
154
155     // determine database with the HTTP header information only
156     mp_util::SRUServerInfo sruinfo = mp_util::get_sru_server_info(package);
157     std::map<std::string, const xmlNode *>::iterator idbexp;
158     idbexp = m_database_explain.find(sruinfo.database);
159
160     // assign explain config XML DOM node if database is known
161     const xmlNode *explainnode = 0;
162     if (idbexp != m_database_explain.end()){
163         explainnode = idbexp->second;
164     }
165     // just moving package if database is not known
166     else {
167         package.move();
168         return;
169     }
170     
171
172     // decode SRU request
173     Z_SOAP *soap = 0;
174     char *charset = 0;
175     char *stylesheet = 0;
176
177     // filter acts as sink for non-valid SRU requests
178     if (! (sru_pdu_req = mp_util::decode_sru_request(package, odr_de, odr_en, 
179                                             sru_pdu_res, soap,
180                                             charset, stylesheet)))
181     {
182         mp_util::build_sru_explain(package, odr_en, sru_pdu_res, 
183                                    sruinfo, explainnode);
184         mp_util::build_sru_response(package, odr_en, soap, 
185                            sru_pdu_res, charset, stylesheet);
186         // package.session().close();
187         return;
188     }
189     
190     // filter acts as sink for SRU explain requests
191     if (sru_pdu_req && sru_pdu_req->which == Z_SRW_explain_request)
192     {
193         Z_SRW_explainRequest *er_req = sru_pdu_req->u.explain_request;
194         //mp_util::build_simple_explain(package, odr_en, sru_pdu_res, 
195         //                           sruinfo, er_req);
196         mp_util::build_sru_explain(package, odr_en, sru_pdu_res, 
197                                    sruinfo, explainnode, er_req);
198         mp_util::build_sru_response(package, odr_en, soap, 
199                                     sru_pdu_res, charset, stylesheet);
200         return;
201     }
202
203     // searchRetrieve
204     else if (sru_pdu_req 
205         && sru_pdu_req->which == Z_SRW_searchRetrieve_request
206         && sru_pdu_req->u.request)
207     {
208         Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;   
209         
210         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_searchRetrieve_response);
211
212         // checking that we have a query
213         ok = mp_util::check_sru_query_exists(package, odr_en, 
214                                              sru_pdu_res, sr_req);
215
216         if (ok && z3950_init_request(package, odr_en, sru_pdu_res))
217         {
218             {
219                 ok = z3950_search_request(package, odr_en,
220                                           sru_pdu_res, sr_req);
221
222                 if (ok 
223                     && sru_pdu_res->u.response->numberOfRecords
224                     && *(sru_pdu_res->u.response->numberOfRecords)
225                     && sr_req->maximumRecords
226                     && *(sr_req->maximumRecords))
227                     
228                     ok = z3950_present_request(package, odr_en,
229                                                sru_pdu_res,
230                                                sr_req);
231                 z3950_close_request(package);
232             }
233         }
234     }
235
236     // scan
237     else if (sru_pdu_req 
238              && sru_pdu_req->which == Z_SRW_scan_request
239              && sru_pdu_req->u.scan_request)
240     {
241         Z_SRW_scanRequest  *sr_req = sru_pdu_req->u.scan_request;   
242
243         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_scan_response);
244         
245         // we do not do scan at the moment, therefore issuing a diagnostic
246         yaz_add_srw_diagnostic(odr_en,
247                                &(sru_pdu_res->u.scan_response->diagnostics), 
248                                &(sru_pdu_res->u.scan_response->num_diagnostics), 
249                                4, "scan");
250  
251         // to be used when we do scan
252         if (false && z3950_init_request(package, odr_en, sru_pdu_res))
253         {
254             z3950_scan_request(package, odr_en, sru_pdu_res, sr_req);    
255             z3950_close_request(package);
256         }        
257     }
258     else
259     {
260         //std::cout << "SRU OPERATION NOT SUPPORTED \n";
261         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
262         
263         // TODO: make nice diagnostic return package 
264         return;
265     }
266
267     // build and send SRU response
268     mp_util::build_sru_response(package, odr_en, soap, 
269                                 sru_pdu_res, charset, stylesheet);
270     return;
271 }
272
273
274
275 bool 
276 yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package, 
277                                          mp::odr &odr_en,
278                                          Z_SRW_PDU *sru_pdu_res,
279                                          const std::string &database) const
280 {
281     // prepare Z3950 package
282     Package z3950_package(package.session(), package.origin());
283     z3950_package.copy_filter(package);
284
285     // set initRequest APDU
286     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
287     Z_InitRequest *init_req = apdu->u.initRequest;
288     //TODO: add user name in apdu
289     //TODO: add user passwd in apdu
290     //init_req->idAuthentication = org_init->idAuthentication;
291     //init_req->implementationId = "IDxyz";
292     //init_req->implementationName = "NAMExyz";
293     //init_req->implementationVersion = "VERSIONxyz";
294
295     ODR_MASK_SET(init_req->options, Z_Options_search);
296     ODR_MASK_SET(init_req->options, Z_Options_present);
297     ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
298     ODR_MASK_SET(init_req->options, Z_Options_scan);
299
300     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
301     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
302     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
303
304     z3950_package.request() = apdu;
305
306     // send Z3950 package
307     z3950_package.move();
308
309     // dead Z3950 backend detection
310     if (z3950_package.session().is_closed()){
311         yaz_add_srw_diagnostic(odr_en,
312                                &(sru_pdu_res->u.response->diagnostics),
313                                &(sru_pdu_res->u.response->num_diagnostics),
314                                2, 0);
315         return false;
316     }
317
318     // check successful initResponse
319     Z_GDU *z3950_gdu = z3950_package.response().get();
320
321     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
322         && z3950_gdu->u.z3950->which == Z_APDU_initResponse 
323         && *z3950_gdu->u.z3950->u.initResponse->result)
324          return true;
325  
326     yaz_add_srw_diagnostic(odr_en,
327                            &(sru_pdu_res->u.response->diagnostics),
328                            &(sru_pdu_res->u.response->num_diagnostics),
329                            2, 0);
330     return false;
331 }
332
333 bool 
334 yf::SRUtoZ3950::Impl::z3950_close_request(mp::Package &package) const
335 {
336     // prepare and close Z3950 package 
337     Package z3950_package(package.session(), package.origin());
338     z3950_package.copy_filter(package);
339     z3950_package.session().close();
340
341     // set close APDU
342     //mp::odr odr_en(ODR_ENCODE);
343     //Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_close);
344     //z3950_package.request() = apdu;
345
346     z3950_package.move();
347
348     // check successful close response
349     //Z_GDU *z3950_gdu = z3950_package.response().get();
350     //if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
351     //    && z3950_gdu->u.z3950->which == Z_APDU_close)
352     //    return true;
353
354     if (z3950_package.session().is_closed()){
355         return true;
356     }
357     return false;
358 }
359
360 bool 
361 yf::SRUtoZ3950::Impl::z3950_search_request(mp::Package &package,  
362                                           mp::odr &odr_en,
363                                           Z_SRW_PDU *sru_pdu_res,
364                                           Z_SRW_searchRetrieveRequest 
365                                           const *sr_req) const
366 {
367
368     assert(sru_pdu_res->u.response);
369
370     Package z3950_package(package.session(), package.origin());
371     z3950_package.copy_filter(package);
372
373     //add stuff in z3950 apdu
374     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
375     Z_SearchRequest *z_searchRequest = apdu->u.searchRequest;
376
377     // z3950'fy database
378     z_searchRequest->num_databaseNames = 1;
379     z_searchRequest->databaseNames = (char**)
380         odr_malloc(odr_en, sizeof(char *));
381
382     if (sr_req->database)
383         z_searchRequest->databaseNames[0] 
384             = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
385     else
386         z_searchRequest->databaseNames[0] 
387             = odr_strdup(odr_en, "Default");
388
389
390     // z3950'fy query
391     Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
392     z_searchRequest->query = z_query;
393  
394     if (!z3950_build_query(odr_en, z_query, 
395                            (const SRW_query&)sr_req->query, 
396                            sr_req->query_type))
397     {    
398         yaz_add_srw_diagnostic(odr_en,
399                                &(sru_pdu_res->u.response->diagnostics), 
400                                &(sru_pdu_res->u.response->num_diagnostics), 
401                                7, "query");
402         return false;
403     }
404
405     z3950_package.request() = apdu;
406         
407     // send Z39.50 package off to backend
408     z3950_package.move();
409
410
411     Z_GDU *z3950_gdu = z3950_package.response().get();
412
413     //TODO: check success condition
414     //int yaz_diag_bib1_to_srw (int bib1_code);
415     //int yaz_diag_srw_to_bib1(int srw_code);
416     //Se kode i src/seshigh.c (srw_bend_search, srw_bend_init).
417
418     if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950 
419         || z3950_gdu->u.z3950->which != Z_APDU_searchResponse
420         || !z3950_gdu->u.z3950->u.searchResponse
421         || !z3950_gdu->u.z3950->u.searchResponse->searchStatus)
422     {
423         yaz_add_srw_diagnostic(odr_en,
424                                &(sru_pdu_res->u.response->diagnostics),
425                                &(sru_pdu_res->u.response->num_diagnostics),
426                                2, 0);
427         return false;
428     }
429     
430     // everything fine, continuing
431     Z_SearchResponse *sr = z3950_gdu->u.z3950->u.searchResponse;
432
433     // checking non surrogate diagnostics in Z3950 search response package
434     if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response, 
435                                      sr->records))
436     {
437         return false;
438     }
439
440     // Finally, roll on and srw'fy number of records
441     sru_pdu_res->u.response->numberOfRecords 
442         = (int *) odr_malloc(odr_en, sizeof(int *));
443     *(sru_pdu_res->u.response->numberOfRecords) = *(sr->resultCount);
444     
445     // srw'fy nextRecordPosition
446     //sru_pdu_res->u.response->nextRecordPosition 
447     //    = (int *) odr_malloc(odr_en, sizeof(int *));
448     //*(sru_pdu_res->u.response->nextRecordPosition) = 1;
449
450     return true;
451 }
452
453 bool 
454 yf::SRUtoZ3950::Impl::z3950_present_request(mp::Package &package, 
455                                            mp::odr &odr_en,
456                                            Z_SRW_PDU *sru_pdu_res,
457                                            Z_SRW_searchRetrieveRequest 
458                                            const *sr_req)
459     const
460 {
461     assert(sru_pdu_res->u.response);
462
463     if (!sr_req)
464         return false;
465
466     
467     // no need to work if nobody wants record ..
468     if (!(sr_req->maximumRecords) || 0 == *(sr_req->maximumRecords))
469         return true;
470
471     bool send_z3950_present = true;
472
473     // recordXPath unsupported.
474     if (sr_req->recordXPath)
475     {
476         send_z3950_present = false;
477         yaz_add_srw_diagnostic(odr_en,
478                                &(sru_pdu_res->u.response->diagnostics), 
479                                &(sru_pdu_res->u.response->num_diagnostics), 
480                                72, 0);
481     }
482     
483     // resultSetTTL unsupported.
484     // resultSetIdleTime in response
485     if (sr_req->resultSetTTL)
486     {
487         send_z3950_present = false;
488         yaz_add_srw_diagnostic(odr_en,
489                                &(sru_pdu_res->u.response->diagnostics), 
490                                &(sru_pdu_res->u.response->num_diagnostics), 
491                                50, 0);
492     }
493     
494     // sort unsupported
495     if (sr_req->sort_type != Z_SRW_sort_type_none)
496     {
497         send_z3950_present = false;
498         yaz_add_srw_diagnostic(odr_en,
499                                &(sru_pdu_res->u.response->diagnostics), 
500                                &(sru_pdu_res->u.response->num_diagnostics), 
501                                80, 0);
502     }
503     
504     // start record requested negative, or larger than number of records
505     if (sr_req->startRecord 
506         && 
507         ((*(sr_req->startRecord) < 0)       // negative
508          ||
509          (sru_pdu_res->u.response->numberOfRecords  //out of range
510           && *(sr_req->startRecord) 
511           > *(sru_pdu_res->u.response->numberOfRecords))
512         ))
513     {
514         send_z3950_present = false;
515         yaz_add_srw_diagnostic(odr_en,
516                                &(sru_pdu_res->u.response->diagnostics), 
517                                &(sru_pdu_res->u.response->num_diagnostics), 
518                                61, 0);
519     }    
520
521     // maximumRecords requested negative
522     if (sr_req->maximumRecords
523         && *(sr_req->maximumRecords) < 0) 
524           
525     {
526         send_z3950_present = false;
527         yaz_add_srw_diagnostic(odr_en,
528                                &(sru_pdu_res->u.response->diagnostics), 
529                                &(sru_pdu_res->u.response->num_diagnostics), 
530                                6, "maximumRecords");
531     }    
532
533     // exit on all these above diagnostics
534     if (!send_z3950_present)
535         return false;
536
537     // now packaging the z3950 present request
538     Package z3950_package(package.session(), package.origin());
539     z3950_package.copy_filter(package); 
540     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
541
542     assert(apdu->u.presentRequest);
543
544     // z3950'fy start record position
545     if (sr_req->startRecord)
546         *(apdu->u.presentRequest->resultSetStartPoint) 
547             = *(sr_req->startRecord);
548     else 
549         *(apdu->u.presentRequest->resultSetStartPoint) = 1;
550     
551     // z3950'fy number of records requested 
552     // protect against requesting records out of range
553     if (sr_req->maximumRecords)
554         *(apdu->u.presentRequest->numberOfRecordsRequested) 
555             = std::min(*(sr_req->maximumRecords), 
556                   *(sru_pdu_res->u.response->numberOfRecords)
557                   - *(apdu->u.presentRequest->resultSetStartPoint)
558                   + 1);
559      
560     // z3950'fy recordPacking
561     int record_packing = Z_SRW_recordPacking_XML;
562     if (sr_req->recordPacking && 's' == *(sr_req->recordPacking))
563         record_packing = Z_SRW_recordPacking_string;
564
565     // RecordSyntax will always be XML
566     (apdu->u.presentRequest->preferredRecordSyntax)
567         = yaz_oidval_to_z3950oid (odr_en, CLASS_RECSYN, VAL_TEXT_XML);
568
569     // z3950'fy record schema
570      if (sr_req->recordSchema)
571      {
572          apdu->u.presentRequest->recordComposition 
573              = (Z_RecordComposition *) 
574                odr_malloc(odr_en, sizeof(Z_RecordComposition));
575          apdu->u.presentRequest->recordComposition->which 
576              = Z_RecordComp_simple;
577          apdu->u.presentRequest->recordComposition->u.simple 
578              = mp_util::build_esn_from_schema(odr_en,
579                                       (const char *) sr_req->recordSchema); 
580      }
581
582     // z3950'fy time to live - flagged as diagnostics above
583     //if (sr_req->resultSetTTL)
584
585     // attaching Z3950 package to filter chain
586     z3950_package.request() = apdu;
587
588     // sending Z30.50 present request 
589     z3950_package.move();
590
591     //check successful Z3950 present response
592     Z_GDU *z3950_gdu = z3950_package.response().get();
593     if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950 
594         || z3950_gdu->u.z3950->which != Z_APDU_presentResponse
595         || !z3950_gdu->u.z3950->u.presentResponse)
596
597     {
598         yaz_add_srw_diagnostic(odr_en,
599                                &(sru_pdu_res->u.response->diagnostics), 
600                                &(sru_pdu_res->u.response->num_diagnostics), 
601                                2, 0);
602         return false;
603     }
604
605
606     // everything fine, continuing
607
608     Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
609     Z_SRW_searchRetrieveResponse *sru_res = sru_pdu_res->u.response;
610         
611
612     // checking non surrogate diagnostics in Z3950 present response package
613     if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response, 
614                                      pr->records))
615         return false;
616     
617
618     
619     // copy all records if existing
620     if (pr->records && pr->records->which == Z_Records_DBOSD)
621     {
622         // srw'fy number of returned records
623         sru_res->num_records
624             = pr->records->u.databaseOrSurDiagnostics->num_records;
625         
626         sru_res->records 
627             = (Z_SRW_record *) odr_malloc(odr_en, 
628                                           sru_res->num_records 
629                                              * sizeof(Z_SRW_record));
630         
631
632         // srw'fy nextRecordPosition
633         // next position never zero or behind the last z3950 record 
634         if (pr->nextResultSetPosition
635             && *(pr->nextResultSetPosition) > 0 
636             && *(pr->nextResultSetPosition) 
637                <= *(sru_pdu_res->u.response->numberOfRecords))
638             sru_res->nextRecordPosition 
639                 = odr_intdup(odr_en, *(pr->nextResultSetPosition));
640         
641         // inserting all records
642         for (int i = 0; i < sru_res->num_records; i++)
643         {
644             Z_NamePlusRecord *npr 
645                 = pr->records->u.databaseOrSurDiagnostics->records[i];
646             
647             sru_res->records[i].recordPosition 
648                 = odr_intdup(odr_en,
649                            i + *(apdu->u.presentRequest->resultSetStartPoint));
650             
651             sru_res->records[i].recordPacking = record_packing;
652             
653             if (npr->which != Z_NamePlusRecord_databaseRecord)
654             {
655                 sru_res->records[i].recordSchema = "diagnostic";
656                 sru_res->records[i].recordData_buf = "67";
657                 sru_res->records[i].recordData_len = 2;
658             }
659             else
660             {
661                 Z_External *r = npr->u.databaseRecord;
662                 oident *ent = oid_getentbyoid(r->direct_reference);
663                 if (r->which == Z_External_octet 
664                     && ent->value == VAL_TEXT_XML)
665                 {
666                     sru_res->records[i].recordSchema = "dc";
667                     sru_res->records[i].recordData_buf
668                         = odr_strdupn(odr_en, 
669                                       (const char *)r->u.octet_aligned->buf, 
670                                       r->u.octet_aligned->len);
671                     sru_res->records[i].recordData_len 
672                         = r->u.octet_aligned->len;
673                 }
674                 else
675                 {
676                     sru_res->records[i].recordSchema = "diagnostic";
677                     sru_res->records[i].recordData_buf = "67";
678                     sru_res->records[i].recordData_len = 2;
679                 }
680             }   
681         }    
682     }
683     
684     return true;
685 }
686
687 bool 
688 yf::SRUtoZ3950::Impl::z3950_scan_request(mp::Package &package,
689                                         mp::odr &odr_en,
690                                         Z_SRW_PDU *sru_pdu_res,
691                                         Z_SRW_scanRequest const *sr_req) const 
692 {
693     assert(sru_pdu_res->u.scan_response);
694
695     Package z3950_package(package.session(), package.origin());
696     z3950_package.copy_filter(package); 
697     //mp::odr odr_en(ODR_ENCODE);
698     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
699
700     //TODO: add stuff in apdu
701     Z_ScanRequest *z_scanRequest = apdu->u.scanRequest;
702
703     // database repackaging
704     z_scanRequest->num_databaseNames = 1;
705     z_scanRequest->databaseNames = (char**)
706         odr_malloc(odr_en, sizeof(char *));
707     if (sr_req->database)
708         z_scanRequest->databaseNames[0] 
709             = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
710     else
711         z_scanRequest->databaseNames[0] 
712             = odr_strdup(odr_en, "Default");
713
714
715     // query repackaging
716     // CQL or XCQL scan is not possible in Z3950, flagging a diagnostic
717     if (sr_req->query_type != Z_SRW_query_type_pqf)
718     {        
719         //send_to_srw_client_error(7, "query");
720         return false;
721     }
722
723     // PQF query repackaging
724     // need to use Z_AttributesPlusTerm structure, not Z_Query
725     // this can be digget out of a 
726     // Z_query->type1(Z_RPNQuery)->RPNStructure(Z_RPNStructure)
727     //   ->u.simple(Z_Operand)->u.attributesPlusTerm(Z_AttributesPlusTerm )
728
729     //Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
730     //z_searchRequest->query = z_query;
731
732     //if (!z3950_build_query(odr_en, z_query, 
733     //                       (const SRW_query&)sr_req->query, 
734     //                       sr_req->query_type))
735     //{    
736         //send_to_srw_client_error(7, "query");
737     //    return false;
738     //}
739
740     // TODO: 
741
742     z3950_package.request() = apdu;
743     std::cout << "z3950_scan_request " << *apdu << "\n";   
744
745     z3950_package.move();
746     //TODO: check success condition
747     return true;
748     return false;
749 }
750
751 bool yf::SRUtoZ3950::Impl::z3950_build_query(mp::odr &odr_en, Z_Query *z_query, 
752                                             const SRW_query &query, 
753                                             SRW_query_type query_type) const
754 {        
755     if (query_type == Z_SRW_query_type_cql)
756     {
757         Z_External *ext = (Z_External *) 
758             odr_malloc(odr_en, sizeof(*ext));
759         ext->direct_reference = 
760             odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
761         ext->indirect_reference = 0;
762         ext->descriptor = 0;
763         ext->which = Z_External_CQL;
764         ext->u.cql = const_cast<char *>(query.cql);
765         
766         z_query->which = Z_Query_type_104;
767         z_query->u.type_104 =  ext;
768         return true;
769     }
770
771     if (query_type == Z_SRW_query_type_pqf)
772     {
773         Z_RPNQuery *RPNquery;
774         YAZ_PQF_Parser pqf_parser;
775         
776         pqf_parser = yaz_pqf_create ();
777         
778         RPNquery = yaz_pqf_parse (pqf_parser, odr_en, query.pqf);
779         if (!RPNquery)
780         {
781             std::cout << "TODO: Handeling of bad PQF\n";
782             std::cout << "TODO: Diagnostic to be send\n";
783         }
784         z_query->which = Z_Query_type_1;
785         z_query->u.type_1 =  RPNquery;
786         
787         yaz_pqf_destroy(pqf_parser);
788         return true;
789     }
790     return false;
791 }
792
793
794 bool 
795 yf::SRUtoZ3950::Impl::z3950_to_srw_diagnostics_ok(mp::odr &odr_en, 
796                                                   Z_SRW_searchRetrieveResponse 
797                                                   *sru_res,
798                                                   Z_Records *records) const
799 {
800     // checking non surrogate diagnostics in Z3950 present response package
801     if (records 
802         && records->which == Z_Records_NSD
803         && records->u.nonSurrogateDiagnostic)
804     {
805         z3950_to_srw_diag(odr_en, sru_res, 
806                           records->u.nonSurrogateDiagnostic);
807         return false;
808     }
809     return true;
810 }
811
812
813 int 
814 yf::SRUtoZ3950::Impl::z3950_to_srw_diag(mp::odr &odr_en, 
815                                        Z_SRW_searchRetrieveResponse *sru_res,
816                                        Z_DefaultDiagFormat *ddf) const
817 {
818     int bib1_code = *ddf->condition;
819     sru_res->num_diagnostics = 1;
820     sru_res->diagnostics = (Z_SRW_diagnostic *)
821         odr_malloc(odr_en, sizeof(*sru_res->diagnostics));
822     yaz_mk_std_diagnostic(odr_en, sru_res->diagnostics,
823                           yaz_diag_bib1_to_srw(bib1_code), 
824                           ddf->u.v2Addinfo);
825     return 0;
826 }
827
828
829
830 static mp::filter::Base* filter_creator()
831 {
832     return new mp::filter::SRUtoZ3950;
833 }
834
835 extern "C" {
836     struct metaproxy_1_filter_struct metaproxy_1_filter_sru_to_z3950 = {
837         0,
838         "sru_z3950",
839         filter_creator
840     };
841 }
842
843
844 /*
845  * Local variables:
846  * c-basic-offset: 4
847  * indent-tabs-mode: nil
848  * c-file-style: "stroustrup"
849  * End:
850  * vim: shiftwidth=4 tabstop=8 expandtab
851  */