Retrieval transform operational
[metaproxy-moved-to-github.git] / src / filter_zoom.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2011 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_zoom.hpp"
21 #include <yaz/zoom.h>
22 #include <metaproxy/package.hpp>
23 #include <metaproxy/util.hpp>
24 #include "torus.hpp"
25
26 #include <libxslt/xsltutils.h>
27 #include <libxslt/transform.h>
28
29 #include <boost/thread/mutex.hpp>
30 #include <boost/thread/condition.hpp>
31 #include <yaz/oid_db.h>
32 #include <yaz/diagbib1.h>
33 #include <yaz/log.h>
34 #include <yaz/zgdu.h>
35 #include <yaz/querytowrbuf.h>
36
37 namespace mp = metaproxy_1;
38 namespace yf = mp::filter;
39
40 namespace metaproxy_1 {
41     namespace filter {
42         struct Zoom::Searchable {
43             std::string database;
44             std::string target;
45             std::string query_encoding;
46             std::string sru;
47             std::string request_syntax;
48             std::string element_set;
49             std::string record_encoding;
50             std::string transform_xsl_fname;
51             bool use_turbomarc;
52             bool piggyback;
53             Searchable();
54             ~Searchable();
55         };
56         class Zoom::Backend {
57             friend class Impl;
58             friend class Frontend;
59             std::string zurl;
60             ZOOM_connection m_connection;
61             ZOOM_resultset m_resultset;
62             std::string m_frontend_database;
63             SearchablePtr sptr;
64             xsltStylesheetPtr xsp;
65         public:
66             Backend(SearchablePtr sptr);
67             ~Backend();
68             void connect(std::string zurl, int *error, const char **addinfo);
69             void search_pqf(const char *pqf, Odr_int *hits,
70                             int *error, const char **addinfo);
71             void present(Odr_int start, Odr_int number, ZOOM_record *recs,
72                          int *error, const char **addinfo);
73             void set_option(const char *name, const char *value);
74             int get_error(const char **addinfo);
75         };
76         class Zoom::Frontend {
77             friend class Impl;
78             Impl *m_p;
79             bool m_is_virtual;
80             bool m_in_use;
81             yazpp_1::GDU m_init_gdu;
82             BackendPtr m_backend;
83             void handle_package(mp::Package &package);
84             void handle_search(mp::Package &package);
85             void handle_present(mp::Package &package);
86             BackendPtr get_backend_from_databases(std::string &database,
87                                                   int *error,
88                                                   const char **addinfo);
89             Z_Records *get_records(Odr_int start,
90                                    Odr_int number_to_present,
91                                    int *error,
92                                    const char **addinfo,
93                                    Odr_int *number_of_records_returned,
94                                    ODR odr, BackendPtr b,
95                                    Odr_oid *preferredRecordSyntax,
96                                    const char *element_set_name);
97         public:
98             Frontend(Impl *impl);
99             ~Frontend();
100         };
101         class Zoom::Impl {
102             friend class Frontend;
103         public:
104             Impl();
105             ~Impl();
106             void process(metaproxy_1::Package & package);
107             void configure(const xmlNode * ptr, bool test_only);
108         private:
109             FrontendPtr get_frontend(mp::Package &package);
110             void release_frontend(mp::Package &package);
111             void parse_torus(const xmlNode *ptr);
112
113             std::list<Zoom::SearchablePtr>m_searchables;
114
115             std::map<mp::Session, FrontendPtr> m_clients;            
116             boost::mutex m_mutex;
117             boost::condition m_cond_session_ready;
118             mp::Torus torus;
119         };
120     }
121 }
122
123 // define Pimpl wrapper forwarding to Impl
124  
125 yf::Zoom::Zoom() : m_p(new Impl)
126 {
127 }
128
129 yf::Zoom::~Zoom()
130 {  // must have a destructor because of boost::scoped_ptr
131 }
132
133 void yf::Zoom::configure(const xmlNode *xmlnode, bool test_only)
134 {
135     m_p->configure(xmlnode, test_only);
136 }
137
138 void yf::Zoom::process(mp::Package &package) const
139 {
140     m_p->process(package);
141 }
142
143
144 // define Implementation stuff
145
146 yf::Zoom::Backend::Backend(SearchablePtr ptr) : sptr(ptr)
147 {
148     m_connection = ZOOM_connection_create(0);
149     m_resultset = 0;
150 }
151
152 yf::Zoom::Backend::~Backend()
153 {
154     if (xsp)
155         xsltFreeStylesheet(xsp);
156     ZOOM_connection_destroy(m_connection);
157     ZOOM_resultset_destroy(m_resultset);
158 }
159
160 void yf::Zoom::Backend::connect(std::string zurl,
161                                 int *error, const char **addinfo)
162 {
163     ZOOM_connection_connect(m_connection, zurl.c_str(), 0);
164     *error = ZOOM_connection_error(m_connection, 0, addinfo);
165 }
166
167 void yf::Zoom::Backend::search_pqf(const char *pqf, Odr_int *hits,
168                                    int *error, const char **addinfo)
169 {
170     m_resultset = ZOOM_connection_search_pqf(m_connection, pqf);
171     *error = ZOOM_connection_error(m_connection, 0, addinfo);
172     if (*error == 0)
173         *hits = ZOOM_resultset_size(m_resultset);
174     else
175         *hits = 0;
176 }
177
178 void yf::Zoom::Backend::present(Odr_int start, Odr_int number,
179                                 ZOOM_record *recs,
180                                 int *error, const char **addinfo)
181 {
182     ZOOM_resultset_records(m_resultset, recs, start, number);
183     *error = ZOOM_connection_error(m_connection, 0, addinfo);
184 }
185
186 void yf::Zoom::Backend::set_option(const char *name, const char *value)
187 {
188     ZOOM_connection_option_set(m_connection, name, value);
189     if (m_resultset)
190         ZOOM_resultset_option_set(m_resultset, name, value);
191 }
192
193 int yf::Zoom::Backend::get_error(const char **addinfo)
194 {
195     return ZOOM_connection_error(m_connection, 0, addinfo);
196 }
197
198 yf::Zoom::Searchable::Searchable()
199 {
200     piggyback = true;
201     use_turbomarc = false;
202 }
203
204 yf::Zoom::Searchable::~Searchable()
205 {
206 }
207
208 yf::Zoom::Frontend::Frontend(Impl *impl) : 
209     m_p(impl), m_is_virtual(false), m_in_use(true)
210 {
211 }
212
213 yf::Zoom::Frontend::~Frontend()
214 {
215 }
216
217 yf::Zoom::FrontendPtr yf::Zoom::Impl::get_frontend(mp::Package &package)
218 {
219     boost::mutex::scoped_lock lock(m_mutex);
220
221     std::map<mp::Session,yf::Zoom::FrontendPtr>::iterator it;
222     
223     while(true)
224     {
225         it = m_clients.find(package.session());
226         if (it == m_clients.end())
227             break;
228         
229         if (!it->second->m_in_use)
230         {
231             it->second->m_in_use = true;
232             return it->second;
233         }
234         m_cond_session_ready.wait(lock);
235     }
236     FrontendPtr f(new Frontend(this));
237     m_clients[package.session()] = f;
238     f->m_in_use = true;
239     return f;
240 }
241
242 void yf::Zoom::Impl::release_frontend(mp::Package &package)
243 {
244     boost::mutex::scoped_lock lock(m_mutex);
245     std::map<mp::Session,yf::Zoom::FrontendPtr>::iterator it;
246     
247     it = m_clients.find(package.session());
248     if (it != m_clients.end())
249     {
250         if (package.session().is_closed())
251         {
252             m_clients.erase(it);
253         }
254         else
255         {
256             it->second->m_in_use = false;
257         }
258         m_cond_session_ready.notify_all();
259     }
260 }
261
262 yf::Zoom::Impl::Impl()
263 {
264 }
265
266 yf::Zoom::Impl::~Impl()
267
268 }
269
270 void yf::Zoom::Impl::parse_torus(const xmlNode *ptr1)
271 {
272     if (!ptr1)
273         return ;
274     for (ptr1 = ptr1->children; ptr1; ptr1 = ptr1->next)
275     {
276         if (ptr1->type != XML_ELEMENT_NODE)
277             continue;
278         if (!strcmp((const char *) ptr1->name, "record"))
279         {
280             const xmlNode *ptr2 = ptr1;
281             for (ptr2 = ptr2->children; ptr2; ptr2 = ptr2->next)
282             {
283                 if (ptr2->type != XML_ELEMENT_NODE)
284                     continue;
285                 if (!strcmp((const char *) ptr2->name, "layer"))
286                 {
287                     Zoom::SearchablePtr s(new Searchable);
288
289                     const xmlNode *ptr3 = ptr2;
290                     for (ptr3 = ptr3->children; ptr3; ptr3 = ptr3->next)
291                     {
292                         if (ptr3->type != XML_ELEMENT_NODE)
293                             continue;
294                         if (!strcmp((const char *) ptr3->name, "id"))
295                         {
296                             s->database = mp::xml::get_text(ptr3);
297                         }
298                         else if (!strcmp((const char *) ptr3->name, "zurl"))
299                         {
300                             s->target = mp::xml::get_text(ptr3);
301                         }
302                         else if (!strcmp((const char *) ptr3->name, "sru"))
303                         {
304                             s->sru = mp::xml::get_text(ptr3);
305                         }
306                         else if (!strcmp((const char *) ptr3->name,
307                                          "queryEncoding"))
308                         {
309                             s->query_encoding = mp::xml::get_text(ptr3);
310                         }
311                         else if (!strcmp((const char *) ptr3->name,
312                                          "piggyback"))
313                         {
314                             s->piggyback = mp::xml::get_bool(ptr3, true);
315                         }
316                         else if (!strcmp((const char *) ptr3->name,
317                                          "requestSyntax"))
318                         {
319                             s->request_syntax = mp::xml::get_text(ptr3);
320                         }
321                         else if (!strcmp((const char *) ptr3->name,
322                                          "elementSet"))
323                         {
324                             s->element_set = mp::xml::get_text(ptr3);
325                         }
326                         else if (!strcmp((const char *) ptr3->name,
327                                          "recordEncoding"))
328                         {
329                             s->record_encoding = mp::xml::get_text(ptr3);
330                         }
331                         else if (!strcmp((const char *) ptr3->name,
332                                          "transform"))
333                         {
334                             s->transform_xsl_fname = mp::xml::get_text(ptr3);
335                         }
336                         else if (!strcmp((const char *) ptr3->name,
337                                          "useTurboMarc"))
338                         {
339                             yaz_log(YLOG_LOG, "seeing useTurboMarc");
340                             s->use_turbomarc = mp::xml::get_bool(ptr3, false);
341                             yaz_log(YLOG_LOG, "value=%s",
342                                     s->use_turbomarc ? "1" : "0");
343                                     
344                         }
345                     }
346                     if (s->database.length() && s->target.length())
347                     {
348                         yaz_log(YLOG_LOG, "add db=%s target=%s turbomarc=%s", 
349                                 s->database.c_str(), s->target.c_str(),
350                                 s->use_turbomarc ? "1" : "0");
351                         m_searchables.push_back(s);
352                     }
353                 }
354             }
355         }
356     }
357 }
358
359
360 void yf::Zoom::Impl::configure(const xmlNode *ptr, bool test_only)
361 {
362     for (ptr = ptr->children; ptr; ptr = ptr->next)
363     {
364         if (ptr->type != XML_ELEMENT_NODE)
365             continue;
366         if (!strcmp((const char *) ptr->name, "records"))
367         {
368             parse_torus(ptr);
369         }
370         else if (!strcmp((const char *) ptr->name, "torus"))
371         {
372             std::string url;
373             const struct _xmlAttr *attr;
374             for (attr = ptr->properties; attr; attr = attr->next)
375             {
376                 if (!strcmp((const char *) attr->name, "url"))
377                     url = mp::xml::get_text(attr->children);
378                 else
379                     throw mp::filter::FilterException(
380                         "Bad attribute " + std::string((const char *)
381                                                        attr->name));
382             }
383             torus.read_searchables(url);
384             xmlDoc *doc = torus.get_doc();
385             if (doc)
386             {
387                 xmlNode *ptr = xmlDocGetRootElement(doc);
388                 parse_torus(ptr);
389             }
390         }
391         else
392         {
393             throw mp::filter::FilterException
394                 ("Bad element " 
395                  + std::string((const char *) ptr->name)
396                  + " in zoom filter");
397         }
398     }
399 }
400
401 yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
402     std::string &database, int *error, const char **addinfo)
403 {
404     std::list<BackendPtr>::const_iterator map_it;
405     if (m_backend && m_backend->m_frontend_database == database)
406         return m_backend;
407
408     std::list<Zoom::SearchablePtr>::iterator map_s =
409         m_p->m_searchables.begin();
410
411     std::string c_db = mp::util::database_name_normalize(database);
412
413     while (map_s != m_p->m_searchables.end())
414     {
415         if (c_db.compare((*map_s)->database) == 0)
416             break;
417         map_s++;
418     }
419     if (map_s == m_p->m_searchables.end())
420     {
421         *error = YAZ_BIB1_DATABASE_DOES_NOT_EXIST;
422         *addinfo = database.c_str();
423         BackendPtr b;
424         return b;
425     }
426
427     xsltStylesheetPtr xsp = 0;
428     if ((*map_s)->transform_xsl_fname.length())
429     {
430         xmlDoc *xsp_doc = xmlParseFile((*map_s)->transform_xsl_fname.c_str());
431         if (!xsp_doc)
432         {
433             *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
434             *addinfo = "xmlParseFile failed";
435             BackendPtr b;
436             return b;
437         }
438         xsp = xsltParseStylesheetDoc(xsp_doc);
439         if (!xsp)
440         {
441             *error = YAZ_BIB1_DATABASE_DOES_NOT_EXIST;
442             *addinfo = "xsltParseStylesheetDoc failed";
443             BackendPtr b;
444             xmlFreeDoc(xsp_doc);
445             return b;
446         }
447     }
448
449     SearchablePtr sptr = *map_s;
450
451     m_backend.reset();
452
453     BackendPtr b(new Backend(sptr));
454
455     b->xsp = xsp;
456     b->m_frontend_database = database;
457
458     if (sptr->query_encoding.length())
459         b->set_option("rpnCharset", sptr->query_encoding.c_str());
460
461     std::string url;
462     if (sptr->sru.length())
463     {
464         url = "http://" + sptr->target;
465         b->set_option("sru", sptr->sru.c_str());
466     }
467     else
468         url = sptr->target;
469
470     b->connect(url, error, addinfo);
471     if (*error == 0)
472     {
473         m_backend = b;
474     }
475     return b;
476 }
477
478 Z_Records *yf::Zoom::Frontend::get_records(Odr_int start,
479                                            Odr_int number_to_present,
480                                            int *error,
481                                            const char **addinfo,
482                                            Odr_int *number_of_records_returned,
483                                            ODR odr,
484                                            BackendPtr b,
485                                            Odr_oid *preferredRecordSyntax,
486                                            const char *element_set_name)
487 {
488     *number_of_records_returned = 0;
489     Z_Records *records = 0;
490     bool enable_pz2_transform = false;
491
492     if (start < 0 || number_to_present <= 0)
493         return records;
494     
495     if (number_to_present > 10000)
496         number_to_present = 10000;
497     
498     ZOOM_record *recs = (ZOOM_record *)
499         odr_malloc(odr, number_to_present * sizeof(*recs));
500
501     char oid_name_str[OID_STR_MAX];
502     const char *syntax_name = 0;
503
504     if (preferredRecordSyntax)
505     {
506         if (!oid_oidcmp(preferredRecordSyntax, yaz_oid_recsyn_xml)
507             && !strcmp(element_set_name, "pz2"))
508         {
509             if (b->sptr->request_syntax.length())
510             {
511                 syntax_name = b->sptr->request_syntax.c_str();
512                 enable_pz2_transform = true;
513             }
514         }
515         else
516         {
517             syntax_name =
518                 yaz_oid_to_string_buf(preferredRecordSyntax, 0, oid_name_str);
519         }
520     }
521
522     yaz_log(YLOG_LOG, "enable_pz2_transform %s", enable_pz2_transform ?
523             "enabled" : "disabled");
524
525     b->set_option("preferredRecordSyntax", syntax_name);
526
527     if (enable_pz2_transform)
528     {
529         element_set_name = "F";
530         if (b->sptr->element_set.length())
531             element_set_name = b->sptr->element_set.c_str();
532     }
533
534     b->set_option("elementSetName", element_set_name);
535
536     b->present(start, number_to_present, recs, error, addinfo);
537
538     Odr_int i = 0;
539     if (!*error)
540     {
541         for (i = 0; i < number_to_present; i++)
542             if (!recs[i])
543                 break;
544     }
545     if (i > 0)
546     {  // only return records if no error and at least one record
547         char *odr_database = odr_strdup(odr,
548                                         b->m_frontend_database.c_str());
549         Z_NamePlusRecordList *npl = (Z_NamePlusRecordList *)
550             odr_malloc(odr, sizeof(*npl));
551         *number_of_records_returned = i;
552         npl->num_records = i;
553         npl->records = (Z_NamePlusRecord **)
554             odr_malloc(odr, i * sizeof(*npl->records));
555         for (i = 0; i < number_to_present; i++)
556         {
557             Z_NamePlusRecord *npr = 0;
558             const char *addinfo;
559             int sur_error = ZOOM_record_error(recs[i], 0 /* msg */,
560                                               &addinfo, 0 /* diagset */);
561                 
562             if (sur_error)
563             {
564                 npr = zget_surrogateDiagRec(odr, odr_database, sur_error,
565                                             addinfo);
566             }
567             else if (enable_pz2_transform)
568             {
569                 char rec_type_str[100];
570
571                 strcpy(rec_type_str, b->sptr->use_turbomarc ?
572                        "txml" : "xml");
573                 
574                 // prevent buffer overflow ...
575                 if (b->sptr->record_encoding.length() > 0 &&
576                     b->sptr->record_encoding.length() < 
577                     (sizeof(rec_type_str)-20))
578                 {
579                     strcat(rec_type_str, "; charset=");
580                     strcat(rec_type_str, b->sptr->record_encoding.c_str());
581                 }
582                 
583                 int rec_len;
584                 const char *rec_buf = ZOOM_record_get(recs[i], rec_type_str,
585                                                       &rec_len);
586                 if (rec_buf && b->xsp)
587                 {
588                     xmlDoc *rec_doc = xmlParseMemory(rec_buf, rec_len);
589                     if (rec_doc)
590                     { 
591                         xmlDoc *rec_res;
592                         rec_res = xsltApplyStylesheet(b->xsp, rec_doc, 0);
593
594                         if (rec_res)
595                             xsltSaveResultToString((xmlChar **) &rec_buf, &rec_len,
596                                                    rec_res, b->xsp);
597                     }
598                 }
599
600                 if (rec_buf)
601                 {
602                     npr = (Z_NamePlusRecord *) odr_malloc(odr, sizeof(*npr));
603                     npr->databaseName = odr_database;
604                     npr->which = Z_NamePlusRecord_databaseRecord;
605                     npr->u.databaseRecord =
606                         z_ext_record_xml(odr, rec_buf, rec_len);
607                 }
608                 else
609                 {
610                     npr = zget_surrogateDiagRec(
611                         odr, odr_database, 
612                         YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
613                         rec_type_str);
614                 }
615             }
616             else
617             {
618                 Z_External *ext =
619                     (Z_External *) ZOOM_record_get(recs[i], "ext", 0);
620                 if (ext)
621                 {
622                     npr = (Z_NamePlusRecord *) odr_malloc(odr, sizeof(*npr));
623                     npr->databaseName = odr_database;
624                     npr->which = Z_NamePlusRecord_databaseRecord;
625                     npr->u.databaseRecord = ext;
626                 }
627                 else
628                 {
629                     npr = zget_surrogateDiagRec(
630                         odr, odr_database, 
631                         YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
632                         "ZOOM_record, type ext");
633                 }
634             }
635             npl->records[i] = npr;
636         }
637         records = (Z_Records*) odr_malloc(odr, sizeof(*records));
638         records->which = Z_Records_DBOSD;
639         records->u.databaseOrSurDiagnostics = npl;
640     }
641     return records;
642 }
643     
644
645 void yf::Zoom::Frontend::handle_search(mp::Package &package)
646 {
647     Z_GDU *gdu = package.request().get();
648     Z_APDU *apdu_req = gdu->u.z3950;
649     Z_APDU *apdu_res = 0;
650     mp::odr odr;
651     Z_SearchRequest *sr = apdu_req->u.searchRequest;
652     if (sr->num_databaseNames != 1)
653     {
654         apdu_res = odr.create_searchResponse(
655             apdu_req, YAZ_BIB1_TOO_MANY_DATABASES_SPECIFIED, 0);
656         package.response() = apdu_res;
657         return;
658     }
659
660     int error = 0;
661     const char *addinfo = 0;
662     std::string db(sr->databaseNames[0]);
663     BackendPtr b = get_backend_from_databases(db, &error, &addinfo);
664     if (error)
665     {
666         apdu_res = 
667             odr.create_searchResponse(
668                 apdu_req, error, addinfo);
669         package.response() = apdu_res;
670         return;
671     }
672
673     b->set_option("setname", "default");
674
675     Odr_int hits = 0;
676     Z_Query *query = sr->query;
677     if (query->which == Z_Query_type_1 || query->which == Z_Query_type_101)
678     {
679         WRBUF w = wrbuf_alloc();
680         yaz_rpnquery_to_wrbuf(w, query->u.type_1);
681
682         b->search_pqf(wrbuf_cstr(w), &hits, &error, &addinfo);
683         wrbuf_destroy(w);
684     }
685     else
686     {
687         apdu_res = 
688             odr.create_searchResponse(apdu_req, YAZ_BIB1_QUERY_TYPE_UNSUPP, 0);
689         package.response() = apdu_res;
690         return;
691     }
692     
693     const char *element_set_name = 0;
694     Odr_int number_to_present = 0;
695     if (!error)
696         mp::util::piggyback_sr(sr, hits, number_to_present, &element_set_name);
697     
698     Odr_int number_of_records_returned = 0;
699     Z_Records *records = get_records(
700         0, number_to_present, &error, &addinfo,
701         &number_of_records_returned, odr, b, sr->preferredRecordSyntax,
702         element_set_name);
703     apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
704     if (records)
705     {
706         apdu_res->u.searchResponse->records = records;
707         apdu_res->u.searchResponse->numberOfRecordsReturned =
708             odr_intdup(odr, number_of_records_returned);
709     }
710     apdu_res->u.searchResponse->resultCount = odr_intdup(odr, hits);
711     package.response() = apdu_res;
712 }
713
714 void yf::Zoom::Frontend::handle_present(mp::Package &package)
715 {
716     Z_GDU *gdu = package.request().get();
717     Z_APDU *apdu_req = gdu->u.z3950;
718     Z_APDU *apdu_res = 0;
719     Z_PresentRequest *pr = apdu_req->u.presentRequest;
720
721     mp::odr odr;
722     if (!m_backend)
723     {
724         package.response() = odr.create_presentResponse(
725             apdu_req, YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST, 0);
726         return;
727     }
728     const char *element_set_name = 0;
729     Z_RecordComposition *comp = pr->recordComposition;
730     if (comp && comp->which != Z_RecordComp_simple)
731     {
732         package.response() = odr.create_presentResponse(
733             apdu_req, 
734             YAZ_BIB1_PRESENT_COMP_SPEC_PARAMETER_UNSUPP, 0);
735         return;
736     }
737     if (comp && comp->u.simple->which == Z_ElementSetNames_generic)
738         element_set_name = comp->u.simple->u.generic;
739     Odr_int number_of_records_returned = 0;
740     int error = 0;
741     const char *addinfo = 0;
742     Z_Records *records = get_records(
743         *pr->resultSetStartPoint - 1, *pr->numberOfRecordsRequested,
744         &error, &addinfo, &number_of_records_returned, odr, m_backend,
745         pr->preferredRecordSyntax, element_set_name);
746
747     apdu_res = odr.create_presentResponse(apdu_req, error, addinfo);
748     if (records)
749     {
750         apdu_res->u.presentResponse->records = records;
751         apdu_res->u.presentResponse->numberOfRecordsReturned =
752             odr_intdup(odr, number_of_records_returned);
753     }
754     package.response() = apdu_res;
755 }
756
757 void yf::Zoom::Frontend::handle_package(mp::Package &package)
758 {
759     Z_GDU *gdu = package.request().get();
760     if (!gdu)
761         ;
762     else if (gdu->which == Z_GDU_Z3950)
763     {
764         Z_APDU *apdu_req = gdu->u.z3950;
765         if (apdu_req->which == Z_APDU_initRequest)
766         {
767             mp::odr odr;
768             package.response() = odr.create_close(
769                 apdu_req,
770                 Z_Close_protocolError,
771                 "double init");
772         }
773         else if (apdu_req->which == Z_APDU_searchRequest)
774         {
775             handle_search(package);
776         }
777         else if (apdu_req->which == Z_APDU_presentRequest)
778         {
779             handle_present(package);
780         }
781         else
782         {
783             mp::odr odr;
784             package.response() = odr.create_close(
785                 apdu_req,
786                 Z_Close_protocolError,
787                 "zoom filter cannot handle this APDU");
788             package.session().close();
789         }
790     }
791     else
792     {
793         package.session().close();
794     }
795 }
796
797 void yf::Zoom::Impl::process(mp::Package &package)
798 {
799     FrontendPtr f = get_frontend(package);
800     Z_GDU *gdu = package.request().get();
801
802     if (f->m_is_virtual)
803     {
804         f->handle_package(package);
805     }
806     else if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
807              Z_APDU_initRequest)
808     {
809         Z_InitRequest *req = gdu->u.z3950->u.initRequest;
810         f->m_init_gdu = gdu;
811         
812         mp::odr odr;
813         Z_APDU *apdu = odr.create_initResponse(gdu->u.z3950, 0, 0);
814         Z_InitResponse *resp = apdu->u.initResponse;
815         
816         int i;
817         static const int masks[] = {
818             Z_Options_search,
819             Z_Options_present,
820             -1 
821         };
822         for (i = 0; masks[i] != -1; i++)
823             if (ODR_MASK_GET(req->options, masks[i]))
824                 ODR_MASK_SET(resp->options, masks[i]);
825         
826         static const int versions[] = {
827             Z_ProtocolVersion_1,
828             Z_ProtocolVersion_2,
829             Z_ProtocolVersion_3,
830             -1
831         };
832         for (i = 0; versions[i] != -1; i++)
833             if (ODR_MASK_GET(req->protocolVersion, versions[i]))
834                 ODR_MASK_SET(resp->protocolVersion, versions[i]);
835             else
836                 break;
837         
838         *resp->preferredMessageSize = *req->preferredMessageSize;
839         *resp->maximumRecordSize = *req->maximumRecordSize;
840         
841         package.response() = apdu;
842         f->m_is_virtual = true;
843     }
844     else
845         package.move();
846
847     release_frontend(package);
848 }
849
850
851 static mp::filter::Base* filter_creator()
852 {
853     return new mp::filter::Zoom;
854 }
855
856 extern "C" {
857     struct metaproxy_1_filter_struct metaproxy_1_filter_zoom = {
858         0,
859         "zoom",
860         filter_creator
861     };
862 }
863
864
865 /*
866  * Local variables:
867  * c-basic-offset: 4
868  * c-file-style: "Stroustrup"
869  * indent-tabs-mode: nil
870  * End:
871  * vim: shiftwidth=4 tabstop=8 expandtab
872  */
873