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