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