Fix assertion failed: assert(m_in_use) MP-603
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 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/srw.h>
30 #include <yaz/pquery.h>
31 #include <yaz/oid_db.h>
32 #include <yaz/log.h>
33 #include <yaz/otherinfo.h>
34 #if YAZ_VERSIONL >= 0x50000
35 #include <yaz/facet.h>
36 #endif
37
38 #include <boost/thread/mutex.hpp>
39 #include <boost/thread/condition.hpp>
40
41 #include <iostream>
42 #include <sstream>
43 #include <string>
44 #include <map>
45
46 namespace mp = metaproxy_1;
47 namespace mp_util = metaproxy_1::util;
48 namespace yf = mp::filter;
49
50 namespace metaproxy_1 {
51     namespace filter {
52         class SRUtoZ3950::Frontend : boost::noncopyable {
53             friend class Impl;
54             bool m_in_use;
55         public:
56             Frontend();
57             ~Frontend();
58         };
59         class SRUtoZ3950::Impl {
60         public:
61             void configure(const xmlNode *xmlnode);
62             void process(metaproxy_1::Package &package);
63         private:
64             FrontendPtr get_frontend(mp::Package &package);
65             void release_frontend(mp::Package &package);
66             std::map<std::string, const xmlNode *> m_database_explain;
67             std::string default_stylesheet;
68
69             typedef std::map<std::string, int> ActiveUrlMap;
70
71             boost::mutex m_mutex_session;
72             boost::condition m_cond_session_ready;
73             std::map<mp::Session, FrontendPtr> m_clients;
74         private:
75             void sru(metaproxy_1::Package &package, Z_GDU *zgdu_req);
76             int z3950_build_query(
77                 mp::odr &odr_en, Z_Query *z_query,
78                 const Z_SRW_searchRetrieveRequest *req
79                 ) const;
80
81             bool z3950_init_request(
82                 mp::Package &package,
83                 mp::odr &odr_en,
84                 std::string zurl,
85                 Z_SRW_PDU *sru_pdu_res,
86                 const Z_SRW_PDU *sru_pdu_req
87                 ) const;
88
89             bool z3950_close_request(mp::Package &package) const;
90
91             bool z3950_search_request(
92                 mp::Package &package,
93                 mp::Package &z3950_package,
94                 mp::odr &odr_en,
95                 Z_SRW_PDU *sru_pdu_res,
96                 Z_SRW_searchRetrieveRequest const *sr_req,
97                 std::string zurl,
98                 std::string db_append
99                 ) const;
100
101             bool z3950_present_request(
102                 mp::Package &package,
103                 mp::odr &odr_en,
104                 Z_SRW_PDU *sru_pdu_res,
105                 Z_SRW_searchRetrieveRequest const *sr_req
106                 ) const;
107
108             bool z3950_to_srw_diagnostics_ok(
109                 mp::odr &odr_en,
110                 Z_SRW_searchRetrieveResponse *srw_res,
111                 Z_Records *records
112                 ) const;
113
114             int z3950_to_srw_diag(
115                 mp::odr &odr_en,
116                 Z_SRW_searchRetrieveResponse *srw_res,
117                 Z_DefaultDiagFormat *ddf
118                 ) const;
119
120         };
121     }
122 }
123
124 yf::SRUtoZ3950::SRUtoZ3950() : m_p(new Impl)
125 {
126 }
127
128 yf::SRUtoZ3950::~SRUtoZ3950()
129 {  // must have a destructor because of boost::scoped_ptr
130 }
131
132 void yf::SRUtoZ3950::configure(const xmlNode *xmlnode, bool test_only,
133                                const char *path)
134 {
135     m_p->configure(xmlnode);
136 }
137
138 void yf::SRUtoZ3950::process(mp::Package &package) const
139 {
140     m_p->process(package);
141 }
142
143 void yf::SRUtoZ3950::Impl::configure(const xmlNode *confignode)
144 {
145     const xmlNode * dbnode;
146
147     for (dbnode = confignode->children; dbnode; dbnode = dbnode->next)
148     {
149         if (dbnode->type != XML_ELEMENT_NODE)
150             continue;
151
152         if (!strcmp((const char *) dbnode->name, "database"))
153         {
154             std::string database;
155
156             for (struct _xmlAttr *attr = dbnode->properties;
157                  attr; attr = attr->next)
158             {
159                 mp::xml::check_attribute(attr, "", "name");
160                 database = mp::xml::get_text(attr);
161
162                 const xmlNode *explainnode;
163                 for (explainnode = dbnode->children;
164                      explainnode; explainnode = explainnode->next)
165                 {
166                     if (explainnode->type == XML_ELEMENT_NODE)
167                     {
168                         m_database_explain.insert(
169                             std::make_pair(database, explainnode));
170                         break;
171                     }
172                 }
173             }
174         }
175         else if (!strcmp((const char *) dbnode->name, "stylesheet"))
176         {
177             default_stylesheet = mp::xml::get_text(dbnode);
178         }
179         else
180         {
181             throw mp::filter::FilterException
182                 ("Bad element "
183                  + std::string((const char *) dbnode->name)
184                  + " in sru_z3950"
185                     );
186         }
187     }
188 }
189
190 void yf::SRUtoZ3950::Impl::sru(mp::Package &package, Z_GDU *zgdu_req)
191 {
192     bool ok = true;
193
194     mp::odr odr_de(ODR_DECODE);
195     Z_SRW_PDU *sru_pdu_req = 0;
196
197     mp::odr odr_en(ODR_ENCODE);
198
199     // determine database with the HTTP header information only
200     mp_util::SRUServerInfo sruinfo = mp_util::get_sru_server_info(package);
201     std::map<std::string, const xmlNode *>::iterator idbexp;
202     idbexp = m_database_explain.find(sruinfo.database);
203
204     // assign explain config XML DOM node if database is known
205     const xmlNode *explainnode = 0;
206     if (idbexp != m_database_explain.end())
207     {
208         explainnode = idbexp->second;
209     }
210
211     // decode SRU request
212     Z_SOAP *soap = 0;
213     char *charset = 0;
214     const char *stylesheet = 0;
215     Z_SRW_diagnostic *diagnostic = 0;
216     int num_diagnostic = 0;
217
218     // filter acts as sink for non-valid SRU requests
219     if (! (sru_pdu_req = mp_util::decode_sru_request(package, odr_de, odr_en,
220                                                      &diagnostic,
221                                                      &num_diagnostic, &soap,
222                                                      charset)))
223     {
224         if (soap)
225         {
226             Z_SRW_PDU *sru_pdu_res = yaz_srw_get(odr_en,
227                                                  Z_SRW_explain_response);
228             sru_pdu_res->u.explain_response->diagnostics = diagnostic;
229             sru_pdu_res->u.explain_response->num_diagnostics = num_diagnostic;
230             mp_util::build_sru_explain(package, odr_en, sru_pdu_res,
231                                        sruinfo, explainnode);
232             mp_util::build_sru_response(package, odr_en, soap,
233                                         sru_pdu_res, charset, stylesheet);
234         }
235         else
236         {
237             metaproxy_1::odr odr;
238             Z_GDU *zgdu_res =
239                 odr.create_HTTP_Response(package.session(),
240                                          zgdu_req->u.HTTP_Request, 400);
241             package.response() = zgdu_res;
242         }
243         return;
244     }
245
246     bool enable_package_log = false;
247     std::string zurl;
248     std::string dbargs;
249     Z_SRW_extra_arg *arg;
250
251     for ( arg = sru_pdu_req->extra_args; arg; arg = arg->next)
252         if (!strcmp(arg->name, "x-target"))
253         {
254             zurl = std::string(arg->value);
255         }
256         else if (!strcmp(arg->name, "x-max-sockets"))
257         {
258             package.origin().set_max_sockets(atoi(arg->value));
259         }
260         else if (!strcmp(arg->name, "x-session-id"))
261         {
262             package.origin().set_custom_session(arg->value);
263         }
264         else if (!strcmp(arg->name, "x-log-enable"))
265         {
266             if (*arg->value == '1')
267             {
268                 enable_package_log = true;
269                 package.log_enable();
270             }
271         }
272         else if (!strncmp(arg->name, "x-client-", 9) && arg->value)
273         {
274             if (dbargs.length())
275                 dbargs += '&';
276             dbargs += mp_util::uri_encode(arg->name + 9);
277             dbargs += '=';
278             dbargs += mp_util::uri_encode(arg->value);
279         }
280
281     assert(sru_pdu_req);
282
283     Package z3950_package(package.session(), package.origin());
284     z3950_package.copy_filter(package);
285
286     Z_SRW_PDU *sru_pdu_res = 0;
287     if (sru_pdu_req->which == Z_SRW_explain_request)
288     {
289         Z_SRW_explainRequest *er_req = sru_pdu_req->u.explain_request;
290         stylesheet = er_req->stylesheet;
291         sru_pdu_res = yaz_srw_get_pdu_e(odr_en, Z_SRW_explain_response,
292                                         sru_pdu_req);
293         sru_pdu_res->u.explain_response->diagnostics = diagnostic;
294         sru_pdu_res->u.explain_response->num_diagnostics = num_diagnostic;
295         mp_util::build_sru_explain(package, odr_en, sru_pdu_res,
296                                    sruinfo, explainnode, er_req);
297     }
298     else if (sru_pdu_req->which == Z_SRW_searchRetrieve_request)
299     {
300         Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;
301         stylesheet = sr_req->stylesheet;
302         sru_pdu_res = yaz_srw_get_pdu_e(odr_en, Z_SRW_searchRetrieve_response,
303                                         sru_pdu_req);
304         sru_pdu_res->u.response->diagnostics = diagnostic;
305         sru_pdu_res->u.response->num_diagnostics = num_diagnostic;
306
307         // checking that we have a query
308         ok = mp_util::check_sru_query_exists(package, odr_en,
309                                              sru_pdu_res, sr_req);
310
311         if (ok && z3950_init_request(package, odr_en,
312                                      zurl, sru_pdu_res, sru_pdu_req))
313         {
314             ok = z3950_search_request(package, z3950_package, odr_en,
315                                       sru_pdu_res, sr_req, zurl, dbargs);
316
317             if (ok
318                 && sru_pdu_res->u.response->numberOfRecords
319                 && *(sru_pdu_res->u.response->numberOfRecords))
320
321                 ok = z3950_present_request(package, odr_en,
322                                            sru_pdu_res,
323                                            sr_req);
324             z3950_close_request(package);
325         }
326     }
327     else if (sru_pdu_req->which == Z_SRW_scan_request)
328     {
329         stylesheet = sru_pdu_req->u.scan_request->stylesheet;
330         sru_pdu_res = yaz_srw_get_pdu_e(odr_en, Z_SRW_scan_response,
331                                         sru_pdu_req);
332         sru_pdu_res->u.scan_response->diagnostics = diagnostic;
333         sru_pdu_res->u.scan_response->num_diagnostics = num_diagnostic;
334
335         // we do not do scan at the moment, therefore issuing a diagnostic
336         yaz_add_srw_diagnostic(odr_en, 
337                                &sru_pdu_res->u.scan_response->diagnostics,
338                                &sru_pdu_res->u.scan_response->num_diagnostics,
339                                YAZ_SRW_UNSUPP_OPERATION, "scan");
340     }
341     else
342     {
343         sru_pdu_res =
344             yaz_srw_get_pdu_e(odr_en, Z_SRW_explain_response, sru_pdu_req);
345         sru_pdu_res->u.explain_response->diagnostics = diagnostic;
346         sru_pdu_res->u.explain_response->num_diagnostics = num_diagnostic;
347         yaz_add_srw_diagnostic(odr_en, &diagnostic, &num_diagnostic,
348                                YAZ_SRW_UNSUPP_OPERATION, "unknown");
349     }
350     if (enable_package_log)
351     {
352         std::string l;
353         package.log_reset(l);
354         if (l.length())
355         {
356             mp::wrbuf w;
357
358             wrbuf_puts(w, "<log>\n");
359             wrbuf_xmlputs(w, l.c_str());
360             wrbuf_puts(w, "</log>");
361
362             sru_pdu_res->extraResponseData_len = w.len();
363             sru_pdu_res->extraResponseData_buf =
364                 odr_strdup(odr_en, wrbuf_cstr(w));
365         }
366     }
367     if (!stylesheet && default_stylesheet.length())
368         stylesheet = default_stylesheet.c_str();
369
370     // build and send SRU response
371     mp_util::build_sru_response(package, odr_en, soap,
372                                 sru_pdu_res, charset, stylesheet);
373 }
374
375
376 yf::SRUtoZ3950::Frontend::Frontend() :  m_in_use(true)
377 {
378 }
379
380 yf::SRUtoZ3950::Frontend::~Frontend()
381 {
382 }
383
384
385 yf::SRUtoZ3950::FrontendPtr yf::SRUtoZ3950::Impl::get_frontend(
386     mp::Package &package)
387 {
388     boost::mutex::scoped_lock lock(m_mutex_session);
389
390     std::map<mp::Session,yf::SRUtoZ3950::FrontendPtr>::iterator it;
391
392     while (true)
393     {
394         it = m_clients.find(package.session());
395         if (it == m_clients.end())
396             break;
397
398         if (!it->second->m_in_use)
399         {
400             it->second->m_in_use = true;
401             return it->second;
402         }
403         m_cond_session_ready.wait(lock);
404     }
405     FrontendPtr f(new Frontend);
406     m_clients[package.session()] = f;
407     f->m_in_use = true;
408     return f;
409 }
410
411 void yf::SRUtoZ3950::Impl::release_frontend(mp::Package &package)
412 {
413     boost::mutex::scoped_lock lock(m_mutex_session);
414     std::map<mp::Session,FrontendPtr>::iterator it;
415
416     it = m_clients.find(package.session());
417     if (it != m_clients.end())
418     {
419         if (package.session().is_closed())
420         {
421             m_clients.erase(it);
422         }
423         else
424         {
425             it->second->m_in_use = false;
426         }
427         m_cond_session_ready.notify_all();
428     }
429 }
430
431 void yf::SRUtoZ3950::Impl::process(mp::Package &package)
432 {
433     FrontendPtr f = get_frontend(package);
434
435     Z_GDU *zgdu_req = package.request().get();
436
437     if (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
438     {
439         sru(package, zgdu_req);
440     }
441     else
442     {
443         package.move();
444     }
445     release_frontend(package);
446 }
447
448 bool
449 yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package,
450                                          mp::odr &odr_en,
451                                          std::string zurl,
452                                          Z_SRW_PDU *sru_pdu_res,
453                                          const Z_SRW_PDU *sru_pdu_req) const
454 {
455     // prepare Z3950 package
456     Package z3950_package(package.session(), package.origin());
457     z3950_package.copy_filter(package);
458
459     // set initRequest APDU
460     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
461     Z_InitRequest *init_req = apdu->u.initRequest;
462
463     Z_IdAuthentication *auth = NULL;
464     if (sru_pdu_req->username && !sru_pdu_req->password)
465     {
466         auth = (Z_IdAuthentication *) odr_malloc(odr_en, sizeof(Z_IdAuthentication));
467         auth->which = Z_IdAuthentication_open;
468         auth->u.open = odr_strdup(odr_en, sru_pdu_req->username);
469     }
470     else if (sru_pdu_req->username && sru_pdu_req->password)
471     {
472         auth = (Z_IdAuthentication *) odr_malloc(odr_en, sizeof(Z_IdAuthentication));
473         auth->which = Z_IdAuthentication_idPass;
474         auth->u.idPass = (Z_IdPass *) odr_malloc(odr_en, sizeof(Z_IdPass));
475         auth->u.idPass->groupId = NULL;
476         auth->u.idPass->password = odr_strdup(odr_en, sru_pdu_req->password);
477         auth->u.idPass->userId = odr_strdup(odr_en, sru_pdu_req->username);
478     }
479
480     init_req->idAuthentication = auth;
481
482     *init_req->preferredMessageSize = 10*1024*1024;
483     *init_req->maximumRecordSize = 10*1024*1024;
484
485     ODR_MASK_SET(init_req->options, Z_Options_search);
486     ODR_MASK_SET(init_req->options, Z_Options_present);
487     ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
488     ODR_MASK_SET(init_req->options, Z_Options_scan);
489
490     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
491     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
492     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
493
494     if (zurl.length())
495     {
496         std::string host;
497         std::list<std::string> dblist;
498         mp_util::split_zurl(zurl, host, dblist);
499         mp_util::set_vhost_otherinfo(&init_req->otherInfo, odr_en, host, 1);
500     }
501
502     Z_GDU *zgdu_req = package.request().get();
503     if (zgdu_req->which == Z_GDU_HTTP_Request)
504     {
505         Z_HTTP_Request *hreq = zgdu_req->u.HTTP_Request;
506         const char *peer_name =
507             z_HTTP_header_lookup(hreq->headers, "X-Forwarded-For");
508         if (peer_name)
509         {
510             yaz_oi_set_string_oid(&init_req->otherInfo, odr_en,
511                                   yaz_oid_userinfo_client_ip, 1, peer_name);
512         }
513     }
514
515     z3950_package.request() = apdu;
516
517     // send Z3950 package
518     z3950_package.move();
519
520     // check successful initResponse
521     Z_GDU *z3950_gdu = z3950_package.response().get();
522
523     int error = YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE;
524     const char *addinfo = 0;
525     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
526         && z3950_gdu->u.z3950->which == Z_APDU_initResponse)
527     {
528         Z_InitResponse *initrs = z3950_gdu->u.z3950->u.initResponse;
529         if (*initrs->result)
530             return true;
531         int no = 0;
532         while (1)
533         {
534             Z_DefaultDiagFormat *df = yaz_decode_init_diag(no, initrs);
535
536             if (!df)
537                 break;
538             yaz_add_srw_diagnostic(odr_en,
539                                    &(sru_pdu_res->u.response->diagnostics),
540                                    &(sru_pdu_res->u.response->num_diagnostics),
541                                    yaz_diag_bib1_to_srw(*df->condition),
542                                    df->u.v2Addinfo);
543             no++;
544         }
545         if (no)
546             return false; // got least one diagnostic from init
547
548         // we just have result=false.
549         error = YAZ_SRW_AUTHENTICATION_ERROR;
550     }
551     else
552         addinfo = "sru_z3950: expected initResponse";
553     yaz_add_srw_diagnostic(odr_en,
554                            &(sru_pdu_res->u.response->diagnostics),
555                            &(sru_pdu_res->u.response->num_diagnostics),
556                            error, addinfo);
557     return false;
558 }
559
560 bool yf::SRUtoZ3950::Impl::z3950_close_request(mp::Package &package) const
561 {
562     Package z3950_package(package.session(), package.origin());
563     z3950_package.copy_filter(package);
564     z3950_package.session().close();
565
566     z3950_package.move();
567
568     if (z3950_package.session().is_closed())
569     {
570         return true;
571     }
572     return false;
573 }
574
575 bool yf::SRUtoZ3950::Impl::z3950_search_request(mp::Package &package,
576                                                 mp::Package &z3950_package,
577                                                 mp::odr &odr_en,
578                                                 Z_SRW_PDU *sru_pdu_res,
579                                                 Z_SRW_searchRetrieveRequest
580                                                 const *sr_req,
581                                                 std::string zurl,
582                                                 std::string dbappend) const
583 {
584
585     assert(sru_pdu_res->u.response);
586
587     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
588     Z_SearchRequest *z_searchRequest = apdu->u.searchRequest;
589
590     // RecordSyntax will always be XML
591     z_searchRequest->preferredRecordSyntax
592         = odr_oiddup(odr_en, yaz_oid_recsyn_xml);
593
594     if (!mp_util::set_databases_from_zurl(odr_en, zurl,
595                                           &z_searchRequest->num_databaseNames,
596                                           &z_searchRequest->databaseNames))
597     {
598         std::string db;
599
600         if (sr_req->database)
601             db = sr_req->database;
602         else
603             db = "Default";
604
605         if (dbappend.length())
606         {
607             db += ",";
608             db += dbappend;
609         }
610         z_searchRequest->num_databaseNames = 1;
611         z_searchRequest->databaseNames = (char**)
612             odr_malloc(odr_en, sizeof(char *));
613         z_searchRequest->databaseNames[0] = odr_strdup(odr_en, db.c_str());
614     }
615 #if YAZ_VERSIONL >= 0x50000
616     // yaz_oi_set_facetlist not public in YAZ 4.2.66
617     if (sr_req->facetList)
618     {
619         Z_OtherInformation **oi = &z_searchRequest->additionalSearchInfo;
620         yaz_oi_set_facetlist(oi, odr_en, sr_req->facetList);
621     }
622 #endif
623
624     Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
625     z_searchRequest->query = z_query;
626
627     int sru_diagnostic = z3950_build_query(odr_en, z_query, sr_req);
628     if (sru_diagnostic)
629     {
630         yaz_add_srw_diagnostic(odr_en,
631                                &(sru_pdu_res->u.response->diagnostics),
632                                &(sru_pdu_res->u.response->num_diagnostics),
633                                sru_diagnostic,
634                                "query");
635         return false;
636     }
637
638     z3950_package.request() = apdu;
639
640     z3950_package.move();
641
642     Z_GDU *z3950_gdu = z3950_package.response().get();
643
644     if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950
645         || z3950_gdu->u.z3950->which != Z_APDU_searchResponse
646         || !z3950_gdu->u.z3950->u.searchResponse
647         || !z3950_gdu->u.z3950->u.searchResponse->searchStatus)
648     {
649         yaz_add_srw_diagnostic(odr_en,
650                                &(sru_pdu_res->u.response->diagnostics),
651                                &(sru_pdu_res->u.response->num_diagnostics),
652                                YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, 0);
653         return false;
654     }
655
656     Z_SearchResponse *sr = z3950_gdu->u.z3950->u.searchResponse;
657
658     if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response,
659                                      sr->records))
660     {
661         return false;
662     }
663 #if YAZ_VERSIONL >= 0x50000
664     Z_FacetList *fl = yaz_oi_get_facetlist(&sr->additionalSearchInfo);
665     if (!fl)
666         fl =  yaz_oi_get_facetlist(&sr->otherInfo);
667     sru_pdu_res->u.response->facetList = fl;
668 #endif
669     sru_pdu_res->u.response->numberOfRecords
670         = odr_intdup(odr_en, *sr->resultCount);
671     return true;
672 }
673
674 bool yf::SRUtoZ3950::Impl::z3950_present_request(
675     mp::Package &package,
676     mp::odr &odr_en,
677     Z_SRW_PDU *sru_pdu_res,
678     const Z_SRW_searchRetrieveRequest *sr_req)
679     const
680 {
681     assert(sru_pdu_res->u.response);
682     int start = 1;
683     int max_recs = 0;
684
685     if (!sr_req)
686         return false;
687
688     if (sr_req->maximumRecords)
689         max_recs = *sr_req->maximumRecords;
690     if (sr_req->startRecord)
691         start = *sr_req->startRecord;
692
693     // no need to work if nobody wants record ..
694     if (max_recs == 0)
695         return true;
696
697     bool send_z3950_present = true;
698
699     // recordXPath unsupported.
700     if (sr_req->recordXPath)
701     {
702         send_z3950_present = false;
703         yaz_add_srw_diagnostic(odr_en,
704                                &(sru_pdu_res->u.response->diagnostics),
705                                &(sru_pdu_res->u.response->num_diagnostics),
706                                YAZ_SRW_XPATH_RETRIEVAL_UNSUPP, 0);
707     }
708
709     // resultSetTTL unsupported.
710     // resultSetIdleTime in response
711     if (sr_req->resultSetTTL)
712     {
713         send_z3950_present = false;
714         yaz_add_srw_diagnostic(odr_en,
715                                &(sru_pdu_res->u.response->diagnostics),
716                                &(sru_pdu_res->u.response->num_diagnostics),
717                                YAZ_SRW_RESULT_SETS_UNSUPP, 0);
718     }
719
720     // sort unsupported
721     if (sr_req->sort_type != Z_SRW_sort_type_none)
722     {
723         send_z3950_present = false;
724         yaz_add_srw_diagnostic(odr_en,
725                                &(sru_pdu_res->u.response->diagnostics),
726                                &(sru_pdu_res->u.response->num_diagnostics),
727                                YAZ_SRW_SORT_UNSUPP, 0);
728     }
729
730     // start record requested negative, or larger than number of records
731     if (start < 0 || start > *sru_pdu_res->u.response->numberOfRecords)
732     {
733         send_z3950_present = false;
734         yaz_add_srw_diagnostic(odr_en,
735                                &(sru_pdu_res->u.response->diagnostics),
736                                &(sru_pdu_res->u.response->num_diagnostics),
737                                YAZ_SRW_FIRST_RECORD_POSITION_OUT_OF_RANGE, 0);
738     }
739
740     // maximumRecords requested negative
741     if (max_recs < 0)
742     {
743         send_z3950_present = false;
744         yaz_add_srw_diagnostic(odr_en,
745                                &(sru_pdu_res->u.response->diagnostics),
746                                &(sru_pdu_res->u.response->num_diagnostics),
747                                YAZ_SRW_UNSUPP_PARAMETER_VALUE,
748                                "maximumRecords");
749     }
750
751     // exit on all these above diagnostics
752     if (!send_z3950_present)
753         return false;
754
755     if (max_recs > *sru_pdu_res->u.response->numberOfRecords - start)
756         max_recs = *sru_pdu_res->u.response->numberOfRecords - start + 1;
757
758     Z_SRW_searchRetrieveResponse *sru_res = sru_pdu_res->u.response;
759     sru_res->records = (Z_SRW_record *)
760         odr_malloc(odr_en, max_recs * sizeof(Z_SRW_record));
761     int num = 0;
762     while (num < max_recs)
763     {
764         // now packaging the z3950 present request
765         Package z3950_package(package.session(), package.origin());
766         z3950_package.copy_filter(package);
767         Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
768
769         assert(apdu->u.presentRequest);
770
771         *apdu->u.presentRequest->resultSetStartPoint = start + num;
772         *apdu->u.presentRequest->numberOfRecordsRequested = max_recs - num;
773
774         // set response packing to be same as "request" packing..
775         int record_packing = Z_SRW_recordPacking_XML;
776         if (sr_req->recordPacking && 's' == *(sr_req->recordPacking))
777             record_packing = Z_SRW_recordPacking_string;
778
779         // RecordSyntax will always be XML
780         apdu->u.presentRequest->preferredRecordSyntax
781             = odr_oiddup(odr_en, yaz_oid_recsyn_xml);
782
783         // z3950'fy record schema
784         if (sr_req->recordSchema)
785         {
786             apdu->u.presentRequest->recordComposition
787                 = (Z_RecordComposition *)
788                 odr_malloc(odr_en, sizeof(Z_RecordComposition));
789             apdu->u.presentRequest->recordComposition->which
790                 = Z_RecordComp_simple;
791             apdu->u.presentRequest->recordComposition->u.simple
792                 = mp_util::build_esn_from_schema(odr_en,
793                                                  (const char *)
794                                                  sr_req->recordSchema);
795         }
796
797         // attaching Z3950 package to filter chain
798         z3950_package.request() = apdu;
799
800         // sending Z30.50 present request
801         z3950_package.move();
802
803         //check successful Z3950 present response
804         Z_GDU *z3950_gdu = z3950_package.response().get();
805         if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950
806             || z3950_gdu->u.z3950->which != Z_APDU_presentResponse
807             || !z3950_gdu->u.z3950->u.presentResponse)
808
809         {
810             yaz_add_srw_diagnostic(odr_en,
811                                    &(sru_pdu_res->u.response->diagnostics),
812                                    &(sru_pdu_res->u.response->num_diagnostics),
813                                    YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, 0);
814             return false;
815         }
816         // everything fine, continuing
817
818         Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
819
820         // checking non surrogate diagnostics in Z3950 present response package
821         if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response,
822                                          pr->records))
823             return false;
824
825         // if anything but database or surrogate diagnostics, stop
826         if (!pr->records || pr->records->which != Z_Records_DBOSD)
827             break;
828         else
829         {
830             // inserting all records
831             int returned_recs =
832                 pr->records->u.databaseOrSurDiagnostics->num_records;
833             for (int i = 0; i < returned_recs; i++)
834             {
835                 int position = i + *apdu->u.presentRequest->resultSetStartPoint;
836                 Z_NamePlusRecord *npr
837                     = pr->records->u.databaseOrSurDiagnostics->records[i];
838
839                 sru_res->records[i + num].recordPacking = record_packing;
840
841                 if (npr->which == Z_NamePlusRecord_surrogateDiagnostic)
842                 {
843                     Z_DiagRec *p = npr->u.surrogateDiagnostic;
844                     if (p->which == Z_DiagRec_defaultFormat)
845                     {
846                         Z_DefaultDiagFormat *df = p->u.defaultFormat;
847                         int c = yaz_diag_bib1_to_srw(*df->condition);
848
849                         yaz_mk_sru_surrogate(
850                             odr_en, sru_res->records + i + num, position,
851                             c, df->u.v2Addinfo);
852                     }
853                     else
854                     {
855                         yaz_mk_sru_surrogate(
856                             odr_en, sru_res->records + i + num, position,
857                             YAZ_SRW_RECORD_TEMPORARILY_UNAVAILABLE, 0);
858                     }
859                 }
860                 else if (npr->which == Z_NamePlusRecord_databaseRecord &&
861                     npr->u.databaseRecord->direct_reference
862                     && !oid_oidcmp(npr->u.databaseRecord->direct_reference,
863                                    yaz_oid_recsyn_xml))
864                 {
865                     // got XML record back
866                     Z_External *r = npr->u.databaseRecord;
867                     sru_res->records[i + num].recordPosition =
868                         odr_intdup(odr_en, position);
869                     sru_res->records[i + num].recordSchema = sr_req->recordSchema;
870                     sru_res->records[i + num].recordData_buf
871                         = odr_strdupn(odr_en,
872                                       (const char *)r->u.octet_aligned->buf,
873                                       r->u.octet_aligned->len);
874                     sru_res->records[i + num].recordData_len
875                         = r->u.octet_aligned->len;
876                 }
877                 else
878                 {
879                     // not XML or no database record at all
880                     yaz_mk_sru_surrogate(
881                         odr_en, sru_res->records + i + num, position,
882                         YAZ_SRW_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA, 0);
883                 }
884             }
885             num += returned_recs;
886         }
887     }
888     sru_res->num_records = num;
889     if (start - 1 + num < *sru_pdu_res->u.response->numberOfRecords)
890         sru_res->nextRecordPosition =
891             odr_intdup(odr_en, start + num);
892     return true;
893 }
894
895 int yf::SRUtoZ3950::Impl::z3950_build_query(
896     mp::odr &odr_en, Z_Query *z_query,
897     const Z_SRW_searchRetrieveRequest *req
898     ) const
899 {
900     if (
901 #ifdef Z_SRW_query_type_cql
902         req->query_type == Z_SRW_query_type_cql
903 #else
904         !strcmp(req->queryType, "cql")
905 #endif
906         )
907     {
908         Z_External *ext = (Z_External *)
909             odr_malloc(odr_en, sizeof(*ext));
910         ext->direct_reference =
911             odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
912         ext->indirect_reference = 0;
913         ext->descriptor = 0;
914         ext->which = Z_External_CQL;
915         ext->u.cql = odr_strdup(odr_en,
916 #ifdef Z_SRW_query_type_cql
917         req->query.cql
918 #else
919         req->query
920 #endif
921             );
922
923         z_query->which = Z_Query_type_104;
924         z_query->u.type_104 =  ext;
925         return 0;
926     }
927
928     if (
929 #ifdef Z_SRW_query_type_pqf
930         req->query_type == Z_SRW_query_type_pqf
931 #else
932         !strcmp(req->queryType, "pqf")
933 #endif
934         )
935     {
936         Z_RPNQuery *RPNquery;
937         YAZ_PQF_Parser pqf_parser;
938
939         pqf_parser = yaz_pqf_create ();
940
941         RPNquery = yaz_pqf_parse (pqf_parser, odr_en,
942 #ifdef Z_SRW_query_type_pqf
943         req->query.pqf
944 #else
945         req->query
946 #endif
947             );
948         yaz_pqf_destroy(pqf_parser);
949
950         if (!RPNquery)
951             return YAZ_SRW_QUERY_SYNTAX_ERROR;
952
953         z_query->which = Z_Query_type_1;
954         z_query->u.type_1 =  RPNquery;
955
956         return 0;
957     }
958     return YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED;
959 }
960
961 bool yf::SRUtoZ3950::Impl::z3950_to_srw_diagnostics_ok(
962     mp::odr &odr_en,
963     Z_SRW_searchRetrieveResponse
964     *sru_res,
965     Z_Records *records) const
966 {
967     // checking non surrogate diagnostics in Z3950 present response package
968     if (records
969         && records->which == Z_Records_NSD
970         && records->u.nonSurrogateDiagnostic)
971     {
972         z3950_to_srw_diag(odr_en, sru_res,
973                           records->u.nonSurrogateDiagnostic);
974         return false;
975     }
976     return true;
977 }
978
979 int yf::SRUtoZ3950::Impl::z3950_to_srw_diag(
980     mp::odr &odr_en,
981     Z_SRW_searchRetrieveResponse *sru_res,
982     Z_DefaultDiagFormat *ddf) const
983 {
984     int bib1_code = *ddf->condition;
985     sru_res->num_diagnostics = 1;
986     sru_res->diagnostics = (Z_SRW_diagnostic *)
987         odr_malloc(odr_en, sizeof(*sru_res->diagnostics));
988     yaz_mk_std_diagnostic(odr_en, sru_res->diagnostics,
989                           yaz_diag_bib1_to_srw(bib1_code),
990                           ddf->u.v2Addinfo);
991     return 0;
992 }
993
994 static mp::filter::Base* filter_creator()
995 {
996     return new mp::filter::SRUtoZ3950;
997 }
998
999 extern "C" {
1000     struct metaproxy_1_filter_struct metaproxy_1_filter_sru_z3950 = {
1001         0,
1002         "sru_z3950",
1003         filter_creator
1004     };
1005 }
1006
1007
1008 /*
1009  * Local variables:
1010  * c-basic-offset: 4
1011  * c-file-style: "Stroustrup"
1012  * indent-tabs-mode: nil
1013  * End:
1014  * vim: shiftwidth=4 tabstop=8 expandtab
1015  */
1016