64a72bd1a557bd9ba9facf091304e0c918d60c59
[metaproxy-moved-to-github.git] / src / filter_record_transform.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2010 Index Data
3
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include "config.hpp"
20 #include "filter_record_transform.hpp"
21 #include <metaproxy/package.hpp>
22 #include <metaproxy/util.hpp>
23 #include "gduutil.hpp"
24
25 #include <yaz/diagbib1.h>
26 #include <yaz/zgdu.h>
27 #include <yaz/retrieval.h>
28
29 #include <iostream>
30
31 namespace mp = metaproxy_1;
32 namespace yf = mp::filter;
33 namespace mp_util = metaproxy_1::util;
34
35 namespace metaproxy_1 {
36     namespace filter {
37         class RecordTransform::Impl {
38         public:
39             Impl();
40             ~Impl();
41             void process(metaproxy_1::Package & package) const;
42             void configure(const xmlNode * xml_node);
43         private:
44             yaz_retrieval_t m_retrieval;
45         };
46     }
47 }
48
49 // define Pimpl wrapper forwarding to Impl
50  
51 yf::RecordTransform::RecordTransform() : m_p(new Impl)
52 {
53 }
54
55 yf::RecordTransform::~RecordTransform()
56 {  // must have a destructor because of boost::scoped_ptr
57 }
58
59 void yf::RecordTransform::configure(const xmlNode *xmlnode, bool test_only)
60 {
61     m_p->configure(xmlnode);
62 }
63
64 void yf::RecordTransform::process(mp::Package &package) const
65 {
66     m_p->process(package);
67 }
68
69
70 // define Implementation stuff
71
72
73
74 yf::RecordTransform::Impl::Impl() 
75 {
76     m_retrieval = yaz_retrieval_create();
77     assert(m_retrieval);
78 }
79
80 yf::RecordTransform::Impl::~Impl()
81
82     if (m_retrieval)
83         yaz_retrieval_destroy(m_retrieval);
84 }
85
86 void yf::RecordTransform::Impl::configure(const xmlNode *xml_node)
87 {
88     //const char *srcdir = getenv("srcdir");
89     //if (srcdir)
90     //    yaz_retrieval_set_path(m_retrieval, srcdir);
91
92     if (!xml_node)
93         throw mp::XMLError("RecordTransform filter config: empty XML DOM");
94
95     // parsing down to retrieval node, which can be any of the children nodes
96     xmlNode *retrieval_node;
97     for (retrieval_node = xml_node->children; 
98          retrieval_node; 
99          retrieval_node = retrieval_node->next)
100     {
101         if (retrieval_node->type != XML_ELEMENT_NODE)
102             continue;
103         if (0 == strcmp((const char *) retrieval_node->name, "retrievalinfo"))
104             break;
105     }
106
107     // read configuration
108     if ( 0 != yaz_retrieval_configure(m_retrieval, retrieval_node)){
109         std::string msg("RecordTransform filter config: ");
110         msg += yaz_retrieval_get_error(m_retrieval);
111         throw mp::XMLError(msg);
112     }
113 }
114
115 void yf::RecordTransform::Impl::process(mp::Package &package) const
116 {
117
118     Z_GDU *gdu_req = package.request().get();
119     
120     // only working on z3950 present packages
121     if (!gdu_req 
122         || !(gdu_req->which == Z_GDU_Z3950) 
123         || !(gdu_req->u.z3950->which == Z_APDU_presentRequest))
124     {
125         package.move();
126         return;
127     }
128     
129     // getting original present request
130     Z_PresentRequest *pr_req = gdu_req->u.z3950->u.presentRequest;
131
132     // setting up ODR's for memory during encoding/decoding
133     //mp::odr odr_de(ODR_DECODE);  
134     mp::odr odr_en(ODR_ENCODE);
135
136     // setting up variables for conversion state
137     yaz_record_conv_t rc = 0;
138     int ret_code;
139
140     const char *input_schema = 0;
141     Odr_oid *input_syntax = 0;
142
143     if(pr_req->recordComposition){
144         input_schema 
145             = mp_util::record_composition_to_esn(pr_req->recordComposition);
146     }
147     if(pr_req->preferredRecordSyntax){
148         input_syntax = pr_req->preferredRecordSyntax;
149     }
150     
151     const char *match_schema = 0;
152     Odr_oid *match_syntax = 0;
153
154     const char *backend_schema = 0;
155     Odr_oid *backend_syntax = 0;
156
157     ret_code 
158         = yaz_retrieval_request(m_retrieval,
159                                 input_schema, input_syntax,
160                                 &match_schema, &match_syntax,
161                                 &rc,
162                                 &backend_schema, &backend_syntax);
163
164     // error handling
165     if (ret_code != 0)
166     {
167         // need to construct present error package and send back
168
169         Z_APDU *apdu = 0;
170
171         const char *details = 0;
172         if (ret_code == -1) /* error ? */
173         {
174            details = yaz_retrieval_get_error(m_retrieval);
175            apdu = odr_en.create_presentResponse(
176                gdu_req->u.z3950,
177                YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS, details);
178         }
179         else if (ret_code == 1 || ret_code == 3)
180         {
181             details = input_schema;
182             apdu = odr_en.create_presentResponse(
183                 gdu_req->u.z3950,
184                 YAZ_BIB1_ELEMENT_SET_NAMES_UNSUPP, details);
185         }
186         else if (ret_code == 2)
187         {
188             char oidbuf[OID_STR_MAX];
189             oid_oid_to_dotstring(input_syntax, oidbuf);
190             details = odr_strdup(odr_en, oidbuf);
191             
192             apdu = odr_en.create_presentResponse(
193                 gdu_req->u.z3950,
194                 YAZ_BIB1_RECORD_SYNTAX_UNSUPP, details);
195         }
196         package.response() = apdu;
197         return;
198     }
199
200     // now re-coding the z3950 backend present request
201      
202     if (backend_syntax) 
203         pr_req->preferredRecordSyntax = odr_oiddup(odr_en, backend_syntax);
204     else
205         pr_req->preferredRecordSyntax = 0;
206     
207
208     // z3950'fy record schema
209     if (backend_schema)
210     {
211         pr_req->recordComposition 
212             = (Z_RecordComposition *) 
213               odr_malloc(odr_en, sizeof(Z_RecordComposition));
214         pr_req->recordComposition->which 
215             = Z_RecordComp_simple;
216         pr_req->recordComposition->u.simple 
217             = (Z_ElementSetNames *)
218                odr_malloc(odr_en, sizeof(Z_ElementSetNames));
219         pr_req->recordComposition->u.simple->which = Z_ElementSetNames_generic;
220         pr_req->recordComposition->u.simple->u.generic 
221             = odr_strdup(odr_en, backend_schema);
222     }
223
224     // attaching Z3950 package to filter chain
225     package.request() = gdu_req;
226
227     package.move();
228
229    //check successful Z3950 present response
230     Z_GDU *gdu_res = package.response().get();
231     if (!gdu_res || gdu_res->which != Z_GDU_Z3950 
232         || gdu_res->u.z3950->which != Z_APDU_presentResponse
233         || !gdu_res->u.z3950->u.presentResponse)
234
235     {
236         package.session().close();
237         return;
238     }
239     
240
241     // everything fine, continuing
242     // std::cout << "z3950_present_request OK\n";
243     // std::cout << "back z3950 " << *gdu_res << "\n";
244
245     Z_PresentResponse * pr_res = gdu_res->u.z3950->u.presentResponse;
246
247     // let non surrogate dioagnostics in Z3950 present response package
248     // pass to frontend - just return
249     if (pr_res->records 
250         && pr_res->records->which == Z_Records_NSD
251         && pr_res->records->u.nonSurrogateDiagnostic)
252     {
253         // we might do more clever tricks to "reverse"
254         // these error(s).
255
256         //*pr_res->records->u.nonSurrogateDiagnostic->condition = 
257         // YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
258     }
259
260     // record transformation must take place 
261     if (rc && pr_res 
262         && pr_res->numberOfRecordsReturned 
263         && *(pr_res->numberOfRecordsReturned) > 0
264         && pr_res->records
265         && pr_res->records->which == Z_Records_DBOSD
266         && pr_res->records->u.databaseOrSurDiagnostics->num_records)
267     {
268          //transform all records
269          for (int i = 0; 
270               i < pr_res->records->u.databaseOrSurDiagnostics->num_records; 
271               i++)
272          {
273              Z_NamePlusRecord *npr 
274                  = pr_res->records->u.databaseOrSurDiagnostics->records[i];
275              if (npr->which == Z_NamePlusRecord_databaseRecord)
276              {
277                  WRBUF output_record = wrbuf_alloc();
278                  Z_External *r = npr->u.databaseRecord;
279                  int ret_trans = 0;
280                  if (r->which == Z_External_OPAC)
281                  {
282 #if YAZ_VERSIONL >= 0x030011
283                      ret_trans = 
284                          yaz_record_conv_opac_record(rc, r->u.opac,
285                                                      output_record);
286 #else
287                      ;
288 #endif
289                  }
290                  else if (r->which == Z_External_octet) 
291                  {
292                      ret_trans =
293                          yaz_record_conv_record(rc, (const char *)
294                                                 r->u.octet_aligned->buf, 
295                                                 r->u.octet_aligned->len,
296                                                 output_record);
297                  }
298                  if (ret_trans == 0)
299                  {
300                      npr->u.databaseRecord =
301                          z_ext_record_oid(odr_en, match_syntax,
302                                           wrbuf_buf(output_record),
303                                           wrbuf_len(output_record));
304                  }
305                  else
306                  {
307                      pr_res->records->
308                          u.databaseOrSurDiagnostics->records[i] 
309                          =  zget_surrogateDiagRec(
310                              odr_en, npr->databaseName,
311                              YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
312                              yaz_record_conv_get_error(rc));
313                  }
314                  wrbuf_destroy(output_record);
315              }
316          }
317     }
318     package.response() = gdu_res;
319     return;
320 }
321
322
323 static mp::filter::Base* filter_creator()
324 {
325     return new mp::filter::RecordTransform;
326 }
327
328 extern "C" {
329     struct metaproxy_1_filter_struct metaproxy_1_filter_record_transform = {
330         0,
331         "record_transform",
332         filter_creator
333     };
334 }
335
336
337 /*
338  * Local variables:
339  * c-basic-offset: 4
340  * c-file-style: "Stroustrup"
341  * indent-tabs-mode: nil
342  * End:
343  * vim: shiftwidth=4 tabstop=8 expandtab
344  */
345