z3950 initRequest and close packages working, missing search, present,
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
1 /* $Id: filter_sru_to_z3950.cpp,v 1.5 2006-09-14 20:29:50 marc Exp $
2    Copyright (c) 2005-2006, Index Data.
3
4    See the LICENSE file for details
5  */
6
7 #include "config.hpp"
8 #include "filter.hpp"
9 #include "package.hpp"
10 #include "util.hpp"
11 #include "filter_sru_to_z3950.hpp"
12
13 #include <yaz/zgdu.h>
14 #include <yaz/srw.h>
15
16 #include <boost/thread/mutex.hpp>
17
18 #include <iostream>
19 #include <sstream>
20 #include <string>
21
22
23 namespace mp = metaproxy_1;
24 namespace yf = mp::filter;
25
26 namespace metaproxy_1 
27 {
28
29     template<typename T>
30     std::string to_string(const T& t)
31     {
32         std::ostringstream o;
33         if(o << t)
34             return o.str();
35         
36         return std::string();
37     }
38
39     std::string http_header_value(const Z_HTTP_Header* header, 
40                                   const std::string name)
41     {
42         while (header && header->name
43                && std::string(header->name) !=  name)
44             header = header->next;
45         
46         if (header && header->name && std::string(header->name) == name
47             && header->value)
48             return std::string(header->value);
49
50         return std::string();
51     }
52     
53
54 }
55
56
57 namespace metaproxy_1 {
58     namespace filter {
59         class SRUtoZ3950::Rep {
60             //friend class SRUtoZ3950;
61         public:
62             void configure(const xmlNode *xmlnode);
63             void process(metaproxy_1::Package &package) const;
64         private:
65             std::string sru_protocol(const Z_HTTP_Request &http_req) const;
66             std::string debug_http(const Z_HTTP_Request &http_req) const;
67             void http_response(mp::Package &package, 
68                                const std::string &content, 
69                                int http_code = 200) const;
70             bool build_sru_debug_package(mp::Package &package) const;
71             bool z3950_init_request(mp::Package &package, 
72                                          const std::string 
73                                          &database = "Default") const;
74             bool z3950_close_request(mp::Package &package) const;
75             bool z3950_search_request(mp::Package &package) const;
76             bool z3950_scan_request(mp::Package &package) const;
77         };
78     }
79 }
80
81 yf::SRUtoZ3950::SRUtoZ3950() : m_p(new Rep)
82 {
83 }
84
85 yf::SRUtoZ3950::~SRUtoZ3950()
86 {  // must have a destructor because of boost::scoped_ptr
87 }
88
89 void yf::SRUtoZ3950::configure(const xmlNode *xmlnode)
90 {
91     m_p->configure(xmlnode);
92 }
93
94 void yf::SRUtoZ3950::process(mp::Package &package) const
95 {
96     m_p->process(package);
97 }
98
99 void yf::SRUtoZ3950::Rep::configure(const xmlNode *xmlnode)
100 {
101 }
102
103 void yf::SRUtoZ3950::Rep::process(mp::Package &package) const
104 {
105     Z_GDU *zgdu_req = package.request().get();
106
107     // ignoring all non HTTP_Request packages
108     if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
109         package.move();
110         return;
111     }
112     
113     // only working on  HTTP_Request packages now
114
115     z3950_init_request(package);
116     //z3950_search_request(package);
117     //z3950_scan_request(package);
118     z3950_close_request(package);
119
120
121     // TODO: Z3950 response parsing and translation to SRU package
122     Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
123
124     
125     build_sru_debug_package(package);
126     return;
127
128
129
130
131
132
133
134
135     // SRU request package checking
136  
137
138     Z_GDU *zgdu_res = 0; 
139     Z_SRW_PDU *sru_pdu_req = 0;
140     Z_SRW_PDU *sru_pdu_res = 0;
141     Z_APDU *z3950_apdu_req = 0;
142     Z_APDU *z3950_apdu_res = 0;
143     
144
145     Z_SOAP *soap_req = 0;
146     char *charset = 0;
147     Z_SRW_diagnostic *diag = 0;
148     int num_diags = 0;
149     //mp::odr odr_de(ODR_DECODE);
150     
151     if (0 == yaz_sru_decode(http_req, &sru_pdu_req, &soap_req, 
152                             odr(ODR_DECODE), &charset, &diag, &num_diags))
153     {
154         std::cout << "SRU GET/POST \n";
155     }
156     else if (0 == yaz_srw_decode(http_req, &sru_pdu_req, &soap_req, 
157                                  odr(ODR_DECODE), &charset))
158     {
159         std::cout << "SRU SOAP \n";
160     } 
161     else 
162     {
163         std::cout << "SRU DECODING ERROR - SHOULD NEVER HAPPEN\n";
164         package.session().close();
165         return;
166     }
167
168     if (num_diags)
169     {
170         std::cout << "SRU DIAGNOSTICS " << num_diags << "\n";
171         // TODO: make nice diagnostic return package 
172         //Z_SRW_PDU *srw_pdu_res =
173         //            yaz_srw_get(odr(ODR_ENCODE),
174         //                        Z_SRW_searchRetrieve_response);
175         //        Z_SRW_searchRetrieveResponse *srw_res = srw_pdu_res->u.response;
176
177         //        srw_res->diagnostics = diagnostic;
178         //        srw_res->num_diagnostics = num_diagnostic;
179         //        send_srw_response(srw_pdu_res);
180         //        return;
181
182         // package.session().close();
183         return;
184     }
185
186     
187     // SRU request package translation to Z3950 package
188
189     // searchRetrieve
190     if (sru_pdu_req && sru_pdu_req->which == Z_SRW_searchRetrieve_request)
191     {
192         Z_SRW_searchRetrieveRequest *srw_req = sru_pdu_req->u.request;   
193
194         // recordXPath unsupported.
195         //if (srw_req->recordXPath)
196         //    yaz_add_srw_diagnostic(odr_decode(),
197         //                           &diag, &num_diags, 72, 0);
198         // sort unsupported
199         //    if (srw_req->sort_type != Z_SRW_sort_type_none)
200         //        yaz_add_srw_diagnostic(odr_decode(),
201         //                               &diag, &num_diags, 80, 0);
202     }
203     else
204     {
205         std::cout << "SRU OPERATION NOT SUPPORTED \n";
206         // TODO: make nice diagnostic return package 
207         // package.session().close();
208         return;
209     }
210
211 }
212
213 bool yf::SRUtoZ3950::Rep::build_sru_debug_package(mp::Package &package) const
214 {
215     Z_GDU *zgdu_req = package.request().get();
216     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
217     {    
218         Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
219         std::string content = debug_http(*http_req);
220         int http_code = 400;    
221         http_response(package, content, http_code);
222         return true;
223     }
224     return false;
225 }
226
227
228 bool 
229 yf::SRUtoZ3950::Rep::z3950_init_request(mp::Package &package, 
230                                              const std::string &database) const
231 {
232     // prepare Z3950 package 
233     Package z3950_package(package.session(), package.origin());
234     z3950_package.copy_filter(package);
235
236     // set initRequest APDU
237     mp::odr odr_en(ODR_ENCODE);
238     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
239     //TODO: add database name in apdu
240     z3950_package.request() = apdu;
241
242     // send Z3950 package
243     z3950_package.move();
244     if (z3950_package.session().is_closed()){
245         package.session().close();
246         return false;
247     }
248
249     // check successful initResponse
250     Z_GDU *gdu = package.response().get();
251     if (gdu && gdu->which == Z_GDU_Z3950 
252         && gdu->u.z3950->which == Z_APDU_initResponse)
253         return true;
254
255     return false;
256 }
257
258 bool 
259 yf::SRUtoZ3950::Rep::z3950_close_request(mp::Package &package) const
260 {
261     // prepare Z3950 package 
262     Package z3950_package(package.session(), package.origin());
263     z3950_package.copy_filter(package);
264
265     // set initRequest APDU
266     mp::odr odr_en(ODR_ENCODE);
267     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_close);
268     //TODO: add database name in apdu
269     z3950_package.request() = apdu;
270
271     // send Z3950 package
272     z3950_package.move();
273     //if (z3950_package.session().is_closed()){
274     //    package.session().close();
275     //    return false;
276     //}
277
278     // check successful initResponse
279     Z_GDU *gdu = package.response().get();
280     if (gdu && gdu->which == Z_GDU_Z3950 
281         && gdu->u.z3950->which == Z_APDU_close)
282         return true;
283
284     return false;
285 }
286
287 bool 
288 yf::SRUtoZ3950::Rep::z3950_search_request(mp::Package &package) const
289 {
290     Package z3950_package(package.session(), package.origin());
291     z3950_package.copy_filter(package); 
292     mp::odr odr_en(ODR_ENCODE);
293     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
294     //TODO: add stuff in apdu
295
296
297     z3950_package.request() = apdu;
298     z3950_package.move();
299     //TODO: check success condition
300     return true;
301     return false;
302 }
303
304 bool 
305 yf::SRUtoZ3950::Rep::z3950_scan_request(mp::Package &package) const
306 {
307     Package z3950_package(package.session(), package.origin());
308     z3950_package.copy_filter(package); 
309     mp::odr odr_en(ODR_ENCODE);
310     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
311     //TODO: add stuff in apdu
312     z3950_package.request() = apdu;
313     z3950_package.move();
314     //TODO: check success condition
315     return true;
316     return false;
317 }
318
319
320
321 std::string 
322 yf::SRUtoZ3950::Rep::sru_protocol(const Z_HTTP_Request &http_req) const
323 {
324     const std::string mime_urlencoded("application/x-www-form-urlencoded");
325     const std::string mime_text_xml("text/xml");
326     const std::string mime_soap_xml("application/soap+xml");
327
328     const std::string http_method(http_req.method);
329     const std::string http_type 
330         =  http_header_value(http_req.headers, "Content-Type");
331
332     if (http_method == "GET")
333         return "SRU GET";
334
335     if (http_method == "POST"
336               && http_type  == mime_urlencoded)
337         return "SRU POST";
338     
339     if ( http_method == "POST"
340          && (http_type  == mime_text_xml
341              || http_type  == mime_soap_xml))
342         return "SRU SOAP";
343
344     return "HTTP";
345 }
346
347 std::string 
348 yf::SRUtoZ3950::Rep::debug_http(const Z_HTTP_Request &http_req) const
349 {
350     std::string message("<html>\n<body>\n<h1>"
351                         "Metaproxy SRUtoZ3950 filter"
352                         "</h1>\n");
353     
354     message += "<h3>HTTP Info</h3><br/>\n";
355     message += "<p>\n";
356     message += "<b>Method: </b> " + std::string(http_req.method) + "<br/>\n";
357     message += "<b>Version:</b> " + std::string(http_req.version) + "<br/>\n";
358     message += "<b>Path:   </b> " + std::string(http_req.path) + "<br/>\n";
359
360     message += "<b>Content-Type:</b>"
361         + http_header_value(http_req.headers, "Content-Type")
362         + "<br/>\n";
363     message += "<b>Content-Length:</b>"
364         + http_header_value(http_req.headers, "Content-Length")
365         + "<br/>\n";
366     message += "</p>\n";    
367     
368     message += "<h3>Headers</h3><br/>\n";
369     message += "<p>\n";    
370     Z_HTTP_Header* header = http_req.headers;
371     while (header){
372         message += "<b>Header: </b> <i>" 
373             + std::string(header->name) + ":</i> "
374             + std::string(header->value) + "<br/>\n";
375         header = header->next;
376     }
377     message += "</p>\n";    
378     message += "</body>\n</html>\n";
379     return message;
380 }
381
382 void yf::SRUtoZ3950::Rep::http_response(metaproxy_1::Package &package, 
383                                         const std::string &content, 
384                                         int http_code) const
385 {
386
387     Z_GDU *zgdu_req = package.request().get(); 
388     Z_GDU *zgdu_res = 0; 
389     mp::odr odr;
390     zgdu_res 
391        = odr.create_HTTP_Response(package.session(), 
392                                   zgdu_req->u.HTTP_Request, 
393                                   http_code);
394         
395     zgdu_res->u.HTTP_Response->content_len = content.size();
396     zgdu_res->u.HTTP_Response->content_buf 
397         = (char*) odr_malloc(odr, zgdu_res->u.HTTP_Response->content_len);
398     
399     strncpy(zgdu_res->u.HTTP_Response->content_buf, 
400             content.c_str(),  zgdu_res->u.HTTP_Response->content_len);
401     
402     //z_HTTP_header_add(odr, &hres->headers,
403     //                  "Content-Type", content_type.c_str());
404     package.response() = zgdu_res;
405 }
406
407
408
409
410 static mp::filter::Base* filter_creator()
411 {
412     return new mp::filter::SRUtoZ3950;
413 }
414
415 extern "C" {
416     struct metaproxy_1_filter_struct metaproxy_1_filter_sru_to_z3950 = {
417         0,
418         "SRUtoZ3950",
419         filter_creator
420     };
421 }
422
423
424 /*
425  * Local variables:
426  * c-basic-offset: 4
427  * indent-tabs-mode: nil
428  * c-file-style: "stroustrup"
429  * End:
430  * vim: shiftwidth=4 tabstop=8 expandtab
431  */