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