metaproxy  1.21.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);
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 
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 
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 
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  if (!mp_util::set_databases_from_zurl(odr_en, zurl,
610  &z_searchRequest->num_databaseNames,
611  &z_searchRequest->databaseNames))
612  {
613  std::string db;
614 
615  if (sr_req->database)
616  db = sr_req->database;
617  else
618  db = "Default";
619 
620  if (dbappend.length())
621  {
622  db += ",";
623  db += dbappend;
624  }
625  z_searchRequest->num_databaseNames = 1;
626  z_searchRequest->databaseNames = (char**)
627  odr_malloc(odr_en, sizeof(char *));
628  z_searchRequest->databaseNames[0] = odr_strdup(odr_en, db.c_str());
629  }
630  if (sr_req->facetList)
631  {
632  Z_OtherInformation **oi = &z_searchRequest->additionalSearchInfo;
633  yaz_oi_set_facetlist(oi, odr_en, sr_req->facetList);
634  }
635  Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
636  z_searchRequest->query = z_query;
637 
638  int sru_diagnostic = z3950_build_query(odr_en, z_query, sr_req);
639  if (sru_diagnostic)
640  {
641  yaz_add_srw_diagnostic(odr_en,
642  &(sru_pdu_res->u.response->diagnostics),
643  &(sru_pdu_res->u.response->num_diagnostics),
644  sru_diagnostic,
645  "query");
646  return false;
647  }
648 
649  z3950_package.request() = apdu;
650 
651  z3950_package.move();
652 
653  Z_GDU *z3950_gdu = z3950_package.response().get();
654 
655  if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950
656  || z3950_gdu->u.z3950->which != Z_APDU_searchResponse
657  || !z3950_gdu->u.z3950->u.searchResponse
658  || !z3950_gdu->u.z3950->u.searchResponse->searchStatus)
659  {
660  yaz_add_srw_diagnostic(odr_en,
661  &(sru_pdu_res->u.response->diagnostics),
662  &(sru_pdu_res->u.response->num_diagnostics),
663  YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, 0);
664  return false;
665  }
666 
667  Z_SearchResponse *sr = z3950_gdu->u.z3950->u.searchResponse;
668 
669  if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response,
670  sr->records))
671  {
672  return false;
673  }
674  Z_FacetList *fl = yaz_oi_get_facetlist(&sr->additionalSearchInfo);
675  if (!fl)
676  fl = yaz_oi_get_facetlist(&sr->otherInfo);
677  sru_pdu_res->u.response->facetList = fl;
678  sru_pdu_res->u.response->numberOfRecords
679  = odr_intdup(odr_en, *sr->resultCount);
680  return true;
681 }
682 
683 bool yf::SRUtoZ3950::Impl::z3950_present_request(
684  mp::Package &package,
685  mp::odr &odr_en,
686  Z_SRW_PDU *sru_pdu_res,
687  const Z_SRW_searchRetrieveRequest *sr_req)
688  const
689 {
690  assert(sru_pdu_res->u.response);
691  int start = 1;
692  int max_recs = 0;
693 
694  if (!sr_req)
695  return false;
696 
697  if (sr_req->maximumRecords)
698  max_recs = *sr_req->maximumRecords;
699  if (sr_req->startRecord)
700  start = *sr_req->startRecord;
701 
702  // no need to work if nobody wants record ..
703  if (max_recs == 0)
704  return true;
705 
706  bool send_z3950_present = true;
707 
708  // recordXPath unsupported.
709  if (sr_req->recordXPath)
710  {
711  send_z3950_present = false;
712  yaz_add_srw_diagnostic(odr_en,
713  &(sru_pdu_res->u.response->diagnostics),
714  &(sru_pdu_res->u.response->num_diagnostics),
715  YAZ_SRW_XPATH_RETRIEVAL_UNSUPP, 0);
716  }
717 
718  // resultSetTTL unsupported.
719  // resultSetIdleTime in response
720  if (sr_req->resultSetTTL)
721  {
722  send_z3950_present = false;
723  yaz_add_srw_diagnostic(odr_en,
724  &(sru_pdu_res->u.response->diagnostics),
725  &(sru_pdu_res->u.response->num_diagnostics),
726  YAZ_SRW_RESULT_SETS_UNSUPP, 0);
727  }
728 
729  // sort unsupported
730  if (sr_req->sort_type != Z_SRW_sort_type_none)
731  {
732  send_z3950_present = false;
733  yaz_add_srw_diagnostic(odr_en,
734  &(sru_pdu_res->u.response->diagnostics),
735  &(sru_pdu_res->u.response->num_diagnostics),
736  YAZ_SRW_SORT_UNSUPP, 0);
737  }
738 
739  // start record requested negative, or larger than number of records
740  if (start < 0 || start > *sru_pdu_res->u.response->numberOfRecords)
741  {
742  send_z3950_present = false;
743  yaz_add_srw_diagnostic(odr_en,
744  &(sru_pdu_res->u.response->diagnostics),
745  &(sru_pdu_res->u.response->num_diagnostics),
746  YAZ_SRW_FIRST_RECORD_POSITION_OUT_OF_RANGE, 0);
747  }
748 
749  // maximumRecords requested negative
750  if (max_recs < 0)
751  {
752  send_z3950_present = false;
753  yaz_add_srw_diagnostic(odr_en,
754  &(sru_pdu_res->u.response->diagnostics),
755  &(sru_pdu_res->u.response->num_diagnostics),
756  YAZ_SRW_UNSUPP_PARAMETER_VALUE,
757  "maximumRecords");
758  }
759 
760  // exit on all these above diagnostics
761  if (!send_z3950_present)
762  return false;
763 
764  if (conf_max_recs > 0 && max_recs > conf_max_recs)
765  max_recs = conf_max_recs;
766 
767  if (max_recs > *sru_pdu_res->u.response->numberOfRecords - start)
768  max_recs = *sru_pdu_res->u.response->numberOfRecords - start + 1;
769 
770  Z_SRW_searchRetrieveResponse *sru_res = sru_pdu_res->u.response;
771  sru_res->records = (Z_SRW_record *)
772  odr_malloc(odr_en, max_recs * sizeof(Z_SRW_record));
773  int num = 0;
774  while (num < max_recs)
775  {
776  // now packaging the z3950 present request
777  Package z3950_package(package.session(), package.origin());
778  z3950_package.copy_filter(package);
779  Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
780 
781  assert(apdu->u.presentRequest);
782 
783  *apdu->u.presentRequest->resultSetStartPoint = start + num;
784  *apdu->u.presentRequest->numberOfRecordsRequested = max_recs - num;
785 
786  // set response packing to be same as "request" packing..
787  int record_packing = Z_SRW_recordPacking_XML;
788  if (sr_req->recordPacking && 's' == *(sr_req->recordPacking))
789  record_packing = Z_SRW_recordPacking_string;
790 
791  // RecordSyntax will always be XML
792  apdu->u.presentRequest->preferredRecordSyntax
793  = odr_oiddup(odr_en, yaz_oid_recsyn_xml);
794 
795  // z3950'fy record schema
796  if (sr_req->recordSchema)
797  {
798  apdu->u.presentRequest->recordComposition
799  = (Z_RecordComposition *)
800  odr_malloc(odr_en, sizeof(Z_RecordComposition));
801  apdu->u.presentRequest->recordComposition->which
802  = Z_RecordComp_simple;
803  apdu->u.presentRequest->recordComposition->u.simple
805  (const char *)
806  sr_req->recordSchema);
807  }
808 
809  // attaching Z3950 package to filter chain
810  z3950_package.request() = apdu;
811 
812  // sending Z30.50 present request
813  z3950_package.move();
814 
815  //check successful Z3950 present response
816  Z_GDU *z3950_gdu = z3950_package.response().get();
817  if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950
818  || z3950_gdu->u.z3950->which != Z_APDU_presentResponse
819  || !z3950_gdu->u.z3950->u.presentResponse)
820 
821  {
822  yaz_add_srw_diagnostic(odr_en,
823  &(sru_pdu_res->u.response->diagnostics),
824  &(sru_pdu_res->u.response->num_diagnostics),
825  YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, 0);
826  return false;
827  }
828  // everything fine, continuing
829 
830  Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
831 
832  // checking non surrogate diagnostics in Z3950 present response package
833  if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response,
834  pr->records))
835  return false;
836 
837  // if anything but database or surrogate diagnostics, stop
838  if (!pr->records || pr->records->which != Z_Records_DBOSD)
839  break;
840  else
841  {
842  // inserting all records
843  int returned_recs =
844  pr->records->u.databaseOrSurDiagnostics->num_records;
845  for (int i = 0; i < returned_recs; i++)
846  {
847  int position = i + *apdu->u.presentRequest->resultSetStartPoint;
848  Z_NamePlusRecord *npr
849  = pr->records->u.databaseOrSurDiagnostics->records[i];
850 
851  sru_res->records[i + num].recordPacking = record_packing;
852 
853  if (npr->which == Z_NamePlusRecord_surrogateDiagnostic)
854  {
855  Z_DiagRec *p = npr->u.surrogateDiagnostic;
856  if (p->which == Z_DiagRec_defaultFormat)
857  {
858  Z_DefaultDiagFormat *df = p->u.defaultFormat;
859  int c = yaz_diag_bib1_to_srw(*df->condition);
860 
861  yaz_mk_sru_surrogate(
862  odr_en, sru_res->records + i + num, position,
863  c, df->u.v2Addinfo);
864  }
865  else
866  {
867  yaz_mk_sru_surrogate(
868  odr_en, sru_res->records + i + num, position,
869  YAZ_SRW_RECORD_TEMPORARILY_UNAVAILABLE, 0);
870  }
871  }
872  else if (npr->which == Z_NamePlusRecord_databaseRecord &&
873  npr->u.databaseRecord->direct_reference
874  && !oid_oidcmp(npr->u.databaseRecord->direct_reference,
875  yaz_oid_recsyn_xml))
876  {
877  // got XML record back
878  Z_External *r = npr->u.databaseRecord;
879  sru_res->records[i + num].recordPosition =
880  odr_intdup(odr_en, position);
881  sru_res->records[i + num].recordSchema = sr_req->recordSchema;
882  sru_res->records[i + num].recordData_buf
883  = odr_strdupn(odr_en,
884  (const char *)r->u.octet_aligned->buf,
885  r->u.octet_aligned->len);
886  sru_res->records[i + num].recordData_len
887  = r->u.octet_aligned->len;
888  }
889  else
890  {
891  // not XML or no database record at all
892  yaz_mk_sru_surrogate(
893  odr_en, sru_res->records + i + num, position,
894  YAZ_SRW_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA, 0);
895  }
896  }
897  num += returned_recs;
898  }
899  }
900  sru_res->num_records = num;
901  if (start - 1 + num < *sru_pdu_res->u.response->numberOfRecords)
902  sru_res->nextRecordPosition =
903  odr_intdup(odr_en, start + num);
904  return true;
905 }
906 
907 int yf::SRUtoZ3950::Impl::z3950_build_query(
908  mp::odr &odr_en, Z_Query *z_query,
909  const Z_SRW_searchRetrieveRequest *req
910  ) const
911 {
912  if (!strcmp(req->queryType, "cql"))
913  {
914  Z_External *ext = (Z_External *)
915  odr_malloc(odr_en, sizeof(*ext));
916  ext->direct_reference =
917  odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
918  ext->indirect_reference = 0;
919  ext->descriptor = 0;
920  ext->which = Z_External_CQL;
921  ext->u.cql = odr_strdup(odr_en, req->query);
922 
923  z_query->which = Z_Query_type_104;
924  z_query->u.type_104 = ext;
925  return 0;
926  }
927 
928  if (!strcmp(req->queryType, "pqf"))
929  {
930  Z_RPNQuery *RPNquery;
931  YAZ_PQF_Parser pqf_parser;
932 
933  pqf_parser = yaz_pqf_create ();
934 
935  RPNquery = yaz_pqf_parse (pqf_parser, odr_en, req->query);
936  yaz_pqf_destroy(pqf_parser);
937 
938  if (!RPNquery)
939  return YAZ_SRW_QUERY_SYNTAX_ERROR;
940 
941  z_query->which = Z_Query_type_1;
942  z_query->u.type_1 = RPNquery;
943 
944  return 0;
945  }
946 
947  if (!strcmp(req->queryType, "ccl"))
948  {
949  z_query->which = Z_Query_type_2;
950  z_query->u.type_2 = odr_create_Odr_oct(odr_en, req->query,
951  strlen(req->query));
952  return 0;
953  }
954 
955  return YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED;
956 }
957 
958 bool yf::SRUtoZ3950::Impl::z3950_to_srw_diagnostics_ok(
959  mp::odr &odr_en,
960  Z_SRW_searchRetrieveResponse
961  *sru_res,
962  Z_Records *records) const
963 {
964  // checking non surrogate diagnostics in Z3950 present response package
965  if (records
966  && records->which == Z_Records_NSD
967  && records->u.nonSurrogateDiagnostic)
968  {
969  z3950_to_srw_diag(odr_en, sru_res,
970  records->u.nonSurrogateDiagnostic);
971  return false;
972  }
973  return true;
974 }
975 
976 int yf::SRUtoZ3950::Impl::z3950_to_srw_diag(
977  mp::odr &odr_en,
978  Z_SRW_searchRetrieveResponse *sru_res,
979  Z_DefaultDiagFormat *ddf) const
980 {
981  int bib1_code = *ddf->condition;
982  sru_res->num_diagnostics = 1;
983  sru_res->diagnostics = (Z_SRW_diagnostic *)
984  odr_malloc(odr_en, sizeof(*sru_res->diagnostics));
985  yaz_mk_std_diagnostic(odr_en, sru_res->diagnostics,
986  yaz_diag_bib1_to_srw(bib1_code),
987  ddf->u.v2Addinfo);
988  return 0;
989 }
990 
991 static mp::filter::Base* filter_creator()
992 {
993  return new mp::filter::SRUtoZ3950;
994 }
995 
996 extern "C" {
997  struct metaproxy_1_filter_struct metaproxy_1_filter_sru_z3950 = {
998  0,
999  "sru_z3950",
1001  };
1002 }
1003 
1004 
1005 /*
1006  * Local variables:
1007  * c-basic-offset: 4
1008  * c-file-style: "Stroustrup"
1009  * indent-tabs-mode: nil
1010  * End:
1011  * vim: shiftwidth=4 tabstop=8 expandtab
1012  */
1013 
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
FrontendPtr get_frontend(mp::Package &package)
void process(metaproxy_1::Package &package)
std::map< mp::Session, FrontendPtr > m_clients
bool z3950_to_srw_diagnostics_ok(mp::odr &odr_en, Z_SRW_searchRetrieveResponse *srw_res, Z_Records *records) const
void sru(metaproxy_1::Package &package, Z_GDU *zgdu_req)
int z3950_to_srw_diag(mp::odr &odr_en, Z_SRW_searchRetrieveResponse *srw_res, Z_DefaultDiagFormat *ddf) const
bool z3950_close_request(mp::Package &package) const
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
void release_frontend(mp::Package &package)
int z3950_build_query(mp::odr &odr_en, Z_Query *z_query, const Z_SRW_searchRetrieveRequest *req) const
std::map< std::string, int > ActiveUrlMap
void configure(const xmlNode *xmlnode)
bool z3950_present_request(mp::Package &package, mp::odr &odr_en, Z_SRW_PDU *sru_pdu_res, Z_SRW_searchRetrieveRequest const *sr_req) const
std::map< std::string, const xmlNode * > m_database_explain
boost::shared_ptr< Frontend > FrontendPtr
struct metaproxy_1_filter_struct metaproxy_1_filter_sru_z3950
static mp::filter::Base * filter_creator()
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)
SRUServerInfo get_sru_server_info(metaproxy_1::Package &package)
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)
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)
Z_ElementSetNames * build_esn_from_schema(metaproxy_1::odr &odr_en, const char *schema)
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