Reformat
[metaproxy-moved-to-github.git] / src / filter_record_transform.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2012 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, const char *path);
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, path);
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                                           const char *path)
89 {
90     yaz_retrieval_set_path(m_retrieval, path);
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     {
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     {
146         input_schema 
147             = mp_util::record_composition_to_esn(pr_req->recordComposition);
148     }
149     if (pr_req->preferredRecordSyntax)
150     {
151         input_syntax = pr_req->preferredRecordSyntax;
152     }
153     
154     const char *match_schema = 0;
155     Odr_oid *match_syntax = 0;
156
157     const char *backend_schema = 0;
158     Odr_oid *backend_syntax = 0;
159
160     ret_code 
161         = yaz_retrieval_request(m_retrieval,
162                                 input_schema, input_syntax,
163                                 &match_schema, &match_syntax,
164                                 &rc,
165                                 &backend_schema, &backend_syntax);
166
167     // error handling
168     if (ret_code != 0)
169     {
170         // need to construct present error package and send back
171
172         Z_APDU *apdu = 0;
173
174         const char *details = 0;
175         if (ret_code == -1) /* error ? */
176         {
177            details = yaz_retrieval_get_error(m_retrieval);
178            apdu = odr_en.create_presentResponse(
179                gdu_req->u.z3950,
180                YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS, details);
181         }
182         else if (ret_code == 1 || ret_code == 3)
183         {
184             details = input_schema;
185             apdu = odr_en.create_presentResponse(
186                 gdu_req->u.z3950,
187                 YAZ_BIB1_ELEMENT_SET_NAMES_UNSUPP, details);
188         }
189         else if (ret_code == 2)
190         {
191             char oidbuf[OID_STR_MAX];
192             oid_oid_to_dotstring(input_syntax, oidbuf);
193             details = odr_strdup(odr_en, oidbuf);
194             
195             apdu = odr_en.create_presentResponse(
196                 gdu_req->u.z3950,
197                 YAZ_BIB1_RECORD_SYNTAX_UNSUPP, details);
198         }
199         package.response() = apdu;
200         return;
201     }
202
203     // now re-coding the z3950 backend present request
204      
205     if (backend_syntax) 
206         pr_req->preferredRecordSyntax = odr_oiddup(odr_en, backend_syntax);
207     else
208         pr_req->preferredRecordSyntax = 0;
209     
210
211     // z3950'fy record schema
212     if (backend_schema)
213     {
214         pr_req->recordComposition 
215             = (Z_RecordComposition *) 
216               odr_malloc(odr_en, sizeof(Z_RecordComposition));
217         pr_req->recordComposition->which 
218             = Z_RecordComp_simple;
219         pr_req->recordComposition->u.simple 
220             = (Z_ElementSetNames *)
221                odr_malloc(odr_en, sizeof(Z_ElementSetNames));
222         pr_req->recordComposition->u.simple->which = Z_ElementSetNames_generic;
223         pr_req->recordComposition->u.simple->u.generic 
224             = odr_strdup(odr_en, backend_schema);
225     }
226
227     // attaching Z3950 package to filter chain
228     package.request() = gdu_req;
229
230     package.move();
231
232    //check successful Z3950 present response
233     Z_GDU *gdu_res = package.response().get();
234     if (!gdu_res || gdu_res->which != Z_GDU_Z3950 
235         || gdu_res->u.z3950->which != Z_APDU_presentResponse
236         || !gdu_res->u.z3950->u.presentResponse)
237
238     {
239         package.session().close();
240         return;
241     }
242     
243
244     // everything fine, continuing
245     // std::cout << "z3950_present_request OK\n";
246     // std::cout << "back z3950 " << *gdu_res << "\n";
247
248     Z_PresentResponse * pr_res = gdu_res->u.z3950->u.presentResponse;
249
250     // let non surrogate dioagnostics in Z3950 present response package
251     // pass to frontend - just return
252     if (pr_res->records 
253         && pr_res->records->which == Z_Records_NSD
254         && pr_res->records->u.nonSurrogateDiagnostic)
255     {
256         // we might do more clever tricks to "reverse"
257         // these error(s).
258
259         //*pr_res->records->u.nonSurrogateDiagnostic->condition = 
260         // YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
261     }
262
263     // record transformation must take place 
264     if (rc && pr_res 
265         && pr_res->numberOfRecordsReturned 
266         && *(pr_res->numberOfRecordsReturned) > 0
267         && pr_res->records
268         && pr_res->records->which == Z_Records_DBOSD
269         && pr_res->records->u.databaseOrSurDiagnostics->num_records)
270     {
271          //transform all records
272          for (int i = 0; 
273               i < pr_res->records->u.databaseOrSurDiagnostics->num_records; 
274               i++)
275          {
276              Z_NamePlusRecord *npr 
277                  = pr_res->records->u.databaseOrSurDiagnostics->records[i];
278              if (npr->which == Z_NamePlusRecord_databaseRecord)
279              {
280                  WRBUF output_record = wrbuf_alloc();
281                  Z_External *r = npr->u.databaseRecord;
282                  int ret_trans = 0;
283                  if (r->which == Z_External_OPAC)
284                  {
285 #if YAZ_VERSIONL >= 0x030011
286                      ret_trans = 
287                          yaz_record_conv_opac_record(rc, r->u.opac,
288                                                      output_record);
289 #else
290                      ;
291 #endif
292                  }
293                  else if (r->which == Z_External_octet) 
294                  {
295                      ret_trans =
296                          yaz_record_conv_record(rc, (const char *)
297                                                 r->u.octet_aligned->buf, 
298                                                 r->u.octet_aligned->len,
299                                                 output_record);
300                  }
301                  if (ret_trans == 0)
302                  {
303                      npr->u.databaseRecord =
304                          z_ext_record_oid(odr_en, match_syntax,
305                                           wrbuf_buf(output_record),
306                                           wrbuf_len(output_record));
307                  }
308                  else
309                  {
310                      pr_res->records->
311                          u.databaseOrSurDiagnostics->records[i] 
312                          =  zget_surrogateDiagRec(
313                              odr_en, npr->databaseName,
314                              YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
315                              yaz_record_conv_get_error(rc));
316                  }
317                  wrbuf_destroy(output_record);
318              }
319          }
320     }
321     package.response() = gdu_res;
322     return;
323 }
324
325
326 static mp::filter::Base* filter_creator()
327 {
328     return new mp::filter::RecordTransform;
329 }
330
331 extern "C" {
332     struct metaproxy_1_filter_struct metaproxy_1_filter_record_transform = {
333         0,
334         "record_transform",
335         filter_creator
336     };
337 }
338
339
340 /*
341  * Local variables:
342  * c-basic-offset: 4
343  * c-file-style: "Stroustrup"
344  * indent-tabs-mode: nil
345  * End:
346  * vim: shiftwidth=4 tabstop=8 expandtab
347  */
348