Add path to configure method of filter.
[metaproxy-moved-to-github.git] / src / filter_record_transform.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2011 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                                     const char *path)
61 {
62     m_p->configure(xmlnode);
63 }
64
65 void yf::RecordTransform::process(mp::Package &package) const
66 {
67     m_p->process(package);
68 }
69
70
71 // define Implementation stuff
72
73
74
75 yf::RecordTransform::Impl::Impl() 
76 {
77     m_retrieval = yaz_retrieval_create();
78     assert(m_retrieval);
79 }
80
81 yf::RecordTransform::Impl::~Impl()
82
83     if (m_retrieval)
84         yaz_retrieval_destroy(m_retrieval);
85 }
86
87 void yf::RecordTransform::Impl::configure(const xmlNode *xml_node)
88 {
89     //const char *srcdir = getenv("srcdir");
90     //if (srcdir)
91     //    yaz_retrieval_set_path(m_retrieval, srcdir);
92
93     if (!xml_node)
94         throw mp::XMLError("RecordTransform filter config: empty XML DOM");
95
96     // parsing down to retrieval node, which can be any of the children nodes
97     xmlNode *retrieval_node;
98     for (retrieval_node = xml_node->children; 
99          retrieval_node; 
100          retrieval_node = retrieval_node->next)
101     {
102         if (retrieval_node->type != XML_ELEMENT_NODE)
103             continue;
104         if (0 == strcmp((const char *) retrieval_node->name, "retrievalinfo"))
105             break;
106     }
107
108     // read configuration
109     if ( 0 != yaz_retrieval_configure(m_retrieval, retrieval_node)){
110         std::string msg("RecordTransform filter config: ");
111         msg += yaz_retrieval_get_error(m_retrieval);
112         throw mp::XMLError(msg);
113     }
114 }
115
116 void yf::RecordTransform::Impl::process(mp::Package &package) const
117 {
118
119     Z_GDU *gdu_req = package.request().get();
120     
121     // only working on z3950 present packages
122     if (!gdu_req 
123         || !(gdu_req->which == Z_GDU_Z3950) 
124         || !(gdu_req->u.z3950->which == Z_APDU_presentRequest))
125     {
126         package.move();
127         return;
128     }
129     
130     // getting original present request
131     Z_PresentRequest *pr_req = gdu_req->u.z3950->u.presentRequest;
132
133     // setting up ODR's for memory during encoding/decoding
134     //mp::odr odr_de(ODR_DECODE);  
135     mp::odr odr_en(ODR_ENCODE);
136
137     // setting up variables for conversion state
138     yaz_record_conv_t rc = 0;
139     int ret_code;
140
141     const char *input_schema = 0;
142     Odr_oid *input_syntax = 0;
143
144     if(pr_req->recordComposition){
145         input_schema 
146             = mp_util::record_composition_to_esn(pr_req->recordComposition);
147     }
148     if(pr_req->preferredRecordSyntax){
149         input_syntax = pr_req->preferredRecordSyntax;
150     }
151     
152     const char *match_schema = 0;
153     Odr_oid *match_syntax = 0;
154
155     const char *backend_schema = 0;
156     Odr_oid *backend_syntax = 0;
157
158     ret_code 
159         = yaz_retrieval_request(m_retrieval,
160                                 input_schema, input_syntax,
161                                 &match_schema, &match_syntax,
162                                 &rc,
163                                 &backend_schema, &backend_syntax);
164
165     // error handling
166     if (ret_code != 0)
167     {
168         // need to construct present error package and send back
169
170         Z_APDU *apdu = 0;
171
172         const char *details = 0;
173         if (ret_code == -1) /* error ? */
174         {
175            details = yaz_retrieval_get_error(m_retrieval);
176            apdu = odr_en.create_presentResponse(
177                gdu_req->u.z3950,
178                YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS, details);
179         }
180         else if (ret_code == 1 || ret_code == 3)
181         {
182             details = input_schema;
183             apdu = odr_en.create_presentResponse(
184                 gdu_req->u.z3950,
185                 YAZ_BIB1_ELEMENT_SET_NAMES_UNSUPP, details);
186         }
187         else if (ret_code == 2)
188         {
189             char oidbuf[OID_STR_MAX];
190             oid_oid_to_dotstring(input_syntax, oidbuf);
191             details = odr_strdup(odr_en, oidbuf);
192             
193             apdu = odr_en.create_presentResponse(
194                 gdu_req->u.z3950,
195                 YAZ_BIB1_RECORD_SYNTAX_UNSUPP, details);
196         }
197         package.response() = apdu;
198         return;
199     }
200
201     // now re-coding the z3950 backend present request
202      
203     if (backend_syntax) 
204         pr_req->preferredRecordSyntax = odr_oiddup(odr_en, backend_syntax);
205     else
206         pr_req->preferredRecordSyntax = 0;
207     
208
209     // z3950'fy record schema
210     if (backend_schema)
211     {
212         pr_req->recordComposition 
213             = (Z_RecordComposition *) 
214               odr_malloc(odr_en, sizeof(Z_RecordComposition));
215         pr_req->recordComposition->which 
216             = Z_RecordComp_simple;
217         pr_req->recordComposition->u.simple 
218             = (Z_ElementSetNames *)
219                odr_malloc(odr_en, sizeof(Z_ElementSetNames));
220         pr_req->recordComposition->u.simple->which = Z_ElementSetNames_generic;
221         pr_req->recordComposition->u.simple->u.generic 
222             = odr_strdup(odr_en, backend_schema);
223     }
224
225     // attaching Z3950 package to filter chain
226     package.request() = gdu_req;
227
228     package.move();
229
230    //check successful Z3950 present response
231     Z_GDU *gdu_res = package.response().get();
232     if (!gdu_res || gdu_res->which != Z_GDU_Z3950 
233         || gdu_res->u.z3950->which != Z_APDU_presentResponse
234         || !gdu_res->u.z3950->u.presentResponse)
235
236     {
237         package.session().close();
238         return;
239     }
240     
241
242     // everything fine, continuing
243     // std::cout << "z3950_present_request OK\n";
244     // std::cout << "back z3950 " << *gdu_res << "\n";
245
246     Z_PresentResponse * pr_res = gdu_res->u.z3950->u.presentResponse;
247
248     // let non surrogate dioagnostics in Z3950 present response package
249     // pass to frontend - just return
250     if (pr_res->records 
251         && pr_res->records->which == Z_Records_NSD
252         && pr_res->records->u.nonSurrogateDiagnostic)
253     {
254         // we might do more clever tricks to "reverse"
255         // these error(s).
256
257         //*pr_res->records->u.nonSurrogateDiagnostic->condition = 
258         // YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
259     }
260
261     // record transformation must take place 
262     if (rc && pr_res 
263         && pr_res->numberOfRecordsReturned 
264         && *(pr_res->numberOfRecordsReturned) > 0
265         && pr_res->records
266         && pr_res->records->which == Z_Records_DBOSD
267         && pr_res->records->u.databaseOrSurDiagnostics->num_records)
268     {
269          //transform all records
270          for (int i = 0; 
271               i < pr_res->records->u.databaseOrSurDiagnostics->num_records; 
272               i++)
273          {
274              Z_NamePlusRecord *npr 
275                  = pr_res->records->u.databaseOrSurDiagnostics->records[i];
276              if (npr->which == Z_NamePlusRecord_databaseRecord)
277              {
278                  WRBUF output_record = wrbuf_alloc();
279                  Z_External *r = npr->u.databaseRecord;
280                  int ret_trans = 0;
281                  if (r->which == Z_External_OPAC)
282                  {
283 #if YAZ_VERSIONL >= 0x030011
284                      ret_trans = 
285                          yaz_record_conv_opac_record(rc, r->u.opac,
286                                                      output_record);
287 #else
288                      ;
289 #endif
290                  }
291                  else if (r->which == Z_External_octet) 
292                  {
293                      ret_trans =
294                          yaz_record_conv_record(rc, (const char *)
295                                                 r->u.octet_aligned->buf, 
296                                                 r->u.octet_aligned->len,
297                                                 output_record);
298                  }
299                  if (ret_trans == 0)
300                  {
301                      npr->u.databaseRecord =
302                          z_ext_record_oid(odr_en, match_syntax,
303                                           wrbuf_buf(output_record),
304                                           wrbuf_len(output_record));
305                  }
306                  else
307                  {
308                      pr_res->records->
309                          u.databaseOrSurDiagnostics->records[i] 
310                          =  zget_surrogateDiagRec(
311                              odr_en, npr->databaseName,
312                              YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
313                              yaz_record_conv_get_error(rc));
314                  }
315                  wrbuf_destroy(output_record);
316              }
317          }
318     }
319     package.response() = gdu_res;
320     return;
321 }
322
323
324 static mp::filter::Base* filter_creator()
325 {
326     return new mp::filter::RecordTransform;
327 }
328
329 extern "C" {
330     struct metaproxy_1_filter_struct metaproxy_1_filter_record_transform = {
331         0,
332         "record_transform",
333         filter_creator
334     };
335 }
336
337
338 /*
339  * Local variables:
340  * c-basic-offset: 4
341  * c-file-style: "Stroustrup"
342  * indent-tabs-mode: nil
343  * End:
344  * vim: shiftwidth=4 tabstop=8 expandtab
345  */
346