metaproxy  1.13.0
filter_virt_db.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 #include "config.hpp"
20 
21 #include "filter_virt_db.hpp"
22 #include <metaproxy/package.hpp>
23 
24 #include <boost/thread/mutex.hpp>
25 #include <boost/thread/condition.hpp>
26 #include <boost/shared_ptr.hpp>
27 
28 #include <metaproxy/util.hpp>
29 
30 #include <yaz/zgdu.h>
31 #include <yaz/otherinfo.h>
32 #include <yaz/diagbib1.h>
33 #include <yaz/match_glob.h>
34 #include <yaz/log.h>
35 #include <yaz/oid_db.h>
36 
37 #include <map>
38 #include <iostream>
39 
40 namespace mp = metaproxy_1;
41 namespace yf = mp::filter;
42 
43 namespace metaproxy_1 {
44  namespace filter {
45 
46  struct VirtualDB::Set {
47  Set(BackendPtr b, std::string setname);
48  Set();
49  ~Set();
50 
52  std::string m_setname;
53  };
54  struct VirtualDB::Map {
55  Map(std::string database, std::list<std::string> targets, std::string route);
56  Map(std::string database, std::string target, std::string route);
57  Map();
58  bool match(const std::string db) const;
59  std::string m_dbpattern;
60  std::list<std::string> m_targets;
61  std::string m_route;
62  };
64  mp::Session m_backend_session;
65  std::list<std::string> m_frontend_databases;
66  std::list<std::string> m_targets;
67  std::string m_route;
70  };
72  Frontend(Rep *rep);
73  ~Frontend();
74  mp::Session m_session;
76  bool m_in_use;
77  yazpp_1::GDU m_init_gdu;
78  std::list<BackendPtr> m_backend_list;
79  std::map<std::string,VirtualDB::Set> m_sets;
80 
81  void search(Package &package, Z_APDU *apdu);
82  void present(Package &package, Z_APDU *apdu);
83  void scan(Package &package, Z_APDU *apdu);
84  int relay_apdu(Package &package, Z_APDU *apdu);
85 
86  void close(Package &package);
87  typedef std::map<std::string,VirtualDB::Set>::iterator Sets_it;
88 
89  void fixup_package(Package &p, BackendPtr b);
90  void fixup_npr_record(ODR odr, Z_NamePlusRecord *npr,
91  BackendPtr b);
92  void fixup_npr_records(ODR odr, Z_Records *records,
93  BackendPtr b);
94 
95  BackendPtr lookup_backend_from_databases(
96  std::list<std::string> databases);
97  BackendPtr create_backend_from_databases(
98  std::list<std::string> databases,
99  int &error_code,
100  std::string &failing_database);
101 
102  BackendPtr init_backend(std::list<std::string> database,
103  Package &package,
104  int &error_code, std::string &addinfo);
106  };
108  friend class VirtualDB;
109  friend struct Frontend;
110 
111  FrontendPtr get_frontend(Package &package);
112  void release_frontend(Package &package);
113  void refresh_torus();
114  private:
115  std::list<VirtualDB::Map>m_maps;
116  typedef std::map<std::string,VirtualDB::Set>::iterator Sets_it;
117  boost::mutex m_mutex;
118  boost::condition m_cond_session_ready;
119  std::map<mp::Session, FrontendPtr> m_clients;
121  };
122  }
123 }
124 
125 yf::VirtualDB::BackendPtr yf::VirtualDB::Frontend::lookup_backend_from_databases(
126  std::list<std::string> databases)
127 {
128  std::list<BackendPtr>::const_iterator map_it;
129  map_it = m_backend_list.begin();
130  for (; map_it != m_backend_list.end(); map_it++)
131  if ((*map_it)->m_frontend_databases == databases)
132  return *map_it;
133  BackendPtr null;
134  return null;
135 }
136 
137 yf::VirtualDB::BackendPtr yf::VirtualDB::Frontend::create_backend_from_databases(
138  std::list<std::string> databases, int &error_code, std::string &addinfo)
139 {
140  BackendPtr b(new Backend);
141  std::list<std::string>::const_iterator db_it = databases.begin();
142 
143  b->m_number_of_sets = 0;
144  b->m_frontend_databases = databases;
145  b->m_named_result_sets = false;
146 
147  bool first_route = true;
148 
149  std::map<std::string,bool> targets_dedup;
150  for (; db_it != databases.end(); db_it++)
151  {
152  std::list<VirtualDB::Map>::const_iterator map_it;
153  map_it = m_p->m_maps.begin();
154  while (map_it != m_p->m_maps.end())
155  {
156  if (map_it->match(*db_it))
157  break;
158  map_it++;
159  }
160 
161  if (map_it == m_p->m_maps.end()) // database not found
162  {
163  error_code = YAZ_BIB1_DATABASE_DOES_NOT_EXIST;
164  addinfo = *db_it;
165  BackendPtr ptr;
166  return ptr;
167  }
168  std::list<std::string>::const_iterator t_it =
169  map_it->m_targets.begin();
170  for (; t_it != map_it->m_targets.end(); t_it++) {
171  if (!targets_dedup[*t_it])
172  {
173  targets_dedup[*t_it] = true;
174  b->m_targets.push_back(*t_it);
175  }
176  }
177 
178  // see if we have a route conflict.
179  if (!first_route && b->m_route != map_it->m_route)
180  {
181  // we have a conflict.. routing must be same for all
182  error_code = YAZ_BIB1_COMBI_OF_SPECIFIED_DATABASES_UNSUPP;
183  BackendPtr ptr;
184  return ptr;
185  }
186  b->m_route = map_it->m_route;
187  first_route = false;
188  }
189  return b;
190 }
191 
192 yf::VirtualDB::BackendPtr yf::VirtualDB::Frontend::init_backend(
193  std::list<std::string> databases, mp::Package &package,
194  int &error_code, std::string &addinfo)
195 {
196  BackendPtr b = create_backend_from_databases(databases, error_code,
197  addinfo);
198  if (!b)
199  return b;
200  Package init_package(b->m_backend_session, package.origin());
201  init_package.copy_filter(package);
202 
203  mp::odr odr;
204 
205  Z_APDU *init_apdu = zget_APDU(odr, Z_APDU_initRequest);
206 
207  mp::util::set_vhost_otherinfo(&init_apdu->u.initRequest->otherInfo, odr,
208  b->m_targets);
209  Z_InitRequest *req = init_apdu->u.initRequest;
210 
211  // copy stuff from Frontend Init Request
212  Z_GDU *org_gdu = m_init_gdu.get();
213  Z_InitRequest *org_init = org_gdu->u.z3950->u.initRequest;
214 
215 
216  const char *peer_name = yaz_oi_get_string_oid(
217  &org_init->otherInfo, yaz_oid_userinfo_client_ip, 1, 0);
218  if (peer_name)
219  yaz_oi_set_string_oid(&init_apdu->u.initRequest->otherInfo, odr,
220  yaz_oid_userinfo_client_ip, 1, peer_name);
221 
222  req->idAuthentication = org_init->idAuthentication;
223  req->implementationId = org_init->implementationId;
224  req->implementationName = org_init->implementationName;
225  req->implementationVersion = org_init->implementationVersion;
226  *req->preferredMessageSize = *org_init->preferredMessageSize;
227  *req->maximumRecordSize = *org_init->maximumRecordSize;
228 
229  ODR_MASK_SET(req->options, Z_Options_search);
230  ODR_MASK_SET(req->options, Z_Options_present);
231  ODR_MASK_SET(req->options, Z_Options_namedResultSets);
232  ODR_MASK_SET(req->options, Z_Options_scan);
233 
234  ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
235  ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
236  ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3);
237 
238  init_package.request() = init_apdu;
239 
240  init_package.move(b->m_route); // sending init
241 
242  Z_GDU *gdu = init_package.response().get();
243  // we hope to get an init response
244  error_code = 0;
245  if (gdu && gdu->which == Z_GDU_Z3950
246  && gdu->u.z3950->which == Z_APDU_initResponse)
247  {
248  Z_InitResponse *res = gdu->u.z3950->u.initResponse;
249  if (ODR_MASK_GET(res->options, Z_Options_namedResultSets))
250  {
251  b->m_named_result_sets = true;
252  }
253  if (*res->result && !init_package.session().is_closed())
254  {
255  m_backend_list.push_back(b);
256  return b;
257  }
258  mp::util::get_init_diagnostics(res, error_code, addinfo);
259  }
260  if (error_code == 0)
261  {
262  std::list<std::string>::const_iterator db_it = databases.begin();
263  error_code = YAZ_BIB1_ACCESS_TO_SPECIFIED_DATABASE_DENIED;
264  if (db_it != databases.end())
265  addinfo = *db_it;
266  }
267  if (!init_package.session().is_closed())
268  {
269  Package close_package(b->m_backend_session, package.origin());
270  close_package.copy_filter(package);
271  close_package.session().close();
272  close_package.move(b->m_route); // closing it
273  }
274  BackendPtr null;
275  return null;
276 }
277 
278 void yf::VirtualDB::Frontend::search(mp::Package &package, Z_APDU *apdu_req)
279 {
280  yazpp_1::GDU ngdu(apdu_req);
281  Z_SearchRequest *req = ngdu.get()->u.z3950->u.searchRequest;
282  std::string vhost;
283  std::string resultSetId = req->resultSetName;
284  mp::odr odr;
285 
286  std::list<std::string> databases;
287  int i;
288  for (i = 0; i<req->num_databaseNames; i++)
289  databases.push_back(req->databaseNames[i]);
290 
291  Sets_it sets_it = m_sets.find(req->resultSetName);
292  if (sets_it != m_sets.end())
293  {
294  // result set already exist
295  // if replace indicator is off: we return diagnostic if
296  // result set already exist.
297  if (*req->replaceIndicator == 0)
298  {
299  Z_APDU *apdu =
300  odr.create_searchResponse(
301  apdu_req,
302  YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF,
303  0);
304  package.response() = apdu;
305 
306  return;
307  }
308  sets_it->second.m_backend->m_number_of_sets--;
309  }
310  // pick up any existing database with named result sets ..
311  // or one which has no result sets.. yet.
312  BackendPtr b; // null for now
313  std::list<BackendPtr>::const_iterator map_it;
314  map_it = m_backend_list.begin();
315  for (; map_it != m_backend_list.end(); map_it++)
316  {
317  BackendPtr tmp = *map_it;
318  if (tmp->m_frontend_databases == databases &&
319  (tmp->m_named_result_sets ||
320  tmp->m_number_of_sets == 0))
321  {
322  b = *map_it;
323  break;
324  }
325  }
326  if (!b) // no backend yet. Must create a new one
327  {
328  int error_code;
329  std::string addinfo;
330  b = init_backend(databases, package, error_code, addinfo);
331  if (!b)
332  {
333  // did not get a backend (unavailable somehow?)
334 
335  Z_APDU *apdu =
336  odr.create_searchResponse(
337  apdu_req, error_code, addinfo.c_str());
338  package.response() = apdu;
339  return;
340  }
341  }
342  m_sets.erase(req->resultSetName);
343  // sending search to backend
344  Package search_package(b->m_backend_session, package.origin());
345 
346  search_package.copy_filter(package);
347 
348  std::string backend_setname;
349  if (b->m_named_result_sets)
350  {
351  backend_setname = std::string(req->resultSetName);
352  }
353  else
354  {
355  backend_setname = "default";
356  req->resultSetName = odr_strdup(odr, backend_setname.c_str());
357  }
358 
359  // pick first targets spec and move the databases from it ..
360  std::list<std::string>::const_iterator t_it = b->m_targets.begin();
361  if (t_it != b->m_targets.end())
362  {
363  mp::util::set_databases_from_zurl(odr, *t_it,
364  &req->num_databaseNames,
365  &req->databaseNames);
366  }
367 
368  *req->replaceIndicator = 1;
369 
370  search_package.request() = ngdu;
371 
372  search_package.move(b->m_route);
373 
374  if (search_package.session().is_closed())
375  {
376  package.response() = search_package.response();
377  package.session().close();
378  return;
379  }
380 
381  Z_GDU *gdu = search_package.response().get();
382  if (gdu && gdu->which == Z_GDU_Z3950
383  && gdu->u.z3950->which == Z_APDU_searchResponse)
384  {
385  Z_SearchResponse *b_resp = gdu->u.z3950->u.searchResponse;
386  Z_Records *z_records = b_resp->records;
387  if (!z_records || (z_records && z_records->which == Z_Records_DBOSD))
388  {
389  b->m_number_of_sets++;
390  m_sets[resultSetId] = VirtualDB::Set(b, backend_setname);
391  fixup_package(search_package, b);
392  }
393  }
394  package.response() = search_package.response();
395 }
396 
397 yf::VirtualDB::Frontend::Frontend(Rep *rep)
398 {
399  m_p = rep;
400  m_is_virtual = false;
401 }
402 
403 void yf::VirtualDB::Frontend::close(mp::Package &package)
404 {
405  std::list<BackendPtr>::const_iterator b_it;
406 
407  for (b_it = m_backend_list.begin(); b_it != m_backend_list.end(); b_it++)
408  {
409  (*b_it)->m_backend_session.close();
410  Package close_package((*b_it)->m_backend_session, package.origin());
411  close_package.copy_filter(package);
412  close_package.move((*b_it)->m_route);
413  }
414  m_backend_list.clear();
415 }
416 
417 yf::VirtualDB::Frontend::~Frontend()
418 {
419 }
420 
421 yf::VirtualDB::FrontendPtr yf::VirtualDB::Rep::get_frontend(mp::Package &package)
422 {
423  boost::mutex::scoped_lock lock(m_mutex);
424 
425  std::map<mp::Session,yf::VirtualDB::FrontendPtr>::iterator it;
426 
427  while(true)
428  {
429  it = m_clients.find(package.session());
430  if (it == m_clients.end())
431  break;
432 
433  if (!it->second->m_in_use)
434  {
435  it->second->m_in_use = true;
436  return it->second;
437  }
438  m_cond_session_ready.wait(lock);
439  }
440  FrontendPtr f(new Frontend(this));
441  m_clients[package.session()] = f;
442  f->m_in_use = true;
443  return f;
444 }
445 
446 void yf::VirtualDB::Rep::release_frontend(mp::Package &package)
447 {
448  boost::mutex::scoped_lock lock(m_mutex);
449  std::map<mp::Session,yf::VirtualDB::FrontendPtr>::iterator it;
450 
451  it = m_clients.find(package.session());
452  if (it != m_clients.end())
453  {
454  if (package.session().is_closed())
455  {
456  it->second->close(package);
457  m_clients.erase(it);
458  }
459  else
460  {
461  it->second->m_in_use = false;
462  }
463  m_cond_session_ready.notify_all();
464  }
465 }
466 
467 
468 yf::VirtualDB::Set::Set(BackendPtr b, std::string setname)
469  : m_backend(b), m_setname(setname)
470 {
471 }
472 
473 
474 yf::VirtualDB::Set::Set()
475 {
476 }
477 
478 
479 yf::VirtualDB::Set::~Set()
480 {
481 }
482 
483 yf::VirtualDB::Map::Map(std::string database,
484  std::list<std::string> targets, std::string route)
485  : m_dbpattern(database), m_targets(targets), m_route(route)
486 {
487 }
488 
489 yf::VirtualDB::Map::Map(std::string database,
490  std::string target, std::string route)
491  : m_dbpattern(database), m_route(route)
492 {
493  m_targets.push_back(target);
494 }
495 
496 
497 yf::VirtualDB::Map::Map()
498 {
499 }
500 
501 bool yf::VirtualDB::Map::match(const std::string db) const
502 {
503  std::string norm_db = mp::util::database_name_normalize(db);
504  if (yaz_match_glob(m_dbpattern.c_str(), norm_db.c_str()))
505  return true;
506  return false;
507 }
508 
509 yf::VirtualDB::VirtualDB() : m_p(new VirtualDB::Rep)
510 {
511  m_p->pass_vhosts = false;
512 }
513 
514 yf::VirtualDB::~VirtualDB() {
515 }
516 
517 void yf::VirtualDB::Frontend::fixup_npr_record(ODR odr, Z_NamePlusRecord *npr,
518  BackendPtr b)
519 {
520  if (npr->databaseName)
521  {
522  std::string b_database = std::string(npr->databaseName);
523 
524  // consider each of the frontend databases..
525  std::list<std::string>::const_iterator db_it;
526  for (db_it = b->m_frontend_databases.begin();
527  db_it != b->m_frontend_databases.end(); db_it++)
528  {
529  // see which target it corresponds to.. (if any)
530  std::list<VirtualDB::Map>::const_iterator map_it =
531  m_p->m_maps.begin();
532  while (map_it != m_p->m_maps.end())
533  {
534  if (map_it->match(*db_it))
535  break;
536  map_it++;
537  }
538  if (map_it != m_p->m_maps.end())
539  {
540  std::list<std::string>::const_iterator t
541  = map_it->m_targets.begin();
542  while (t != map_it->m_targets.end())
543  {
544  if (*t == b_database)
545  {
546  npr->databaseName = odr_strdup(odr, (*db_it).c_str());
547  return;
548  }
549  t++;
550  }
551  }
552 
553  }
554  db_it = b->m_frontend_databases.begin();
555  if (db_it != b->m_frontend_databases.end())
556  {
557  std::string database = *db_it;
558  npr->databaseName = odr_strdup(odr, database.c_str());
559  }
560  }
561 }
562 
563 void yf::VirtualDB::Frontend::fixup_npr_records(ODR odr, Z_Records *records,
564  BackendPtr b)
565 {
566  if (records && records->which == Z_Records_DBOSD)
567  {
568  Z_NamePlusRecordList *nprlist = records->u.databaseOrSurDiagnostics;
569  int i;
570  for (i = 0; i < nprlist->num_records; i++)
571  {
572  fixup_npr_record(odr, nprlist->records[i], b);
573  }
574  }
575 }
576 
577 void yf::VirtualDB::Frontend::fixup_package(mp::Package &p, BackendPtr b)
578 {
579  Z_GDU *gdu = p.response().get();
580  mp::odr odr;
581 
582  if (gdu && gdu->which == Z_GDU_Z3950)
583  {
584  Z_APDU *apdu = gdu->u.z3950;
585  if (apdu->which == Z_APDU_presentResponse)
586  {
587  fixup_npr_records(odr, apdu->u.presentResponse->records, b);
588  p.response() = gdu;
589  }
590  else if (apdu->which == Z_APDU_searchResponse)
591  {
592  fixup_npr_records(odr, apdu->u.searchResponse->records, b);
593  p.response() = gdu;
594  }
595  }
596 }
597 
598 void yf::VirtualDB::Frontend::present(mp::Package &package, Z_APDU *apdu_req)
599 {
600  yazpp_1::GDU ngdu(apdu_req);
601  Z_PresentRequest *req = ngdu.get()->u.z3950->u.presentRequest;
602  std::string resultSetId = req->resultSetId;
603  mp::odr odr;
604 
605  Sets_it sets_it = m_sets.find(resultSetId);
606  if (sets_it == m_sets.end())
607  {
608  Z_APDU *apdu =
609  odr.create_presentResponse(
610  apdu_req,
611  YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
612  resultSetId.c_str());
613  package.response() = apdu;
614  return;
615  }
616  Session *id =
617  new mp::Session(sets_it->second.m_backend->m_backend_session);
618 
619  // sending present to backend
620  Package present_package(*id, package.origin());
621  present_package.copy_filter(package);
622 
623  req->resultSetId = odr_strdup(odr, sets_it->second.m_setname.c_str());
624 
625  present_package.request() = ngdu;
626 
627  present_package.move(sets_it->second.m_backend->m_route);
628 
629  fixup_package(present_package, sets_it->second.m_backend);
630 
631  if (present_package.session().is_closed())
632  {
633  package.response() = present_package.response();
634  package.session().close();
635  }
636  else
637  {
638  package.response() = present_package.response();
639  }
640  delete id;
641 }
642 
643 int yf::VirtualDB::Frontend::relay_apdu(mp::Package &package, Z_APDU *apdu_req)
644 {
645  int no = 0;
646  std::list<BackendPtr>::const_iterator map_it;
647  map_it = m_backend_list.begin();
648  for (; map_it != m_backend_list.end(); map_it++)
649  {
650  BackendPtr b = *map_it;
651 
652  Package relay_package(b->m_backend_session, package.origin());
653  relay_package.copy_filter(package);
654 
655  relay_package.request() = yazpp_1::GDU(apdu_req);
656 
657  relay_package.move(b->m_route);
658  package.response() = relay_package.response();
659  no++;
660  }
661  return no;
662 }
663 
664 void yf::VirtualDB::Frontend::scan(mp::Package &package, Z_APDU *apdu_req)
665 {
666  yazpp_1::GDU ngdu(apdu_req);
667  Z_ScanRequest *req = ngdu.get()->u.z3950->u.scanRequest;
668  std::string vhost;
669  mp::odr odr;
670 
671  std::list<std::string> databases;
672  int i;
673  for (i = 0; i<req->num_databaseNames; i++)
674  databases.push_back(req->databaseNames[i]);
675 
676  BackendPtr b;
677  // pick up any existing backend with a database match
678  std::list<BackendPtr>::const_iterator map_it;
679  map_it = m_backend_list.begin();
680  for (; map_it != m_backend_list.end(); map_it++)
681  {
682  BackendPtr tmp = *map_it;
683  if (tmp->m_frontend_databases == databases)
684  break;
685  }
686  if (map_it != m_backend_list.end())
687  b = *map_it;
688  if (!b) // no backend yet. Must create a new one
689  {
690  int error_code;
691  std::string addinfo;
692  b = init_backend(databases, package, error_code, addinfo);
693  if (!b)
694  {
695  // did not get a backend (unavailable somehow?)
696  Z_APDU *apdu =
697  odr.create_scanResponse(
698  apdu_req, error_code, addinfo.c_str());
699  package.response() = apdu;
700 
701  return;
702  }
703  }
704  // sending scan to backend
705  Package scan_package(b->m_backend_session, package.origin());
706 
707  scan_package.copy_filter(package);
708 
709  // pick first targets spec and move the databases from it ..
710  std::list<std::string>::const_iterator t_it = b->m_targets.begin();
711  if (t_it != b->m_targets.end())
712  {
713  mp::util::set_databases_from_zurl(odr, *t_it,
714  &req->num_databaseNames,
715  &req->databaseNames);
716  }
717 
718  scan_package.request() = ngdu;
719 
720  scan_package.move(b->m_route);
721 
722  if (scan_package.session().is_closed())
723  {
724  package.response() = scan_package.response();
725  package.session().close();
726  return;
727  }
728  package.response() = scan_package.response();
729 }
730 
731 
732 void yf::VirtualDB::add_map_db2targets(std::string db,
733  std::list<std::string> targets,
734  std::string route)
735 {
736  m_p->m_maps.push_back(
737  VirtualDB::Map(mp::util::database_name_normalize(db), targets, route));
738 }
739 
740 
741 void yf::VirtualDB::add_map_db2target(std::string db,
742  std::string target,
743  std::string route)
744 
745 {
746  m_p->m_maps.push_back(
747  VirtualDB::Map(mp::util::database_name_normalize(db), target, route));
748 }
749 
750 void yf::VirtualDB::process(mp::Package &package) const
751 {
752  FrontendPtr f = m_p->get_frontend(package);
753 
754  Z_GDU *gdu = package.request().get();
755 
756  if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
757  Z_APDU_initRequest && !f->m_is_virtual)
758  {
759  Z_InitRequest *req = gdu->u.z3950->u.initRequest;
760 
761  std::list<std::string> vhosts;
762  mp::util::get_vhost_otherinfo(req->otherInfo, vhosts);
763 
764  if (vhosts.size() > 0 && m_p->pass_vhosts)
765  {
766  package.move();
767  }
768  else
769  {
770  f->m_init_gdu = gdu;
771 
772  mp::odr odr;
773  Z_APDU *apdu = odr.create_initResponse(gdu->u.z3950, 0, 0);
774  Z_InitResponse *resp = apdu->u.initResponse;
775 
776  int i;
777  static const int masks[] = {
778  Z_Options_search,
779  Z_Options_present,
780  Z_Options_namedResultSets,
781  Z_Options_scan,
782  -1
783  };
784  for (i = 0; masks[i] != -1; i++)
785  if (ODR_MASK_GET(req->options, masks[i]))
786  ODR_MASK_SET(resp->options, masks[i]);
787 
788  static const int versions[] = {
789  Z_ProtocolVersion_1,
790  Z_ProtocolVersion_2,
791  Z_ProtocolVersion_3,
792  -1
793  };
794  for (i = 0; versions[i] != -1; i++)
795  if (ODR_MASK_GET(req->protocolVersion, versions[i]))
796  ODR_MASK_SET(resp->protocolVersion, versions[i]);
797  else
798  break;
799 
800  *resp->preferredMessageSize = *req->preferredMessageSize;
801  *resp->maximumRecordSize = *req->maximumRecordSize;
802 
803  package.response() = apdu;
804  f->m_is_virtual = true;
805  }
806  }
807  else if (!f->m_is_virtual)
808  package.move();
809  else if (gdu && gdu->which == Z_GDU_Z3950)
810  {
811  Z_APDU *apdu = gdu->u.z3950;
812  if (apdu->which == Z_APDU_initRequest)
813  {
814  mp::odr odr;
815 
816  package.response() = odr.create_close(
817  apdu,
818  Z_Close_protocolError,
819  "double init");
820 
821  package.session().close();
822  }
823  else if (apdu->which == Z_APDU_searchRequest)
824  {
825  f->search(package, apdu);
826  }
827  else if (apdu->which == Z_APDU_presentRequest)
828  {
829  f->present(package, apdu);
830  }
831  else if (apdu->which == Z_APDU_scanRequest)
832  {
833  f->scan(package, apdu);
834  }
835  else if (apdu->which == Z_APDU_close)
836  {
837  if (f->relay_apdu(package, apdu) == 0)
838  {
839  mp::odr odr;
840 
841  package.response() = odr.create_close(
842  apdu, Z_Close_finished, "virt_db");
843 
844  package.session().close();
845  }
846  }
847  else
848  {
849  mp::odr odr;
850 
851  package.response() = odr.create_close(
852  apdu, Z_Close_protocolError,
853  "unsupported APDU in filter_virt_db");
854 
855  package.session().close();
856  }
857  }
858  m_p->release_frontend(package);
859 }
860 
861 void mp::filter::VirtualDB::configure(const xmlNode * ptr, bool test_only,
862  const char *path)
863 {
864  for (ptr = ptr->children; ptr; ptr = ptr->next)
865  {
866  if (ptr->type != XML_ELEMENT_NODE)
867  continue;
868  if (!strcmp((const char *) ptr->name, "pass-vhosts"))
869  {
870  m_p->pass_vhosts = mp::xml::get_bool(ptr, false);
871  }
872  else if (!strcmp((const char *) ptr->name, "virtual"))
873  {
874  std::string database;
875  std::list<std::string> targets;
876  xmlNode *v_node = ptr->children;
877  for (; v_node; v_node = v_node->next)
878  {
879  if (v_node->type != XML_ELEMENT_NODE)
880  continue;
881 
882  if (mp::xml::is_element_mp(v_node, "database"))
883  database = mp::xml::get_text(v_node);
884  else if (mp::xml::is_element_mp(v_node, "target"))
885  targets.push_back(mp::xml::get_text(v_node));
886  else
887  throw mp::filter::FilterException
888  ("Bad element "
889  + std::string((const char *) v_node->name)
890  + " in virtual section"
891  );
892  }
893  std::string route = mp::xml::get_route(ptr);
894 
895  VirtualDB::Map vmap(mp::util::database_name_normalize(database),
896  targets, route);
897  m_p->m_maps.push_back(vmap);
898  }
899  else
900  {
901  throw mp::filter::FilterException
902  ("Bad element "
903  + std::string((const char *) ptr->name)
904  + " in virt_db filter");
905  }
906  }
907 }
908 
909 static mp::filter::Base* filter_creator()
910 {
911  return new mp::filter::VirtualDB;
912 }
913 
914 extern "C" {
915  struct metaproxy_1_filter_struct metaproxy_1_filter_virt_db = {
916  0,
917  "virt_db",
919  };
920 }
921 
922 
923 /*
924  * Local variables:
925  * c-basic-offset: 4
926  * c-file-style: "Stroustrup"
927  * indent-tabs-mode: nil
928  * End:
929  * vim: shiftwidth=4 tabstop=8 expandtab
930  */
931 
std::map< mp::Session, FrontendPtr > m_clients
std::list< std::string > m_frontend_databases
std::list< VirtualDB::Map > m_maps
boost::shared_ptr< Frontend > FrontendPtr
std::list< std::string > m_targets
static mp::filter::Base * filter_creator()
boost::scoped_ptr< Rep > m_p
std::map< std::string, VirtualDB::Set > m_sets
boost::shared_ptr< Backend > BackendPtr
struct metaproxy_1_filter_struct metaproxy_1_filter_virt_db
std::map< std::string, VirtualDB::Set >::iterator Sets_it
std::map< std::string, VirtualDB::Set >::iterator Sets_it