Added makefile for MS VS. Compiles OK for Visual Studio 2003 / 7.1.
[metaproxy-moved-to-github.git] / src / filter_virt_db.cpp
1 /* $Id: filter_virt_db.cpp,v 1.13 2005-11-03 14:45:16 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 "util.hpp"
16 #include "filter_virt_db.hpp"
17
18 #include <yaz/zgdu.h>
19 #include <yaz/log.h>
20 #include <yaz/otherinfo.h>
21 #include <yaz/diagbib1.h>
22
23 #include <list>
24 #include <map>
25 #include <iostream>
26
27 namespace yf = yp2::filter;
28
29 namespace yp2 {
30     namespace filter {
31         struct Virt_db_set {
32             Virt_db_set(yp2::Session &id, std::string setname,
33                         std::string vhost, bool named_result_sets);
34             Virt_db_set();
35             ~Virt_db_set();
36
37             yp2::Session m_backend_session;
38             std::string m_backend_setname;
39             std::string m_vhost;
40             bool m_named_result_sets;
41         };
42         struct Virt_db_session {
43             Virt_db_session(yp2::Session &id, bool use_vhost);
44             Virt_db_session();
45             yp2::Session m_session;
46             bool m_use_vhost;
47             std::map<std::string,Virt_db_set> m_sets;
48         };
49         struct Virt_db_map {
50             Virt_db_map(std::string vhost);
51             Virt_db_map();
52             std::string m_vhost;
53         };
54         class Virt_db::Rep {
55             friend class Virt_db;
56             
57             void release_session(Package &package);
58             void init(Package &package, Z_APDU *apdu, bool &move_later);
59             void search(Package &package, Z_APDU *apdu, bool &move_later);
60             void present(Package &package, Z_APDU *apdu, bool &move_later);
61         private:
62             boost::mutex m_sessions_mutex;
63             std::map<yp2::Session,Virt_db_session>m_sessions;
64             std::map<std::string, Virt_db_map>m_maps;
65
66             typedef std::map<yp2::Session,Virt_db_session>::iterator Ses_it;
67             typedef std::map<std::string,Virt_db_set>::iterator Sets_it;
68         };
69     }
70 }
71
72 using namespace yp2;
73
74 yf::Virt_db_set::Virt_db_set(yp2::Session &id, std::string setname,
75                              std::string vhost, bool named_result_sets)
76     :   m_backend_session(id), m_backend_setname(setname), m_vhost(vhost),
77         m_named_result_sets(named_result_sets)
78 {
79 }
80
81
82 yf::Virt_db_set::Virt_db_set()
83 {
84 }
85
86
87 yf::Virt_db_set::~Virt_db_set()
88 {
89 }
90
91 yf::Virt_db_map::Virt_db_map(std::string vhost)
92     : m_vhost(vhost) 
93 {
94 }
95
96 yf::Virt_db_map::Virt_db_map()
97 {
98 }
99
100 yf::Virt_db_session::Virt_db_session()
101     : m_use_vhost(false)
102 {
103
104 }
105
106 yf::Virt_db_session::Virt_db_session(yp2::Session &id,
107                                      bool use_vhost) :
108     m_session(id) , m_use_vhost(use_vhost)
109 {
110
111 }
112
113 yf::Virt_db::Virt_db() : m_p(new Virt_db::Rep)
114 {
115 }
116
117 yf::Virt_db::~Virt_db() {
118 }
119
120 void yf::Virt_db::Rep::release_session(Package &package)
121 {
122     boost::mutex::scoped_lock lock(m_sessions_mutex);
123     
124     m_sessions.erase(package.session());
125 }
126
127 void yf::Virt_db::Rep::present(Package &package, Z_APDU *apdu, bool &move_later){
128     Session *id = 0;
129     Z_PresentRequest *req = apdu->u.presentRequest;
130     std::string resultSetId = req->resultSetId;
131     yp2::odr odr;
132     {
133         boost::mutex::scoped_lock lock(m_sessions_mutex);
134         
135         Ses_it it = m_sessions.find(package.session());
136         if (it == m_sessions.end())
137         {
138             package.response() = odr.create_close(
139                 Z_Close_protocolError,
140                 "no session for present request");
141             package.session().close();
142             return;
143         }
144         if (it->second.m_use_vhost)
145         {
146             move_later = true;
147             return;
148         }
149         Sets_it sets_it = it->second.m_sets.find(resultSetId);
150         if (sets_it == it->second.m_sets.end())
151         {
152             Z_APDU *apdu = zget_APDU(odr, Z_APDU_presentResponse);
153             
154             Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
155             apdu->u.presentResponse->records = rec;
156             rec->which = Z_Records_NSD;
157             rec->u.nonSurrogateDiagnostic =
158                 zget_DefaultDiagFormat(
159                     odr,
160                     YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
161                     resultSetId.c_str());
162             package.response() = apdu;
163
164             return;
165         }
166         id = new yp2::Session(sets_it->second.m_backend_session);
167     }
168     
169     // sending present to backend
170     Package present_package(*id, package.origin());
171     present_package.copy_filter(package);
172     
173     req->resultSetId = odr_strdup(odr, "default");
174     present_package.request() = yazpp_1::GDU(apdu);
175
176     present_package.move();
177
178     if (present_package.session().is_closed())
179     {
180         Z_APDU *apdu = zget_APDU(odr, Z_APDU_presentResponse);
181         
182         Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
183         apdu->u.presentResponse->records = rec;
184         rec->which = Z_Records_NSD;
185         rec->u.nonSurrogateDiagnostic =
186             zget_DefaultDiagFormat(
187                 odr,
188                 YAZ_BIB1_RESULT_SET_NO_LONGER_EXISTS_UNILATERALLY_DELETED_BY_,
189                 resultSetId.c_str());
190         package.response() = apdu;
191         
192         boost::mutex::scoped_lock lock(m_sessions_mutex);
193         Ses_it it = m_sessions.find(package.session());
194         if (it != m_sessions.end())
195             it->second.m_sets.erase(resultSetId);
196     }
197     else
198     {
199         package.response() = present_package.response();
200     }
201     delete id;
202 }
203
204 void yf::Virt_db::Rep::search(Package &package, Z_APDU *apdu, bool &move_later)
205 {
206     Z_SearchRequest *req = apdu->u.searchRequest;
207     std::string vhost;
208     std::string database;
209     std::string resultSetId = req->resultSetName;
210     bool support_named_result_sets = false;  // whether backend supports it
211     yp2::odr odr;
212     {
213         boost::mutex::scoped_lock lock(m_sessions_mutex);
214
215         Ses_it it = m_sessions.find(package.session());
216         if (it == m_sessions.end())
217         {
218             package.response() = odr.create_close(
219                 Z_Close_protocolError,
220                 "no session for search request");
221             package.session().close();
222
223             return;
224         }
225         if (it->second.m_use_vhost)
226         {
227             move_later = true;
228             return;
229         }
230         if (req->num_databaseNames != 1)
231         {   // exactly one database must be specified
232             Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
233             
234             Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
235             apdu->u.searchResponse->records = rec;
236             rec->which = Z_Records_NSD;
237             rec->u.nonSurrogateDiagnostic =
238                 zget_DefaultDiagFormat(
239                     odr, YAZ_BIB1_TOO_MANY_DATABASES_SPECIFIED, 0);
240             package.response() = apdu;
241             
242             return;
243         }
244         database = req->databaseNames[0];
245         std::map<std::string, Virt_db_map>::iterator map_it;
246         map_it = m_maps.find(database);
247         if (map_it == m_maps.end()) 
248         {   // no map for database: return diagnostic
249             Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
250             
251             Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
252             apdu->u.searchResponse->records = rec;
253             rec->which = Z_Records_NSD;
254             rec->u.nonSurrogateDiagnostic =
255                 zget_DefaultDiagFormat(
256                     odr, YAZ_BIB1_DATABASE_DOES_NOT_EXIST, database.c_str());
257             package.response() = apdu;
258             
259             return;
260         }
261         if (*req->replaceIndicator == 0)
262         {
263             Sets_it sets_it = it->second.m_sets.find(req->resultSetName);
264             if (sets_it != it->second.m_sets.end())
265             {
266                 Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
267                 
268                 Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
269                 apdu->u.searchResponse->records = rec;
270                 rec->which = Z_Records_NSD;
271                 rec->u.nonSurrogateDiagnostic =
272                     zget_DefaultDiagFormat(
273                         odr,
274                         YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF,
275                         0);
276                 package.response() = apdu;
277                 
278                 return;
279             }
280         }
281         it->second.m_sets.erase(req->resultSetName);
282         vhost = map_it->second.m_vhost;
283     }
284     // we might look for an existing session with same vhost
285     Session id;
286     const char *vhost_cstr = vhost.c_str();
287     if (true)
288     {  // sending init to backend
289         Package init_package(id, package.origin());
290         init_package.copy_filter(package);
291         
292         Z_APDU *init_apdu = zget_APDU(odr, Z_APDU_initRequest);
293         
294         yaz_oi_set_string_oidval(&init_apdu->u.initRequest->otherInfo, odr,
295                                  VAL_PROXY, 1, vhost_cstr);
296         
297         init_package.request() = init_apdu;
298
299         init_package.move();  // sending init 
300
301         if (init_package.session().is_closed())
302         {
303             Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
304             
305             Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
306             apdu->u.searchResponse->records = rec;
307             rec->which = Z_Records_NSD;
308             rec->u.nonSurrogateDiagnostic =
309                 zget_DefaultDiagFormat(
310                     odr, YAZ_BIB1_DATABASE_UNAVAILABLE, database.c_str());
311             package.response() = apdu;
312         }
313         Z_GDU *gdu = init_package.response().get();
314         // we hope to get an init response
315         if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
316             Z_APDU_initResponse)
317         {
318             if (ODR_MASK_GET(gdu->u.z3950->u.initResponse->options,
319                              Z_Options_namedResultSets))
320                 support_named_result_sets = true;
321         }
322         else
323         {
324             Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
325             
326             Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
327             apdu->u.searchResponse->records = rec;
328             rec->which = Z_Records_NSD;
329             rec->u.nonSurrogateDiagnostic =
330                 zget_DefaultDiagFormat(
331                     odr, YAZ_BIB1_DATABASE_UNAVAILABLE, database.c_str());
332             package.response() = apdu;
333             
334             return;
335         }
336     }
337     // sending search to backend
338     Package search_package(id, package.origin());
339
340     search_package.copy_filter(package);
341     const char *sep = strchr(vhost_cstr, '/');
342     if (sep)
343         req->databaseNames[0] = odr_strdup(odr, sep+1);
344
345     *req->replaceIndicator = 1;
346
347     std::string backend_resultSetId = "default";
348     req->resultSetName = odr_strdup(odr, backend_resultSetId.c_str());
349     search_package.request() = yazpp_1::GDU(apdu);
350     
351     search_package.move();
352
353     if (search_package.session().is_closed())
354     {
355         Z_APDU *apdu = zget_APDU(odr, Z_APDU_searchResponse);
356         
357         Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
358         apdu->u.searchResponse->records = rec;
359         rec->which = Z_Records_NSD;
360         rec->u.nonSurrogateDiagnostic =
361             zget_DefaultDiagFormat(
362                 odr, YAZ_BIB1_DATABASE_UNAVAILABLE, database.c_str());
363         package.response() = apdu;
364         
365         return;
366     }
367     package.response() = search_package.response();
368     
369     boost::mutex::scoped_lock lock(m_sessions_mutex);
370     Ses_it it = m_sessions.find(package.session());
371     if (it != m_sessions.end())
372         it->second.m_sets[resultSetId] =
373             Virt_db_set(id, backend_resultSetId, vhost,
374                         support_named_result_sets);
375 }
376
377 void yf::Virt_db::Rep::init(Package &package, Z_APDU *apdu, bool &move_later)
378 {
379     release_session(package);
380     boost::mutex::scoped_lock lock(m_sessions_mutex);
381
382     Z_InitRequest *req = apdu->u.initRequest;
383     
384     const char *vhost =
385         yaz_oi_get_string_oidval(&req->otherInfo, VAL_PROXY, 1, 0);
386     if (!vhost)
387     {
388         yp2::odr odr;
389         Z_APDU *apdu = zget_APDU(odr, Z_APDU_initResponse);
390         Z_InitResponse *resp = apdu->u.initResponse;
391         
392         int i;
393         static const int masks[] = {
394             Z_Options_search, Z_Options_present, Z_Options_namedResultSets, -1 
395         };
396         for (i = 0; masks[i] != -1; i++)
397             if (ODR_MASK_GET(req->options, masks[i]))
398                 ODR_MASK_SET(resp->options, masks[i]);
399         
400         static const int versions[] = {
401             Z_ProtocolVersion_1,
402             Z_ProtocolVersion_2,
403             Z_ProtocolVersion_3,
404             -1
405         };
406         for (i = 0; versions[i] != -1; i++)
407             if (ODR_MASK_GET(req->protocolVersion, versions[i]))
408                 ODR_MASK_SET(resp->protocolVersion, versions[i]);
409             else
410                 break;
411
412         package.response() = apdu;
413         
414         m_sessions[package.session()] = Virt_db_session(package.session(), false);
415     }
416     else
417     {
418         m_sessions[package.session()] = Virt_db_session(package.session(), true);
419         move_later = true;
420     }
421 }
422
423 void yf::Virt_db::add_map_db2vhost(std::string db, std::string vhost)
424 {
425     m_p->m_maps[db] = Virt_db_map(vhost);
426 }
427
428 void yf::Virt_db::process(Package &package) const
429 {
430     Z_GDU *gdu = package.request().get();
431
432     if (!gdu || gdu->which != Z_GDU_Z3950)
433         package.move();
434     else
435     {
436         bool move_later = false;
437         Z_APDU *apdu = gdu->u.z3950;
438         if (apdu->which == Z_APDU_initRequest)
439         {
440             m_p->init(package, apdu, move_later);
441         }
442         else if (apdu->which == Z_APDU_searchRequest)
443         {
444             m_p->search(package, apdu, move_later);
445         }
446         else if (apdu->which == Z_APDU_presentRequest)
447         {
448             m_p->present(package, apdu, move_later);
449         }
450         else
451         {
452             yp2::odr odr;
453             
454             package.response() = odr.create_close(
455                 Z_Close_protocolError,
456                 "unsupported APDU in filter_virt_db");
457                                                  
458             package.session().close();
459         }
460         if (move_later)
461             package.move();
462     }
463     if (package.session().is_closed())
464         m_p->release_session(package);
465 }
466
467
468 /*
469  * Local variables:
470  * c-basic-offset: 4
471  * indent-tabs-mode: nil
472  * c-file-style: "stroustrup"
473  * End:
474  * vim: shiftwidth=4 tabstop=8 expandtab
475  */