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