Added Z3950 Client Filter, AKA Z39.50 backend.
[metaproxy-moved-to-github.git] / src / filter_z3950_client.cpp
1 /* $Id: filter_z3950_client.cpp,v 1.1 2005-10-16 16:05:44 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_z3950_client.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 <yaz++/socket-manager.h>
23 #include <yaz++/pdu-assoc.h>
24 #include <yaz++/z-assoc.h>
25
26 #include <iostream>
27
28 namespace yf = yp2::filter;
29
30 namespace yp2 {
31     namespace filter {
32         class Z3950Client::Assoc : public yazpp_1::Z_Assoc{
33             friend class Pimpl;
34         public:
35             Assoc(yp2::Session id, yazpp_1::SocketManager *socket_manager,
36                   yazpp_1::IPDU_Observable *PDU_Observable,
37                   std::string host);
38             ~Assoc();
39             void connectNotify();
40             void failNotify();
41             void timeoutNotify();
42             void recv_GDU(Z_GDU *gdu, int len);
43             yazpp_1::IPDU_Observer* sessionNotify(
44                 yazpp_1::IPDU_Observable *the_PDU_Observable,
45                 int fd);
46         private:
47             yp2::Session m_session_id;
48             yazpp_1::SocketManager *m_socket_manager;
49             yazpp_1::IPDU_Observable *m_PDU_Observable;
50             Package *m_package;
51             bool m_waiting;
52             bool m_connected;
53             std::string m_host;
54         };
55
56         class Z3950Client::Pimpl {
57         public:
58             boost::mutex m_mutex;
59             std::list<Z3950Client::Assoc *> m_clients;
60             Z3950Client::Assoc *get_assoc(Package &package);
61             void send_and_receive(Package &package,
62                                   yf::Z3950Client::Assoc *c);
63         };
64     }
65 }
66
67
68 yf::Z3950Client::Assoc::Assoc(yp2::Session id,
69                               yazpp_1::SocketManager *socket_manager,
70                               yazpp_1::IPDU_Observable *PDU_Observable,
71                               std::string host)
72     :  Z_Assoc(PDU_Observable), m_session_id(id),
73        m_socket_manager(socket_manager), m_PDU_Observable(PDU_Observable),
74        m_package(0), m_waiting(false), m_connected(false),
75        m_host(host)
76 {
77 }
78
79 yf::Z3950Client::Assoc::~Assoc()
80 {
81     delete m_socket_manager;
82 }
83
84 void yf::Z3950Client::Assoc::connectNotify()
85 {
86     m_waiting = false;
87
88     m_connected = true;
89 }
90
91 void yf::Z3950Client::Assoc::failNotify()
92 {
93     m_waiting = false;
94
95     ODR odr = odr_createmem(ODR_ENCODE);
96
97     Z_APDU *apdu = zget_APDU(odr, Z_APDU_close);
98
99     *apdu->u.close->closeReason = Z_Close_peerAbort;
100
101     if (m_package)
102     {
103         m_package->response() = apdu;
104         m_package->session().close();
105     }
106
107     odr_destroy(odr);
108 }
109
110 void yf::Z3950Client::Assoc::timeoutNotify()
111 {
112     m_waiting = false;
113
114     ODR odr = odr_createmem(ODR_ENCODE);
115
116     Z_APDU *apdu = zget_APDU(odr, Z_APDU_close);
117
118     *apdu->u.close->closeReason = Z_Close_lackOfActivity;
119
120     if (m_package)
121     {
122         m_package->response() = apdu;
123         m_package->session().close();
124     }
125     odr_destroy(odr);
126 }
127
128 void yf::Z3950Client::Assoc::recv_GDU(Z_GDU *gdu, int len)
129 {
130     m_waiting = false;
131
132     if (m_package)
133         m_package->response() = gdu;
134 }
135
136 yazpp_1::IPDU_Observer *yf::Z3950Client::Assoc::sessionNotify(
137     yazpp_1::IPDU_Observable *the_PDU_Observable,
138     int fd)
139 {
140     return 0;
141 }
142
143
144 yf::Z3950Client::Z3950Client() {
145     m_p = new yf::Z3950Client::Pimpl;
146 }
147
148 yf::Z3950Client::~Z3950Client() {
149     delete m_p;
150 }
151
152 yf::Z3950Client::Assoc *yf::Z3950Client::Pimpl::get_assoc(Package &package) 
153 {
154     Z_GDU *gdu = package.request().get();
155
156     // only deal with Z39.50
157     if (!gdu || gdu->which != Z_GDU_Z3950)
158     {
159         package.move();
160         return 0;
161     }
162
163     // only one thread messes with the clients list at a time
164     boost::mutex::scoped_lock lock(m_mutex);
165
166     Z_APDU *apdu = gdu->u.z3950;
167
168     std::list<yf::Z3950Client::Assoc *>::iterator it;
169
170     for (it = m_clients.begin(); it != m_clients.end(); it++)
171     {
172         if ((*it)->m_session_id == package.session())
173             break;
174     }
175     if (it != m_clients.end())
176         return *it;
177
178     // new session ..
179
180     // check that it is init. If not, close
181     if (apdu->which != Z_APDU_initRequest)
182     {
183         ODR odr = odr_createmem(ODR_ENCODE);
184         Z_APDU *apdu = zget_APDU(odr, Z_APDU_close);
185         
186         *apdu->u.close->closeReason = Z_Close_protocolError;
187         package.response() = apdu;
188         
189         package.session().close();
190         odr_destroy(odr);
191         return 0;
192     }
193     // check virtual host
194     const char *vhost =
195         yaz_oi_get_string_oidval(&apdu->u.initRequest->otherInfo,
196                                  VAL_PROXY, 1, 0);
197     if (!vhost)
198     {
199         ODR odr = odr_createmem(ODR_ENCODE);
200         Z_APDU *apdu = zget_APDU(odr, Z_APDU_initResponse);
201         
202         apdu->u.initResponse->userInformationField =
203             zget_init_diagnostics(odr, 
204                                   YAZ_BIB1_INIT_NEGOTIATION_OPTION_REQUIRED,
205                                   "Virtual host not given");
206         package.response() = apdu;
207             
208         package.session().close();
209         odr_destroy(odr);
210         return 0;
211     }
212     
213     yazpp_1::SocketManager *sm = new yazpp_1::SocketManager;
214     yazpp_1::PDU_Assoc *pdu_as = new yazpp_1::PDU_Assoc(sm);
215     yf::Z3950Client::Assoc *as = new yf::Z3950Client::Assoc(package.session(),
216                                                             sm, pdu_as,
217                                                             vhost);
218     m_clients.push_back(as);
219     return as;
220 }
221
222 void yf::Z3950Client::Pimpl::send_and_receive(Package &package,
223                                               yf::Z3950Client::Assoc *c)
224 {
225     // we should lock c!
226
227     c->m_package = &package;
228     c->m_waiting = true;
229     if (!c->m_connected)
230     {
231         c->client(c->m_host.c_str());
232
233         while (c->m_waiting && c->m_socket_manager->processEvent() > 0)
234             ;
235     }
236     if (!c->m_connected)
237     {
238         return;
239     }
240
241     // prepare response
242     c->m_waiting = true;
243     
244     // relay the package  ..
245     int len;
246     c->send_GDU(package.request().get(), &len);
247     
248     while (c->m_waiting && c->m_socket_manager->processEvent() > 0)
249         ;
250 }
251
252 void yf::Z3950Client::process(Package &package) const
253 {
254     yf::Z3950Client::Assoc *c = m_p->get_assoc(package);
255     if (!c)
256         return;
257     m_p->send_and_receive(package, c);
258 }
259
260
261 /*
262  * Local variables:
263  * c-basic-offset: 4
264  * indent-tabs-mode: nil
265  * c-file-style: "stroustrup"
266  * End:
267  * vim: shiftwidth=4 tabstop=8 expandtab
268  */