More work on Virt_db filter
[metaproxy-moved-to-github.git] / src / filter_virt_db.cpp
1 /* $Id: filter_virt_db.cpp,v 1.2 2005-10-24 21:01:53 adam Exp $
2    Copyright (c) 2005, Index Data.
3
4 %LICENSE%
5  */
6
7 #include "config.hpp"
8
9 #include "filter.hpp"
10 #include "router.hpp"
11 #include "package.hpp"
12
13 #include <boost/thread/mutex.hpp>
14
15 #include "filter_virt_db.hpp"
16
17 #include <yaz/zgdu.h>
18 #include <yaz/log.h>
19 #include <yaz/otherinfo.h>
20 #include <yaz/diagbib1.h>
21
22 #include <list>
23 #include <map>
24 #include <iostream>
25
26 namespace yf = yp2::filter;
27
28 namespace yp2 {
29     namespace filter {
30         struct Virt_db_set {
31             Virt_db_set(yp2::Session &id, Z_InternationalString *setname,
32                         std::string vhost);
33             ~Virt_db_set();
34
35             yp2::Session m_session;
36             std::string m_setname;
37             std::string m_vhost;
38         };
39         struct Virt_db_session {
40             Virt_db_session(yp2::Session &id, bool use_vhost);
41             yp2::Session m_session;
42             bool m_use_vhost;
43             std::list<Virt_db_set> m_sets;
44         };
45         struct Virt_db_map {
46             Virt_db_map(std::string vhost);
47             Virt_db_map();
48             std::string m_vhost;
49         };
50         class Virt_db::Rep {
51             friend class Virt_db;
52             
53             void release_session(Package &package);
54             void init(Package &package, Z_APDU *apdu, bool &move_later);
55             void search(Package &package, Z_APDU *apdu, bool &move_later);
56         private:
57             boost::mutex m_sessions_mutex;
58             std::list<Virt_db_session>m_sessions;
59             std::map<std::string, Virt_db_map>m_maps;
60         };
61     }
62 }
63
64 yf::Virt_db_set::Virt_db_set(yp2::Session &id, Z_InternationalString *setname,
65                              std::string vhost)
66     :   m_session(id), m_setname(setname), m_vhost(vhost)
67 {
68 }
69
70 yf::Virt_db_set::~Virt_db_set()
71 {
72 }
73
74 yf::Virt_db_map::Virt_db_map(std::string vhost)
75     : m_vhost(vhost) 
76 {
77 }
78
79 yf::Virt_db_map::Virt_db_map()
80 {
81 }
82
83 yf::Virt_db_session::Virt_db_session(yp2::Session &id,
84                                      bool use_vhost) :
85     m_session(id) , m_use_vhost(use_vhost)
86 {
87
88 }
89
90 yf::Virt_db::Virt_db() {
91     m_p = new Virt_db::Rep;
92 }
93
94 yf::Virt_db::~Virt_db() {
95     delete m_p;
96 }
97
98 void yf::Virt_db::Rep::release_session(Package &package)
99 {
100     if (package.session().is_closed()) 
101     {
102         boost::mutex::scoped_lock lock(m_sessions_mutex);
103         
104         std::list<Virt_db_session>::iterator it;
105         for (it = m_sessions.begin(); it != m_sessions.end(); it++)
106         {
107             if (package.session() == (*it).m_session)
108                 break;
109         }
110         if (it == m_sessions.end())
111             return;
112         m_sessions.erase(it);
113     }
114 }
115
116 void yf::Virt_db::Rep::search(Package &package, Z_APDU *apdu, bool &move_later)
117 {
118     Z_SearchRequest *req = apdu->u.searchRequest;
119     std::string vhost;
120     std::string database;
121     Session *id = 0;
122     {
123         boost::mutex::scoped_lock lock(m_sessions_mutex);
124         
125         std::list<Virt_db_session>::iterator it;
126         for (it = m_sessions.begin(); it != m_sessions.end(); it++)
127         {
128             if (package.session() == (*it).m_session)
129                 break;
130         }
131         if (it == m_sessions.end())
132         {
133             // error should be returned
134             move_later = true;
135             return;
136         }
137         if ((*it).m_use_vhost)
138         {
139             move_later = true;
140             return;
141         }
142         if (req->num_databaseNames != 1)
143         {   // exactly one database must be specified
144             ODR odr = odr_createmem(ODR_ENCODE);
145             Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
146             
147             Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
148             apdu->u.searchResponse->records = rec;
149             rec->which = Z_Records_NSD;
150             rec->u.nonSurrogateDiagnostic =
151                 zget_DefaultDiagFormat(
152                     odr, YAZ_BIB1_TOO_MANY_DATABASES_SPECIFIED, 0);
153             package.response() = apdu;
154             
155             odr_destroy(odr);
156             return;
157         }
158         database = req->databaseNames[0];
159         std::map<std::string, Virt_db_map>::iterator map_it;
160         map_it = m_maps.find(database);
161         if (map_it == m_maps.end()) 
162         {   // no map for database: return diagnostic
163             ODR odr = odr_createmem(ODR_ENCODE);
164             Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
165             
166             Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
167             apdu->u.searchResponse->records = rec;
168             rec->which = Z_Records_NSD;
169             rec->u.nonSurrogateDiagnostic =
170                 zget_DefaultDiagFormat(
171                     odr, YAZ_BIB1_DATABASE_UNAVAILABLE, database.c_str());
172             package.response() = apdu;
173             
174             odr_destroy(odr);
175             return;
176         }
177         vhost = map_it->second.m_vhost;
178         id = new Session;
179         (*it).m_sets.push_back(Virt_db_set(*id, req->resultSetName, vhost));
180     }
181     const char *vhost_cstr = vhost.c_str();
182     if (true)
183     {  // sending init to backend
184         Package init_package(*id, package.origin());
185         init_package.copy_filter(package);
186         
187         ODR odr = odr_createmem(ODR_ENCODE);
188         Z_APDU *init_apdu = zget_APDU(odr, Z_APDU_initRequest);
189         
190         yaz_oi_set_string_oidval(&init_apdu->u.initRequest->otherInfo, odr,
191                                  VAL_PROXY, 1, vhost_cstr);
192         
193         init_package.request() = init_apdu;
194         odr_destroy(odr);
195
196         init_package.move();  // send init 
197
198         if (init_package.session().is_closed())
199         {
200             ODR odr = odr_createmem(ODR_ENCODE);
201             Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
202             
203             Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
204             apdu->u.searchResponse->records = rec;
205             rec->which = Z_Records_NSD;
206             rec->u.nonSurrogateDiagnostic =
207                 zget_DefaultDiagFormat(
208                     odr, YAZ_BIB1_DATABASE_UNAVAILABLE, database.c_str());
209             package.response() = apdu;
210             
211             odr_destroy(odr);
212             return;
213         }
214     }
215     // sending search to backend
216     Package search_package(*id, package.origin());
217     search_package.copy_filter(package);
218     const char *sep = strchr(vhost_cstr, '/');
219     ODR odr = odr_createmem(ODR_ENCODE);
220     if (sep)
221         req->databaseNames[0] = odr_strdup(odr, sep+1);
222     
223     search_package.request() = yazpp_1::GDU(apdu);
224     
225     odr_destroy(odr);
226     
227     search_package.move();
228
229     package.response() = search_package.response();
230 }
231
232 void yf::Virt_db::Rep::init(Package &package, Z_APDU *apdu, bool &move_later)
233 {
234     boost::mutex::scoped_lock lock(m_sessions_mutex);
235     std::list<Virt_db_session>::iterator it;
236
237     for (it = m_sessions.begin(); it != m_sessions.end(); it++)
238     {
239         if (package.session() == (*it).m_session)
240             break;
241     }
242     if (it != m_sessions.end())
243         m_sessions.erase(it);
244
245     Z_InitRequest *req = apdu->u.initRequest;
246     
247     const char *vhost =
248         yaz_oi_get_string_oidval(&req->otherInfo, VAL_PROXY, 1, 0);
249     if (!vhost)
250     {
251         ODR odr = odr_createmem(ODR_ENCODE);
252         
253         Z_APDU *apdu = zget_APDU(odr, Z_APDU_initResponse);
254         Z_InitResponse *resp = apdu->u.initResponse;
255         
256         int i;
257         static const int masks[] = {
258             Z_Options_search, Z_Options_present, 0 
259         };
260         for (i = 0; masks[i]; i++)
261             if (ODR_MASK_GET(req->options, masks[i]))
262                 ODR_MASK_SET(resp->options, masks[i]);
263         
264         package.response() = apdu;
265         
266         odr_destroy(odr);
267
268         m_sessions.push_back(Virt_db_session(package.session(), false));
269     }
270     else
271     {
272         m_sessions.push_back(Virt_db_session(package.session(), true));
273         move_later = true;
274     }
275 }
276
277 void yf::Virt_db::add_map_db2vhost(std::string db, std::string vhost)
278 {
279     m_p->m_maps[db] = Virt_db_map(vhost);
280 }
281
282 void yf::Virt_db::process(Package &package) const
283 {
284     Z_GDU *gdu = package.request().get();
285
286     if (!gdu || gdu->which != Z_GDU_Z3950)
287         package.move();
288     else
289     {
290         bool move_later = false;
291         Z_APDU *apdu = gdu->u.z3950;
292         if (apdu->which == Z_APDU_initRequest)
293         {
294             m_p->init(package, apdu, move_later);
295         }
296         else if (apdu->which == Z_APDU_searchRequest)
297         {
298             m_p->search(package, apdu, move_later);
299         }
300         else
301         {
302             ODR odr = odr_createmem(ODR_ENCODE);
303             
304             Z_APDU *apdu = zget_APDU(odr, Z_APDU_close);
305             
306             *apdu->u.close->closeReason = Z_Close_protocolError;
307             
308             package.response() = apdu;
309             package.session().close();
310             odr_destroy(odr);
311         }
312         if (move_later)
313             package.move();
314     }
315     m_p->release_session(package);
316 }
317
318
319 /*
320  * Local variables:
321  * c-basic-offset: 4
322  * indent-tabs-mode: nil
323  * c-file-style: "stroustrup"
324  * End:
325  * vim: shiftwidth=4 tabstop=8 expandtab
326  */