added logging after vir-db filter, added correct initializing and checking of init...
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
1 /* $Id: filter_sru_to_z3950.cpp,v 1.6 2006-09-14 23:07:35 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
116     // TODO: Z3950 response parsing and translation to SRU package
117     Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
118
119     if (z3950_init_request(package))
120     {
121         z3950_search_request(package);
122         z3950_scan_request(package);
123         z3950_close_request(package);
124     }
125     
126     build_sru_debug_package(package);
127     return;
128
129
130
131
132
133
134
135
136     // SRU request package checking
137  
138
139     Z_GDU *zgdu_res = 0; 
140     Z_SRW_PDU *sru_pdu_req = 0;
141     Z_SRW_PDU *sru_pdu_res = 0;
142     Z_APDU *z3950_apdu_req = 0;
143     Z_APDU *z3950_apdu_res = 0;
144     
145
146     Z_SOAP *soap_req = 0;
147     char *charset = 0;
148     Z_SRW_diagnostic *diag = 0;
149     int num_diags = 0;
150     //mp::odr odr_de(ODR_DECODE);
151     
152     if (0 == yaz_sru_decode(http_req, &sru_pdu_req, &soap_req, 
153                             odr(ODR_DECODE), &charset, &diag, &num_diags))
154     {
155         std::cout << "SRU GET/POST \n";
156     }
157     else if (0 == yaz_srw_decode(http_req, &sru_pdu_req, &soap_req, 
158                                  odr(ODR_DECODE), &charset))
159     {
160         std::cout << "SRU SOAP \n";
161     } 
162     else 
163     {
164         std::cout << "SRU DECODING ERROR - SHOULD NEVER HAPPEN\n";
165         package.session().close();
166         return;
167     }
168
169     if (num_diags)
170     {
171         std::cout << "SRU DIAGNOSTICS " << num_diags << "\n";
172         // TODO: make nice diagnostic return package 
173         //Z_SRW_PDU *srw_pdu_res =
174         //            yaz_srw_get(odr(ODR_ENCODE),
175         //                        Z_SRW_searchRetrieve_response);
176         //        Z_SRW_searchRetrieveResponse *srw_res = srw_pdu_res->u.response;
177
178         //        srw_res->diagnostics = diagnostic;
179         //        srw_res->num_diagnostics = num_diagnostic;
180         //        send_srw_response(srw_pdu_res);
181         //        return;
182
183         // package.session().close();
184         return;
185     }
186
187     
188     // SRU request package translation to Z3950 package
189
190     // searchRetrieve
191     if (sru_pdu_req && sru_pdu_req->which == Z_SRW_searchRetrieve_request)
192     {
193         Z_SRW_searchRetrieveRequest *srw_req = sru_pdu_req->u.request;   
194
195         // recordXPath unsupported.
196         //if (srw_req->recordXPath)
197         //    yaz_add_srw_diagnostic(odr_decode(),
198         //                           &diag, &num_diags, 72, 0);
199         // sort unsupported
200         //    if (srw_req->sort_type != Z_SRW_sort_type_none)
201         //        yaz_add_srw_diagnostic(odr_decode(),
202         //                               &diag, &num_diags, 80, 0);
203     }
204     else
205     {
206         std::cout << "SRU OPERATION NOT SUPPORTED \n";
207         // TODO: make nice diagnostic return package 
208         // package.session().close();
209         return;
210     }
211
212 }
213
214 bool yf::SRUtoZ3950::Rep::build_sru_debug_package(mp::Package &package) const
215 {
216     Z_GDU *zgdu_req = package.request().get();
217     if  (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
218     {    
219         Z_HTTP_Request* http_req =  zgdu_req->u.HTTP_Request;
220         std::string content = debug_http(*http_req);
221         int http_code = 400;    
222         http_response(package, content, http_code);
223         return true;
224     }
225     return false;
226 }
227
228
229 bool 
230 yf::SRUtoZ3950::Rep::z3950_init_request(mp::Package &package, 
231                                              const std::string &database) const
232 {
233     // prepare Z3950 package
234     //Session s;
235     //Package z3950_package(s, package.origin());
236     Package z3950_package(package.session(), package.origin());
237     z3950_package.copy_filter(package);
238
239     // set initRequest APDU
240     mp::odr odr_en(ODR_ENCODE);
241     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
242     Z_InitRequest *init_req = apdu->u.initRequest;
243     //TODO: add user name in apdu
244     //TODO: add user passwd in apdu
245     //init_req->idAuthentication = org_init->idAuthentication;
246     //init_req->implementationId = "IDxyz";
247     //init_req->implementationName = "NAMExyz";
248     //init_req->implementationVersion = "VERSIONxyz";
249
250     ODR_MASK_SET(init_req->options, Z_Options_search);
251     ODR_MASK_SET(init_req->options, Z_Options_present);
252     ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
253     ODR_MASK_SET(init_req->options, Z_Options_scan);
254
255     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
256     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
257     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
258
259     z3950_package.request() = apdu;
260
261     // send Z3950 package
262     z3950_package.move();
263
264     // dead Z3950 backend detection
265     if (z3950_package.session().is_closed()){
266         package.session().close();
267         return false;
268     }
269
270     // check successful initResponse
271     Z_GDU *z3950_gdu = z3950_package.response().get();
272
273     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
274         && z3950_gdu->u.z3950->which == Z_APDU_initResponse)
275          return true;
276  
277     return false;
278 }
279
280 bool 
281 yf::SRUtoZ3950::Rep::z3950_close_request(mp::Package &package) const
282 {
283     // prepare Z3950 package 
284     Package z3950_package(package.session(), package.origin());
285     z3950_package.copy_filter(package);
286
287     // set initRequest APDU
288     mp::odr odr_en(ODR_ENCODE);
289     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_close);
290     //TODO: add database name in apdu
291     z3950_package.request() = apdu;
292
293     // send Z3950 package
294     z3950_package.move();
295
296     // dead Z3950 backend detection
297     if (z3950_package.session().is_closed()){
298         //package.session().close();
299         return true;
300     }
301
302     // check successful close response
303     Z_GDU *z3950_gdu = z3950_package.response().get();
304     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950 
305         && z3950_gdu->u.z3950->which == Z_APDU_close)
306         return true;
307
308     return false;
309 }
310
311 bool 
312 yf::SRUtoZ3950::Rep::z3950_search_request(mp::Package &package) const
313 {
314     Package z3950_package(package.session(), package.origin());
315     z3950_package.copy_filter(package); 
316     mp::odr odr_en(ODR_ENCODE);
317     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
318
319     //TODO: add stuff in apdu
320     Z_SearchRequest *z_searchRequest = apdu->u.searchRequest;
321     z_searchRequest->num_databaseNames = 1;
322     //z_searchRequest->databaseNames = (char**)
323     //    odr_malloc(m_s2z_odr_search, sizeof(char *));
324     //z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
325
326     // TODO query transformation
327     Z_Query *query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
328     z_searchRequest->query = query;
329
330 //             if (srw_req->query_type == Z_SRW_query_type_cql)
331 //             {
332 //                 Z_External *ext = (Z_External *) 
333 //                     odr_malloc(m_s2z_odr_search, sizeof(*ext));
334 //                 ext->direct_reference = 
335 //                     odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
336 //                 ext->indirect_reference = 0;
337 //                 ext->descriptor = 0;
338 //                 ext->which = Z_External_CQL;
339 //                 ext->u.cql = srw_req->query.cql;
340                 
341 //                 query->which = Z_Query_type_104;
342 //                 query->u.type_104 =  ext;
343 //             }
344 //             else if (srw_req->query_type == Z_SRW_query_type_pqf)
345 //                Z_RPNQuery *RPNquery;
346 //                 YAZ_PQF_Parser pqf_parser;
347                 
348 //                 pqf_parser = yaz_pqf_create ();
349                 
350 //                 RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
351 //                                           srw_req->query.pqf);
352 //                 if (!RPNquery)
353 //                 {
354 //                     const char *pqf_msg;
355 //                     size_t off;
356 //                     int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
357 //                     int ioff = off;
358 //                     yaz_log(YLOG_LOG, "%*s^\n", ioff+4, "");
359 //                     yaz_log(YLOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
360                     
361 //                     send_to_srw_client_error(10, 0);
362 //                     return;
363 //                 }
364 //                 query->which = Z_Query_type_1;
365 //                 query->u.type_1 =  RPNquery;
366                 
367 //                 yaz_pqf_destroy (pqf_parser);
368 //             }
369 //             else
370 //             {
371 //                 send_to_srw_client_error(7, "query");
372 //                 return;
373 //             }
374
375     z3950_package.request() = apdu;
376     z3950_package.move();
377     //TODO: check success condition
378     return true;
379     return false;
380 }
381
382 bool 
383 yf::SRUtoZ3950::Rep::z3950_scan_request(mp::Package &package) const
384 {
385     Package z3950_package(package.session(), package.origin());
386     z3950_package.copy_filter(package); 
387     mp::odr odr_en(ODR_ENCODE);
388     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
389     //TODO: add stuff in apdu
390     z3950_package.request() = apdu;
391     z3950_package.move();
392     //TODO: check success condition
393     return true;
394     return false;
395 }
396
397
398
399 std::string 
400 yf::SRUtoZ3950::Rep::sru_protocol(const Z_HTTP_Request &http_req) const
401 {
402     const std::string mime_urlencoded("application/x-www-form-urlencoded");
403     const std::string mime_text_xml("text/xml");
404     const std::string mime_soap_xml("application/soap+xml");
405
406     const std::string http_method(http_req.method);
407     const std::string http_type 
408         =  http_header_value(http_req.headers, "Content-Type");
409
410     if (http_method == "GET")
411         return "SRU GET";
412
413     if (http_method == "POST"
414               && http_type  == mime_urlencoded)
415         return "SRU POST";
416     
417     if ( http_method == "POST"
418          && (http_type  == mime_text_xml
419              || http_type  == mime_soap_xml))
420         return "SRU SOAP";
421
422     return "HTTP";
423 }
424
425 std::string 
426 yf::SRUtoZ3950::Rep::debug_http(const Z_HTTP_Request &http_req) const
427 {
428     std::string message("<html>\n<body>\n<h1>"
429                         "Metaproxy SRUtoZ3950 filter"
430                         "</h1>\n");
431     
432     message += "<h3>HTTP Info</h3><br/>\n";
433     message += "<p>\n";
434     message += "<b>Method: </b> " + std::string(http_req.method) + "<br/>\n";
435     message += "<b>Version:</b> " + std::string(http_req.version) + "<br/>\n";
436     message += "<b>Path:   </b> " + std::string(http_req.path) + "<br/>\n";
437
438     message += "<b>Content-Type:</b>"
439         + http_header_value(http_req.headers, "Content-Type")
440         + "<br/>\n";
441     message += "<b>Content-Length:</b>"
442         + http_header_value(http_req.headers, "Content-Length")
443         + "<br/>\n";
444     message += "</p>\n";    
445     
446     message += "<h3>Headers</h3><br/>\n";
447     message += "<p>\n";    
448     Z_HTTP_Header* header = http_req.headers;
449     while (header){
450         message += "<b>Header: </b> <i>" 
451             + std::string(header->name) + ":</i> "
452             + std::string(header->value) + "<br/>\n";
453         header = header->next;
454     }
455     message += "</p>\n";    
456     message += "</body>\n</html>\n";
457     return message;
458 }
459
460 void yf::SRUtoZ3950::Rep::http_response(metaproxy_1::Package &package, 
461                                         const std::string &content, 
462                                         int http_code) const
463 {
464
465     Z_GDU *zgdu_req = package.request().get(); 
466     Z_GDU *zgdu_res = 0; 
467     mp::odr odr;
468     zgdu_res 
469        = odr.create_HTTP_Response(package.session(), 
470                                   zgdu_req->u.HTTP_Request, 
471                                   http_code);
472         
473     zgdu_res->u.HTTP_Response->content_len = content.size();
474     zgdu_res->u.HTTP_Response->content_buf 
475         = (char*) odr_malloc(odr, zgdu_res->u.HTTP_Response->content_len);
476     
477     strncpy(zgdu_res->u.HTTP_Response->content_buf, 
478             content.c_str(),  zgdu_res->u.HTTP_Response->content_len);
479     
480     //z_HTTP_header_add(odr, &hres->headers,
481     //                  "Content-Type", content_type.c_str());
482     package.response() = zgdu_res;
483 }
484
485
486
487
488 static mp::filter::Base* filter_creator()
489 {
490     return new mp::filter::SRUtoZ3950;
491 }
492
493 extern "C" {
494     struct metaproxy_1_filter_struct metaproxy_1_filter_sru_to_z3950 = {
495         0,
496         "SRUtoZ3950",
497         filter_creator
498     };
499 }
500
501
502 /*
503  * Local variables:
504  * c-basic-offset: 4
505  * indent-tabs-mode: nil
506  * c-file-style: "stroustrup"
507  * End:
508  * vim: shiftwidth=4 tabstop=8 expandtab
509  */