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