Use Odr_oid for OIDs. Requires YAZ 3.0.2
[metaproxy-moved-to-github.git] / src / filter_record_transform.cpp
1 /* $Id: filter_record_transform.cpp,v 1.11 2007-05-09 07:14:28 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     Odr_oid *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_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     // now re-coding the z3950 backend present request
193      
194     if (backend_syntax) 
195         pr_req->preferredRecordSyntax = odr_oiddup(odr_en, backend_syntax);
196     else
197         pr_req->preferredRecordSyntax = 0;
198     
199
200     // z3950'fy record schema
201     if (backend_schema)
202     {
203         pr_req->recordComposition 
204             = (Z_RecordComposition *) 
205               odr_malloc(odr_en, sizeof(Z_RecordComposition));
206         pr_req->recordComposition->which 
207             = Z_RecordComp_simple;
208         pr_req->recordComposition->u.simple 
209             = (Z_ElementSetNames *)
210                odr_malloc(odr_en, sizeof(Z_ElementSetNames));
211         pr_req->recordComposition->u.simple->which = Z_ElementSetNames_generic;
212         pr_req->recordComposition->u.simple->u.generic 
213             = odr_strdup(odr_en, backend_schema);
214     }
215
216     // attaching Z3950 package to filter chain
217     package.request() = gdu_req;
218
219     package.move();
220
221    //check successful Z3950 present response
222     Z_GDU *gdu_res = package.response().get();
223     if (!gdu_res || gdu_res->which != Z_GDU_Z3950 
224         || gdu_res->u.z3950->which != Z_APDU_presentResponse
225         || !gdu_res->u.z3950->u.presentResponse)
226
227     {
228         std::cout << "record-transform: error back present\n";
229         package.session().close();
230         return;
231     }
232     
233
234     // everything fine, continuing
235     // std::cout << "z3950_present_request OK\n";
236     // std::cout << "back z3950 " << *gdu_res << "\n";
237
238     Z_PresentResponse * pr_res = gdu_res->u.z3950->u.presentResponse;
239
240     // let non surrogate dioagnostics in Z3950 present response package
241     // pass to frontend - just return
242     if (pr_res->records 
243         && pr_res->records->which == Z_Records_NSD
244         && pr_res->records->u.nonSurrogateDiagnostic)
245     {
246         // we might do more clever tricks to "reverse"
247         // these error(s).
248
249         //*pr_res->records->u.nonSurrogateDiagnostic->condition = 
250         // YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
251     }
252
253     // record transformation must take place 
254     if (rc && pr_res 
255         && pr_res->numberOfRecordsReturned 
256         && *(pr_res->numberOfRecordsReturned) > 0
257         && pr_res->records
258         && pr_res->records->which == Z_Records_DBOSD
259         && pr_res->records->u.databaseOrSurDiagnostics->num_records)
260     {
261          //transform all records
262          for (int i = 0; 
263               i < pr_res->records->u.databaseOrSurDiagnostics->num_records; 
264               i++)
265          {
266              Z_NamePlusRecord *npr 
267                  = pr_res->records->u.databaseOrSurDiagnostics->records[i];
268              if (npr->which == Z_NamePlusRecord_databaseRecord)
269              {
270                  Z_External *r = npr->u.databaseRecord;
271                  //oident *ent = oid_getentbyoid(r->direct_reference);
272                  if (r->which == Z_External_octet) 
273                  {
274                      WRBUF output_record = wrbuf_alloc();
275                      int ret_trans 
276                          =  yaz_record_conv_record(rc,
277                                                    (const char *)
278                                                    r->u.octet_aligned->buf, 
279                                                    r->u.octet_aligned->len,
280                                                    output_record);
281                      if (ret_trans == 0)
282                      {
283                          npr->u.databaseRecord =
284                              z_ext_record_oid(odr_en, match_syntax,
285                                           wrbuf_buf(output_record),
286                                           wrbuf_len(output_record));
287                      }
288                      else
289                      {
290                          pr_res->records->
291                              u.databaseOrSurDiagnostics->records[i] 
292                              =  zget_surrogateDiagRec(
293                                  odr_en, npr->databaseName,
294                                  YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
295                                  yaz_record_conv_get_error(rc));
296                      }
297                      wrbuf_destroy(output_record);
298                  }
299              }
300          }
301     }
302     package.response() = gdu_res;
303     return;
304 }
305
306
307 static mp::filter::Base* filter_creator()
308 {
309     return new mp::filter::RecordTransform;
310 }
311
312 extern "C" {
313     struct metaproxy_1_filter_struct metaproxy_1_filter_record_transform = {
314         0,
315         "record_transform",
316         filter_creator
317     };
318 }
319
320
321 /*
322  * Local variables:
323  * c-basic-offset: 4
324  * indent-tabs-mode: nil
325  * c-file-style: "stroustrup"
326  * End:
327  * vim: shiftwidth=4 tabstop=8 expandtab
328  */