GPL v2.
[metaproxy-moved-to-github.git] / src / filter_backend_test.cpp
1 /* $Id: filter_backend_test.cpp,v 1.26 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
24 #include "filter.hpp"
25 #include "package.hpp"
26 #include "util.hpp"
27 #include "filter_backend_test.hpp"
28
29 #include <stdexcept>
30 #include <list>
31 #include <map>
32 #include <iostream>
33
34 #include <boost/thread/mutex.hpp>
35
36 #include <yaz/zgdu.h>
37 #include <yaz/log.h>
38 #include <yaz/otherinfo.h>
39 #include <yaz/diagbib1.h>
40 #include <yaz/oid_db.h>
41
42 namespace mp = metaproxy_1;
43 namespace yf = mp::filter;
44 using namespace mp;
45
46 namespace metaproxy_1 {
47     namespace filter {
48         class Session_info {
49             int dummy;
50         };
51         class BackendTest::Rep {
52             friend class BackendTest;
53
54             Z_Records *fetch(
55                 ODR odr, Odr_oid *preferredRecordSyntax,
56                 Z_ElementSetNames *esn,
57                 int start, int number, int &error_code, std::string &addinfo,
58                 int *number_returned, int *next_position);
59
60             bool m_support_named_result_sets;
61
62             session_map<Session_info> m_sessions;
63         };
64     }
65 }
66
67
68 static const int result_set_size = 42;
69
70 // an ISO2709 USMARC/MARC21 record that we return..
71 static const char *marc_record =
72   "\x30\x30\x33\x36\x36\x6E\x61\x6D\x20\x20\x32\x32\x30\x30\x31\x36"
73   "\x39\x38\x61\x20\x34\x35\x30\x30\x30\x30\x31\x30\x30\x31\x33\x30"
74   "\x30\x30\x30\x30\x30\x30\x33\x30\x30\x30\x34\x30\x30\x30\x31\x33"
75   "\x30\x30\x35\x30\x30\x31\x37\x30\x30\x30\x31\x37\x30\x30\x38\x30"
76   "\x30\x34\x31\x30\x30\x30\x33\x34\x30\x31\x30\x30\x30\x31\x37\x30"
77   "\x30\x31\x37\x39\x30\x34\x30\x30\x30\x31\x33\x30\x30\x30\x37\x35"
78   "\x30\x35\x30\x30\x30\x31\x32\x30\x30\x30\x38\x38\x31\x30\x30\x30"
79   "\x30\x31\x37\x30\x30\x31\x30\x30\x32\x34\x35\x30\x30\x33\x30\x30"
80   "\x30\x31\x31\x37\x32\x36\x30\x30\x30\x31\x32\x30\x30\x31\x34\x37"
81   "\x32\x36\x33\x30\x30\x30\x39\x30\x30\x31\x35\x39\x33\x30\x30\x30"
82   "\x30\x31\x31\x30\x30\x31\x36\x38\x1E\x20\x20\x20\x31\x31\x32\x32"
83   "\x34\x34\x36\x36\x20\x1E\x44\x4C\x43\x1E\x30\x30\x30\x30\x30\x30"
84   "\x30\x30\x30\x30\x30\x30\x30\x30\x2E\x30\x1E\x39\x31\x30\x37\x31"
85   "\x30\x63\x31\x39\x39\x31\x30\x37\x30\x31\x6E\x6A\x75\x20\x20\x20"
86   "\x20\x20\x20\x20\x20\x20\x20\x20\x30\x30\x30\x31\x30\x20\x65\x6E"
87   "\x67\x20\x20\x1E\x20\x20\x1F\x61\x44\x4C\x43\x1F\x63\x44\x4C\x43"
88   "\x1E\x30\x30\x1F\x61\x31\x32\x33\x2D\x78\x79\x7A\x1E\x31\x30\x1F"
89   "\x61\x4A\x61\x63\x6B\x20\x43\x6F\x6C\x6C\x69\x6E\x73\x1E\x31\x30"
90   "\x1F\x61\x48\x6F\x77\x20\x74\x6F\x20\x70\x72\x6F\x67\x72\x61\x6D"
91   "\x20\x61\x20\x63\x6F\x6D\x70\x75\x74\x65\x72\x1E\x31\x20\x1F\x61"
92   "\x50\x65\x6E\x67\x75\x69\x6E\x1E\x20\x20\x1F\x61\x38\x37\x31\x30"
93   "\x1E\x20\x20\x1F\x61\x70\x2E\x20\x63\x6D\x2E\x1E\x20\x20\x1F\x61"
94   "\x20\x20\x20\x31\x31\x32\x32\x34\x34\x36\x36\x20\x1E\x1D";
95
96
97 yf::BackendTest::BackendTest() : m_p(new BackendTest::Rep) {
98     m_p->m_support_named_result_sets = false;
99 }
100
101 yf::BackendTest::~BackendTest() {
102 }
103
104 Z_Records *yf::BackendTest::Rep::fetch(
105     ODR odr, Odr_oid *preferredRecordSyntax,
106     Z_ElementSetNames *esn,
107     int start, int number, int &error_code, std::string &addinfo,
108     int *number_returned, int *next_position)
109 {
110     const char *element_set_name = "F"; // default to use
111     
112     if (number + start - 1 > result_set_size || start < 1)
113     {
114         error_code = YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE;
115         return 0;
116     }
117
118     if (!preferredRecordSyntax)
119         preferredRecordSyntax = odr_oiddup(odr, yaz_oid_recsyn_usmarc);
120
121     if (preferredRecordSyntax)
122     {
123         if (!oid_oidcmp(preferredRecordSyntax, yaz_oid_recsyn_xml))
124             ;
125         else if (!oid_oidcmp(preferredRecordSyntax, yaz_oid_recsyn_usmarc))
126             ;
127         else
128         {
129             error_code = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
130             return 0;
131         }
132     }
133
134     // no element set, "B" and "F" are supported
135     if (esn)
136     {
137         if (esn->which != Z_ElementSetNames_generic)
138         {
139             error_code 
140                 = YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
141             return 0;
142         }
143         element_set_name = esn->u.generic;
144     }
145     if (!strcmp(element_set_name, "B") 
146         && !oid_oidcmp(preferredRecordSyntax, yaz_oid_recsyn_usmarc))
147         ; // Brief
148     else if (!strcmp(element_set_name, "F") 
149              && !oid_oidcmp(preferredRecordSyntax, yaz_oid_recsyn_usmarc))
150         ; // Full
151     else if (!strncmp(element_set_name, "FF", 2) 
152              && !oid_oidcmp(preferredRecordSyntax, yaz_oid_recsyn_xml))
153         ; // Huge XML test record
154     else
155     {
156         error_code 
157             = YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
158         addinfo = std::string(element_set_name);
159         return 0;
160     }
161     Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
162     rec->which = Z_Records_DBOSD;
163     rec->u.databaseOrSurDiagnostics = (Z_NamePlusRecordList *)
164         odr_malloc(odr, sizeof(Z_NamePlusRecordList));
165     rec->u.databaseOrSurDiagnostics->num_records = number;
166     rec->u.databaseOrSurDiagnostics->records = (Z_NamePlusRecord **)
167         odr_malloc(odr, sizeof(Z_NamePlusRecord *) * number);
168     int i;
169     for (i = 0; i<number; i++)
170     {
171         rec->u.databaseOrSurDiagnostics->records[i] = (Z_NamePlusRecord *)
172             odr_malloc(odr, sizeof(Z_NamePlusRecord));
173         Z_NamePlusRecord *npr = rec->u.databaseOrSurDiagnostics->records[i];
174         npr->databaseName = 0;
175         npr->which = Z_NamePlusRecord_databaseRecord;
176
177         if (!strncmp(element_set_name, "FF", 2))
178         {   // Huge XML test record
179             size_t sz = 1024;
180             if (element_set_name[2])
181                 sz = atoi(element_set_name+2) * 1024;
182             if (sz < 10)
183                 sz = 10;
184             char *tmp_rec = (char*) xmalloc(sz);
185
186             memset(tmp_rec, 'a', sz);
187             memcpy(tmp_rec, "<a>", 3);
188             memcpy(tmp_rec + sz - 4, "</a>", 4);
189
190             npr->u.databaseRecord = z_ext_record_xml(odr, tmp_rec, sz);
191             xfree(tmp_rec);
192         }
193         else
194         {
195             char *tmp_rec = odr_strdup(odr, marc_record);
196             char offset_str[30];
197             sprintf(offset_str, "test__%09d_", i+start);
198             memcpy(tmp_rec+186, offset_str, strlen(offset_str));
199             npr->u.databaseRecord = z_ext_record_usmarc(
200                 odr, tmp_rec, strlen(tmp_rec));
201         }
202
203     }
204     *number_returned = number;
205     if (start + number > result_set_size)
206         *next_position = 0;
207     else
208         *next_position = start + number;
209     return rec;
210 }
211
212 void yf::BackendTest::process(Package &package) const
213 {
214     Z_GDU *gdu = package.request().get();
215
216     if (!gdu || gdu->which != Z_GDU_Z3950)
217         package.move();
218     else
219     {
220         Z_APDU *apdu_req = gdu->u.z3950;
221         Z_APDU *apdu_res = 0;
222         mp::odr odr;
223         
224         if (apdu_req->which != Z_APDU_initRequest && 
225             !m_p->m_sessions.exist(package.session()))
226         {
227             apdu_res = odr.create_close(apdu_req,
228                                         Z_Close_protocolError,
229                                         "no init for filter_backend_test");
230             package.session().close();
231         }
232         else if (apdu_req->which == Z_APDU_initRequest)
233         {
234             apdu_res = odr.create_initResponse(apdu_req, 0, 0);
235             Z_InitRequest *req = apdu_req->u.initRequest;
236             Z_InitResponse *resp = apdu_res->u.initResponse;
237
238             resp->implementationName = "backend_test";
239             if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
240                 m_p->m_support_named_result_sets = true;
241             
242             int i;
243             static const int masks[] = {
244                 Z_Options_search, Z_Options_present,
245                 Z_Options_namedResultSets, -1 
246             };
247             for (i = 0; masks[i] != -1; i++)
248                 if (ODR_MASK_GET(req->options, masks[i]))
249                     ODR_MASK_SET(resp->options, masks[i]);
250             static const int versions[] = {
251                 Z_ProtocolVersion_1,
252                 Z_ProtocolVersion_2,
253                 Z_ProtocolVersion_3,
254                 -1
255             };
256             for (i = 0; versions[i] != -1; i++)
257                 if (ODR_MASK_GET(req->protocolVersion, versions[i]))
258                     ODR_MASK_SET(resp->protocolVersion, versions[i]);
259                 else
260                     break;
261
262             Session_info info;
263             m_p->m_sessions.create(info, package.session());
264         }
265         else if (apdu_req->which == Z_APDU_searchRequest)
266         {
267             Z_SearchRequest *req = apdu_req->u.searchRequest;
268                 
269             if (!m_p->m_support_named_result_sets && 
270                 strcmp(req->resultSetName, "default"))
271             {
272                 apdu_res = 
273                     odr.create_searchResponse(
274                         apdu_req,  YAZ_BIB1_RESULT_SET_NAMING_UNSUPP, 0);
275             }
276             else
277             {
278                 Z_Records *records = 0;
279                 int number_returned = 0;
280                 int next_position = 0;
281                 int error_code = 0;
282                 std::string addinfo;
283                 
284                 int number = 0;
285                 mp::util::piggyback(*req->smallSetUpperBound,
286                                     *req->largeSetLowerBound,
287                                     *req->mediumSetPresentNumber,
288                                     result_set_size,
289                                     number);
290                 
291                 if (number) 
292                 {   // not a large set for sure 
293                     Z_ElementSetNames *esn;
294                     if (number > *req->smallSetUpperBound)
295                         esn = req->mediumSetElementSetNames;
296                     else
297                         esn = req->smallSetElementSetNames;
298                     records = m_p->fetch(
299                         odr, req->preferredRecordSyntax, esn,
300                         1, number,
301                         error_code, addinfo,
302                         &number_returned,
303                         &next_position);
304                 }
305                 if (error_code)
306                 {
307                     apdu_res = 
308                         odr.create_searchResponse(
309                             apdu_req, error_code, addinfo.c_str());
310                     Z_SearchResponse *resp = apdu_res->u.searchResponse;
311                     *resp->resultCount = result_set_size;
312                 }
313                 else
314                 {
315                     apdu_res = 
316                         odr.create_searchResponse(apdu_req, 0, 0);
317                     Z_SearchResponse *resp = apdu_res->u.searchResponse;
318                     *resp->resultCount = result_set_size;
319                     *resp->numberOfRecordsReturned = number_returned;
320                     *resp->nextResultSetPosition = next_position;
321                     resp->records = records;
322                 }
323             }
324         }
325         else if (apdu_req->which == Z_APDU_presentRequest)
326         { 
327             Z_PresentRequest *req = apdu_req->u.presentRequest;
328             int number_returned = 0;
329             int next_position = 0;
330             int error_code = 0;
331             std::string addinfo;
332             Z_ElementSetNames *esn = 0;
333
334             if (req->recordComposition)
335             {
336                 if (req->recordComposition->which == Z_RecordComp_simple)
337                     esn = req->recordComposition->u.simple;
338                 else
339                 {
340                     apdu_res =
341                         odr.create_presentResponse(
342                             apdu_req,
343                             YAZ_BIB1_ONLY_A_SINGLE_ELEMENT_SET_NAME_SUPPORTED,
344                             0);
345                     package.response() = apdu_res;
346                     return;
347                 }
348             }
349             Z_Records *records = m_p->fetch(
350                 odr, req->preferredRecordSyntax, esn,
351                 *req->resultSetStartPoint, *req->numberOfRecordsRequested,
352                 error_code, addinfo,
353                 &number_returned,
354                 &next_position);
355
356             if (error_code)
357             {
358                 apdu_res =
359                     odr.create_presentResponse(apdu_req, error_code,
360                                                addinfo.c_str());
361             }
362             else
363             {
364                 apdu_res =
365                     odr.create_presentResponse(apdu_req, 0, 0);
366                 Z_PresentResponse *resp = apdu_res->u.presentResponse;
367                 resp->records = records;
368                 *resp->numberOfRecordsReturned = number_returned;
369                 *resp->nextResultSetPosition = next_position;
370             }
371         }
372         else
373         {
374             apdu_res = odr.create_close(apdu_req,
375                                         Z_Close_protocolError,
376                                         "backend_test: unhandled APDU");
377             package.session().close();
378         }
379         if (apdu_res)
380             package.response() = apdu_res;
381     }
382     if (package.session().is_closed())
383         m_p->m_sessions.release(package.session());
384 }
385
386 static mp::filter::Base* filter_creator()
387 {
388     return new mp::filter::BackendTest;
389 }
390
391 extern "C" {
392     struct metaproxy_1_filter_struct metaproxy_1_filter_backend_test = {
393         0,
394         "backend_test",
395         filter_creator
396     };
397 }
398
399
400 /*
401  * Local variables:
402  * c-basic-offset: 4
403  * indent-tabs-mode: nil
404  * c-file-style: "stroustrup"
405  * End:
406  * vim: shiftwidth=4 tabstop=8 expandtab
407  */