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