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