Fixed bug #975: HTTP HEAD Request kills SRU server.
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
1 /* $Id: filter_sru_to_z3950.cpp,v 1.31 2007-03-20 07:05:10 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         if (soap)
183         {
184             mp_util::build_sru_explain(package, odr_en, sru_pdu_res, 
185                                        sruinfo, explainnode);
186             mp_util::build_sru_response(package, odr_en, soap, 
187                                         sru_pdu_res, charset, stylesheet);
188         }
189         else
190         {
191             metaproxy_1::odr odr; 
192             Z_GDU *zgdu_res = 
193                 odr.create_HTTP_Response(package.session(), 
194                                          zgdu_req->u.HTTP_Request, 400);
195             package.response() = zgdu_res;
196         }
197         return;
198     }
199     
200     // filter acts as sink for SRU explain requests
201     if (sru_pdu_req && sru_pdu_req->which == Z_SRW_explain_request)
202     {
203         Z_SRW_explainRequest *er_req = sru_pdu_req->u.explain_request;
204         //mp_util::build_simple_explain(package, odr_en, sru_pdu_res, 
205         //                           sruinfo, er_req);
206         mp_util::build_sru_explain(package, odr_en, sru_pdu_res, 
207                                    sruinfo, explainnode, er_req);
208         mp_util::build_sru_response(package, odr_en, soap, 
209                                     sru_pdu_res, charset, stylesheet);
210         return;
211     }
212
213     // searchRetrieve
214     else if (sru_pdu_req 
215         && sru_pdu_req->which == Z_SRW_searchRetrieve_request
216         && sru_pdu_req->u.request)
217     {
218         Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;   
219         
220         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_searchRetrieve_response);
221
222         // checking that we have a query
223         ok = mp_util::check_sru_query_exists(package, odr_en, 
224                                              sru_pdu_res, sr_req);
225
226         if (ok && z3950_init_request(package, odr_en, sru_pdu_res))
227         {
228             {
229                 ok = z3950_search_request(package, odr_en,
230                                           sru_pdu_res, sr_req);
231
232                 if (ok 
233                     && sru_pdu_res->u.response->numberOfRecords
234                     && *(sru_pdu_res->u.response->numberOfRecords)
235                     && sr_req->maximumRecords
236                     && *(sr_req->maximumRecords))
237                     
238                     ok = z3950_present_request(package, odr_en,
239                                                sru_pdu_res,
240                                                sr_req);
241                 z3950_close_request(package);
242             }
243         }
244     }
245
246     // scan
247     else if (sru_pdu_req 
248              && sru_pdu_req->which == Z_SRW_scan_request
249              && sru_pdu_req->u.scan_request)
250     {
251         Z_SRW_scanRequest  *sr_req = sru_pdu_req->u.scan_request;   
252
253         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_scan_response);
254         
255         // we do not do scan at the moment, therefore issuing a diagnostic
256         yaz_add_srw_diagnostic(odr_en,
257                                &(sru_pdu_res->u.scan_response->diagnostics), 
258                                &(sru_pdu_res->u.scan_response->num_diagnostics), 
259                                4, "scan");
260  
261         // to be used when we do scan
262         if (false && z3950_init_request(package, odr_en, sru_pdu_res))
263         {
264             z3950_scan_request(package, odr_en, sru_pdu_res, sr_req);    
265             z3950_close_request(package);
266         }        
267     }
268     else
269     {
270         //std::cout << "SRU OPERATION NOT SUPPORTED \n";
271         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
272         
273         // TODO: make nice diagnostic return package 
274         return;
275     }
276
277     // build and send SRU response
278     mp_util::build_sru_response(package, odr_en, soap, 
279                                 sru_pdu_res, charset, stylesheet);
280     return;
281 }
282
283
284
285 bool 
286 yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package, 
287                                          mp::odr &odr_en,
288                                          Z_SRW_PDU *sru_pdu_res,
289                                          const std::string &database) const
290 {
291     // prepare Z3950 package
292     Package z3950_package(package.session(), package.origin());
293     z3950_package.copy_filter(package);
294
295     // set initRequest APDU
296     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
297     Z_InitRequest *init_req = apdu->u.initRequest;
298     //TODO: add user name in apdu
299     //TODO: add user passwd in apdu
300     //init_req->idAuthentication = org_init->idAuthentication;
301     //init_req->implementationId = "IDxyz";
302     //init_req->implementationName = "NAMExyz";
303     //init_req->implementationVersion = "VERSIONxyz";
304
305     ODR_MASK_SET(init_req->options, Z_Options_search);
306     ODR_MASK_SET(init_req->options, Z_Options_present);
307     ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
308     ODR_MASK_SET(init_req->options, Z_Options_scan);
309
310     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
311     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
312     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
313
314     z3950_package.request() = apdu;
315
316     // send Z3950 package
317     z3950_package.move();
318
319     // dead Z3950 backend detection
320     if (z3950_package.session().is_closed()){
321         yaz_add_srw_diagnostic(odr_en,
322                                &(sru_pdu_res->u.response->diagnostics),
323                                &(sru_pdu_res->u.response->num_diagnostics),
324                                2, 0);
325         return false;
326     }
327
328     // check successful initResponse
329     Z_GDU *z3950_gdu = z3950_package.response().get();
330
331     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
332         && z3950_gdu->u.z3950->which == Z_APDU_initResponse 
333         && *z3950_gdu->u.z3950->u.initResponse->result)
334          return true;
335  
336     yaz_add_srw_diagnostic(odr_en,
337                            &(sru_pdu_res->u.response->diagnostics),
338                            &(sru_pdu_res->u.response->num_diagnostics),
339                            2, 0);
340     return false;
341 }
342
343 bool 
344 yf::SRUtoZ3950::Impl::z3950_close_request(mp::Package &package) const
345 {
346     // prepare and close Z3950 package 
347     Package z3950_package(package.session(), package.origin());
348     z3950_package.copy_filter(package);
349     z3950_package.session().close();
350
351     // set close APDU
352     //mp::odr odr_en(ODR_ENCODE);
353     //Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_close);
354     //z3950_package.request() = apdu;
355
356     z3950_package.move();
357
358     // check successful close response
359     //Z_GDU *z3950_gdu = z3950_package.response().get();
360     //if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
361     //    && z3950_gdu->u.z3950->which == Z_APDU_close)
362     //    return true;
363
364     if (z3950_package.session().is_closed()){
365         return true;
366     }
367     return false;
368 }
369
370 bool 
371 yf::SRUtoZ3950::Impl::z3950_search_request(mp::Package &package,  
372                                           mp::odr &odr_en,
373                                           Z_SRW_PDU *sru_pdu_res,
374                                           Z_SRW_searchRetrieveRequest 
375                                           const *sr_req) const
376 {
377
378     assert(sru_pdu_res->u.response);
379
380     Package z3950_package(package.session(), package.origin());
381     z3950_package.copy_filter(package);
382
383     //add stuff in z3950 apdu
384     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
385     Z_SearchRequest *z_searchRequest = apdu->u.searchRequest;
386
387     // z3950'fy database
388     z_searchRequest->num_databaseNames = 1;
389     z_searchRequest->databaseNames = (char**)
390         odr_malloc(odr_en, sizeof(char *));
391
392     if (sr_req->database)
393         z_searchRequest->databaseNames[0] 
394             = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
395     else
396         z_searchRequest->databaseNames[0] 
397             = odr_strdup(odr_en, "Default");
398
399
400     // z3950'fy query
401     Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
402     z_searchRequest->query = z_query;
403  
404     if (!z3950_build_query(odr_en, z_query, 
405                            (const SRW_query&)sr_req->query, 
406                            sr_req->query_type))
407     {    
408         yaz_add_srw_diagnostic(odr_en,
409                                &(sru_pdu_res->u.response->diagnostics), 
410                                &(sru_pdu_res->u.response->num_diagnostics), 
411                                7, "query");
412         return false;
413     }
414
415     z3950_package.request() = apdu;
416         
417     // send Z39.50 package off to backend
418     z3950_package.move();
419
420
421     Z_GDU *z3950_gdu = z3950_package.response().get();
422
423     //TODO: check success condition
424     //int yaz_diag_bib1_to_srw (int bib1_code);
425     //int yaz_diag_srw_to_bib1(int srw_code);
426     //Se kode i src/seshigh.c (srw_bend_search, srw_bend_init).
427
428     if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950 
429         || z3950_gdu->u.z3950->which != Z_APDU_searchResponse
430         || !z3950_gdu->u.z3950->u.searchResponse
431         || !z3950_gdu->u.z3950->u.searchResponse->searchStatus)
432     {
433         yaz_add_srw_diagnostic(odr_en,
434                                &(sru_pdu_res->u.response->diagnostics),
435                                &(sru_pdu_res->u.response->num_diagnostics),
436                                2, 0);
437         return false;
438     }
439     
440     // everything fine, continuing
441     Z_SearchResponse *sr = z3950_gdu->u.z3950->u.searchResponse;
442
443     // checking non surrogate diagnostics in Z3950 search response package
444     if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response, 
445                                      sr->records))
446     {
447         return false;
448     }
449
450     // Finally, roll on and srw'fy number of records
451     sru_pdu_res->u.response->numberOfRecords 
452         = (int *) odr_malloc(odr_en, sizeof(int *));
453     *(sru_pdu_res->u.response->numberOfRecords) = *(sr->resultCount);
454     
455     // srw'fy nextRecordPosition
456     //sru_pdu_res->u.response->nextRecordPosition 
457     //    = (int *) odr_malloc(odr_en, sizeof(int *));
458     //*(sru_pdu_res->u.response->nextRecordPosition) = 1;
459
460     return true;
461 }
462
463 bool 
464 yf::SRUtoZ3950::Impl::z3950_present_request(mp::Package &package, 
465                                            mp::odr &odr_en,
466                                            Z_SRW_PDU *sru_pdu_res,
467                                            Z_SRW_searchRetrieveRequest 
468                                            const *sr_req)
469     const
470 {
471     assert(sru_pdu_res->u.response);
472
473     if (!sr_req)
474         return false;
475
476     
477     // no need to work if nobody wants record ..
478     if (!(sr_req->maximumRecords) || 0 == *(sr_req->maximumRecords))
479         return true;
480
481     bool send_z3950_present = true;
482
483     // recordXPath unsupported.
484     if (sr_req->recordXPath)
485     {
486         send_z3950_present = false;
487         yaz_add_srw_diagnostic(odr_en,
488                                &(sru_pdu_res->u.response->diagnostics), 
489                                &(sru_pdu_res->u.response->num_diagnostics), 
490                                72, 0);
491     }
492     
493     // resultSetTTL unsupported.
494     // resultSetIdleTime in response
495     if (sr_req->resultSetTTL)
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                                50, 0);
502     }
503     
504     // sort unsupported
505     if (sr_req->sort_type != Z_SRW_sort_type_none)
506     {
507         send_z3950_present = false;
508         yaz_add_srw_diagnostic(odr_en,
509                                &(sru_pdu_res->u.response->diagnostics), 
510                                &(sru_pdu_res->u.response->num_diagnostics), 
511                                80, 0);
512     }
513     
514     // start record requested negative, or larger than number of records
515     if (sr_req->startRecord 
516         && 
517         ((*(sr_req->startRecord) < 0)       // negative
518          ||
519          (sru_pdu_res->u.response->numberOfRecords  //out of range
520           && *(sr_req->startRecord) 
521           > *(sru_pdu_res->u.response->numberOfRecords))
522         ))
523     {
524         send_z3950_present = false;
525         yaz_add_srw_diagnostic(odr_en,
526                                &(sru_pdu_res->u.response->diagnostics), 
527                                &(sru_pdu_res->u.response->num_diagnostics), 
528                                61, 0);
529     }    
530
531     // maximumRecords requested negative
532     if (sr_req->maximumRecords
533         && *(sr_req->maximumRecords) < 0) 
534           
535     {
536         send_z3950_present = false;
537         yaz_add_srw_diagnostic(odr_en,
538                                &(sru_pdu_res->u.response->diagnostics), 
539                                &(sru_pdu_res->u.response->num_diagnostics), 
540                                6, "maximumRecords");
541     }    
542
543     // exit on all these above diagnostics
544     if (!send_z3950_present)
545         return false;
546
547     // now packaging the z3950 present request
548     Package z3950_package(package.session(), package.origin());
549     z3950_package.copy_filter(package); 
550     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
551
552     assert(apdu->u.presentRequest);
553
554     // z3950'fy start record position
555     if (sr_req->startRecord)
556         *(apdu->u.presentRequest->resultSetStartPoint) 
557             = *(sr_req->startRecord);
558     else 
559         *(apdu->u.presentRequest->resultSetStartPoint) = 1;
560     
561     // z3950'fy number of records requested 
562     // protect against requesting records out of range
563     if (sr_req->maximumRecords)
564         *(apdu->u.presentRequest->numberOfRecordsRequested) 
565             = std::min(*(sr_req->maximumRecords), 
566                   *(sru_pdu_res->u.response->numberOfRecords)
567                   - *(apdu->u.presentRequest->resultSetStartPoint)
568                   + 1);
569      
570     // z3950'fy recordPacking
571     int record_packing = Z_SRW_recordPacking_XML;
572     if (sr_req->recordPacking && 's' == *(sr_req->recordPacking))
573         record_packing = Z_SRW_recordPacking_string;
574
575     // RecordSyntax will always be XML
576     (apdu->u.presentRequest->preferredRecordSyntax)
577         = yaz_oidval_to_z3950oid (odr_en, CLASS_RECSYN, VAL_TEXT_XML);
578
579     // z3950'fy record schema
580      if (sr_req->recordSchema)
581      {
582          apdu->u.presentRequest->recordComposition 
583              = (Z_RecordComposition *) 
584                odr_malloc(odr_en, sizeof(Z_RecordComposition));
585          apdu->u.presentRequest->recordComposition->which 
586              = Z_RecordComp_simple;
587          apdu->u.presentRequest->recordComposition->u.simple 
588              = mp_util::build_esn_from_schema(odr_en,
589                                       (const char *) sr_req->recordSchema); 
590      }
591
592     // z3950'fy time to live - flagged as diagnostics above
593     //if (sr_req->resultSetTTL)
594
595     // attaching Z3950 package to filter chain
596     z3950_package.request() = apdu;
597
598     // sending Z30.50 present request 
599     z3950_package.move();
600
601     //check successful Z3950 present response
602     Z_GDU *z3950_gdu = z3950_package.response().get();
603     if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950 
604         || z3950_gdu->u.z3950->which != Z_APDU_presentResponse
605         || !z3950_gdu->u.z3950->u.presentResponse)
606
607     {
608         yaz_add_srw_diagnostic(odr_en,
609                                &(sru_pdu_res->u.response->diagnostics), 
610                                &(sru_pdu_res->u.response->num_diagnostics), 
611                                2, 0);
612         return false;
613     }
614
615
616     // everything fine, continuing
617
618     Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
619     Z_SRW_searchRetrieveResponse *sru_res = sru_pdu_res->u.response;
620         
621
622     // checking non surrogate diagnostics in Z3950 present response package
623     if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response, 
624                                      pr->records))
625         return false;
626     
627
628     
629     // copy all records if existing
630     if (pr->records && pr->records->which == Z_Records_DBOSD)
631     {
632         // srw'fy number of returned records
633         sru_res->num_records
634             = pr->records->u.databaseOrSurDiagnostics->num_records;
635         
636         sru_res->records 
637             = (Z_SRW_record *) odr_malloc(odr_en, 
638                                           sru_res->num_records 
639                                              * sizeof(Z_SRW_record));
640         
641
642         // srw'fy nextRecordPosition
643         // next position never zero or behind the last z3950 record 
644         if (pr->nextResultSetPosition
645             && *(pr->nextResultSetPosition) > 0 
646             && *(pr->nextResultSetPosition) 
647                <= *(sru_pdu_res->u.response->numberOfRecords))
648             sru_res->nextRecordPosition 
649                 = odr_intdup(odr_en, *(pr->nextResultSetPosition));
650         
651         // inserting all records
652         for (int i = 0; i < sru_res->num_records; i++)
653         {
654             Z_NamePlusRecord *npr 
655                 = pr->records->u.databaseOrSurDiagnostics->records[i];
656             
657             sru_res->records[i].recordPosition 
658                 = odr_intdup(odr_en,
659                            i + *(apdu->u.presentRequest->resultSetStartPoint));
660             
661             sru_res->records[i].recordPacking = record_packing;
662             
663             if (npr->which != Z_NamePlusRecord_databaseRecord)
664             {
665                 sru_res->records[i].recordSchema = "diagnostic";
666                 sru_res->records[i].recordData_buf = "67";
667                 sru_res->records[i].recordData_len = 2;
668             }
669             else
670             {
671                 Z_External *r = npr->u.databaseRecord;
672                 oident *ent = oid_getentbyoid(r->direct_reference);
673                 if (r->which == Z_External_octet 
674                     && ent->value == VAL_TEXT_XML)
675                 {
676                     sru_res->records[i].recordSchema = "dc";
677                     sru_res->records[i].recordData_buf
678                         = odr_strdupn(odr_en, 
679                                       (const char *)r->u.octet_aligned->buf, 
680                                       r->u.octet_aligned->len);
681                     sru_res->records[i].recordData_len 
682                         = r->u.octet_aligned->len;
683                 }
684                 else
685                 {
686                     sru_res->records[i].recordSchema = "diagnostic";
687                     sru_res->records[i].recordData_buf = "67";
688                     sru_res->records[i].recordData_len = 2;
689                 }
690             }   
691         }    
692     }
693     
694     return true;
695 }
696
697 bool 
698 yf::SRUtoZ3950::Impl::z3950_scan_request(mp::Package &package,
699                                         mp::odr &odr_en,
700                                         Z_SRW_PDU *sru_pdu_res,
701                                         Z_SRW_scanRequest const *sr_req) const 
702 {
703     assert(sru_pdu_res->u.scan_response);
704
705     Package z3950_package(package.session(), package.origin());
706     z3950_package.copy_filter(package); 
707     //mp::odr odr_en(ODR_ENCODE);
708     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
709
710     //TODO: add stuff in apdu
711     Z_ScanRequest *z_scanRequest = apdu->u.scanRequest;
712
713     // database repackaging
714     z_scanRequest->num_databaseNames = 1;
715     z_scanRequest->databaseNames = (char**)
716         odr_malloc(odr_en, sizeof(char *));
717     if (sr_req->database)
718         z_scanRequest->databaseNames[0] 
719             = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
720     else
721         z_scanRequest->databaseNames[0] 
722             = odr_strdup(odr_en, "Default");
723
724
725     // query repackaging
726     // CQL or XCQL scan is not possible in Z3950, flagging a diagnostic
727     if (sr_req->query_type != Z_SRW_query_type_pqf)
728     {        
729         //send_to_srw_client_error(7, "query");
730         return false;
731     }
732
733     // PQF query repackaging
734     // need to use Z_AttributesPlusTerm structure, not Z_Query
735     // this can be digget out of a 
736     // Z_query->type1(Z_RPNQuery)->RPNStructure(Z_RPNStructure)
737     //   ->u.simple(Z_Operand)->u.attributesPlusTerm(Z_AttributesPlusTerm )
738
739     //Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
740     //z_searchRequest->query = z_query;
741
742     //if (!z3950_build_query(odr_en, z_query, 
743     //                       (const SRW_query&)sr_req->query, 
744     //                       sr_req->query_type))
745     //{    
746         //send_to_srw_client_error(7, "query");
747     //    return false;
748     //}
749
750     // TODO: 
751
752     z3950_package.request() = apdu;
753     std::cout << "z3950_scan_request " << *apdu << "\n";   
754
755     z3950_package.move();
756     //TODO: check success condition
757     return true;
758     return false;
759 }
760
761 bool yf::SRUtoZ3950::Impl::z3950_build_query(mp::odr &odr_en, Z_Query *z_query, 
762                                             const SRW_query &query, 
763                                             SRW_query_type query_type) const
764 {        
765     if (query_type == Z_SRW_query_type_cql)
766     {
767         Z_External *ext = (Z_External *) 
768             odr_malloc(odr_en, sizeof(*ext));
769         ext->direct_reference = 
770             odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
771         ext->indirect_reference = 0;
772         ext->descriptor = 0;
773         ext->which = Z_External_CQL;
774         ext->u.cql = const_cast<char *>(query.cql);
775         
776         z_query->which = Z_Query_type_104;
777         z_query->u.type_104 =  ext;
778         return true;
779     }
780
781     if (query_type == Z_SRW_query_type_pqf)
782     {
783         Z_RPNQuery *RPNquery;
784         YAZ_PQF_Parser pqf_parser;
785         
786         pqf_parser = yaz_pqf_create ();
787         
788         RPNquery = yaz_pqf_parse (pqf_parser, odr_en, query.pqf);
789         if (!RPNquery)
790         {
791             std::cout << "TODO: Handeling of bad PQF\n";
792             std::cout << "TODO: Diagnostic to be send\n";
793         }
794         z_query->which = Z_Query_type_1;
795         z_query->u.type_1 =  RPNquery;
796         
797         yaz_pqf_destroy(pqf_parser);
798         return true;
799     }
800     return false;
801 }
802
803
804 bool 
805 yf::SRUtoZ3950::Impl::z3950_to_srw_diagnostics_ok(mp::odr &odr_en, 
806                                                   Z_SRW_searchRetrieveResponse 
807                                                   *sru_res,
808                                                   Z_Records *records) const
809 {
810     // checking non surrogate diagnostics in Z3950 present response package
811     if (records 
812         && records->which == Z_Records_NSD
813         && records->u.nonSurrogateDiagnostic)
814     {
815         z3950_to_srw_diag(odr_en, sru_res, 
816                           records->u.nonSurrogateDiagnostic);
817         return false;
818     }
819     return true;
820 }
821
822
823 int 
824 yf::SRUtoZ3950::Impl::z3950_to_srw_diag(mp::odr &odr_en, 
825                                        Z_SRW_searchRetrieveResponse *sru_res,
826                                        Z_DefaultDiagFormat *ddf) const
827 {
828     int bib1_code = *ddf->condition;
829     sru_res->num_diagnostics = 1;
830     sru_res->diagnostics = (Z_SRW_diagnostic *)
831         odr_malloc(odr_en, sizeof(*sru_res->diagnostics));
832     yaz_mk_std_diagnostic(odr_en, sru_res->diagnostics,
833                           yaz_diag_bib1_to_srw(bib1_code), 
834                           ddf->u.v2Addinfo);
835     return 0;
836 }
837
838
839
840 static mp::filter::Base* filter_creator()
841 {
842     return new mp::filter::SRUtoZ3950;
843 }
844
845 extern "C" {
846     struct metaproxy_1_filter_struct metaproxy_1_filter_sru_to_z3950 = {
847         0,
848         "sru_z3950",
849         filter_creator
850     };
851 }
852
853
854 /*
855  * Local variables:
856  * c-basic-offset: 4
857  * indent-tabs-mode: nil
858  * c-file-style: "stroustrup"
859  * End:
860  * vim: shiftwidth=4 tabstop=8 expandtab
861  */