204639ec1bd8ecb3471afc9f12a278a3e5e321b3
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 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 // make std::min actually work on Windows
20 #define NOMINMAX 1
21
22 #include "config.hpp"
23 #include <metaproxy/package.hpp>
24 #include <metaproxy/util.hpp>
25 #include "gduutil.hpp"
26 #include "sru_util.hpp"
27 #include "filter_sru_to_z3950.hpp"
28
29 #include <yaz/srw.h>
30 #include <yaz/pquery.h>
31 #include <yaz/oid_db.h>
32 #include <yaz/log.h>
33 #include <yaz/otherinfo.h>
34 #if YAZ_VERSIONL >= 0x50000
35 #include <yaz/facet.h>
36 #endif
37
38 #include <boost/thread/mutex.hpp>
39 #include <boost/thread/condition.hpp>
40
41 #include <iostream>
42 #include <sstream>
43 #include <string>
44 #include <map>
45
46 namespace mp = metaproxy_1;
47 namespace mp_util = metaproxy_1::util;
48 namespace yf = mp::filter;
49
50 namespace metaproxy_1 {
51     namespace filter {
52         class SRUtoZ3950::Frontend : boost::noncopyable {
53             friend class Impl;
54             bool m_in_use;
55         public:
56             Frontend();
57             ~Frontend();
58         };
59         class SRUtoZ3950::Impl {
60         public:
61             void configure(const xmlNode *xmlnode);
62             void process(metaproxy_1::Package &package);
63         private:
64             FrontendPtr get_frontend(mp::Package &package);
65             void release_frontend(mp::Package &package);
66             std::map<std::string, const xmlNode *> m_database_explain;
67             std::string default_stylesheet;
68
69             typedef std::map<std::string, int> ActiveUrlMap;
70
71             boost::mutex m_url_mutex;
72             boost::condition m_cond_url_ready;
73             ActiveUrlMap m_active_urls;
74
75
76             boost::mutex m_mutex_session;
77             boost::condition m_cond_session_ready;
78             std::map<mp::Session, FrontendPtr> m_clients;
79         private:
80             void sru(metaproxy_1::Package &package, Z_GDU *zgdu_req);
81             int z3950_build_query(
82                 mp::odr &odr_en, Z_Query *z_query,
83                 const Z_SRW_searchRetrieveRequest *req
84                 ) const;
85
86             bool z3950_init_request(
87                 mp::Package &package,
88                 mp::odr &odr_en,
89                 std::string zurl,
90                 Z_SRW_PDU *sru_pdu_res,
91                 const Z_SRW_PDU *sru_pdu_req
92                 ) const;
93
94             bool z3950_close_request(mp::Package &package) const;
95
96             bool z3950_search_request(
97                 mp::Package &package,
98                 mp::Package &z3950_package,
99                 mp::odr &odr_en,
100                 Z_SRW_PDU *sru_pdu_res,
101                 Z_SRW_searchRetrieveRequest const *sr_req,
102                 std::string zurl,
103                 std::string db_append
104                 ) const;
105
106             bool z3950_present_request(
107                 mp::Package &package,
108                 mp::odr &odr_en,
109                 Z_SRW_PDU *sru_pdu_res,
110                 Z_SRW_searchRetrieveRequest const *sr_req
111                 ) const;
112
113             bool z3950_to_srw_diagnostics_ok(
114                 mp::odr &odr_en,
115                 Z_SRW_searchRetrieveResponse *srw_res,
116                 Z_Records *records
117                 ) const;
118
119             int z3950_to_srw_diag(
120                 mp::odr &odr_en,
121                 Z_SRW_searchRetrieveResponse *srw_res,
122                 Z_DefaultDiagFormat *ddf
123                 ) const;
124
125         };
126     }
127 }
128
129 yf::SRUtoZ3950::SRUtoZ3950() : m_p(new Impl)
130 {
131 }
132
133 yf::SRUtoZ3950::~SRUtoZ3950()
134 {  // must have a destructor because of boost::scoped_ptr
135 }
136
137 void yf::SRUtoZ3950::configure(const xmlNode *xmlnode, bool test_only,
138                                const char *path)
139 {
140     m_p->configure(xmlnode);
141 }
142
143 void yf::SRUtoZ3950::process(mp::Package &package) const
144 {
145     m_p->process(package);
146 }
147
148 void yf::SRUtoZ3950::Impl::configure(const xmlNode *confignode)
149 {
150     const xmlNode * dbnode;
151
152     for (dbnode = confignode->children; dbnode; dbnode = dbnode->next)
153     {
154         if (dbnode->type != XML_ELEMENT_NODE)
155             continue;
156
157         if (!strcmp((const char *) dbnode->name, "database"))
158         {
159             std::string database;
160
161             for (struct _xmlAttr *attr = dbnode->properties;
162                  attr; attr = attr->next)
163             {
164                 mp::xml::check_attribute(attr, "", "name");
165                 database = mp::xml::get_text(attr);
166
167                 const xmlNode *explainnode;
168                 for (explainnode = dbnode->children;
169                      explainnode; explainnode = explainnode->next)
170                 {
171                     if (explainnode->type == XML_ELEMENT_NODE)
172                     {
173                         m_database_explain.insert(
174                             std::make_pair(database, explainnode));
175                         break;
176                     }
177                 }
178             }
179         }
180         else if (!strcmp((const char *) dbnode->name, "stylesheet"))
181         {
182             default_stylesheet = mp::xml::get_text(dbnode);
183         }
184         else
185         {
186             throw mp::filter::FilterException
187                 ("Bad element "
188                  + std::string((const char *) dbnode->name)
189                  + " in sru_z3950"
190                     );
191         }
192     }
193 }
194
195 void yf::SRUtoZ3950::Impl::sru(mp::Package &package, Z_GDU *zgdu_req)
196 {
197     bool ok = true;
198
199     mp::odr odr_de(ODR_DECODE);
200     Z_SRW_PDU *sru_pdu_req = 0;
201
202     mp::odr odr_en(ODR_ENCODE);
203
204     // determine database with the HTTP header information only
205     mp_util::SRUServerInfo sruinfo = mp_util::get_sru_server_info(package);
206     std::map<std::string, const xmlNode *>::iterator idbexp;
207     idbexp = m_database_explain.find(sruinfo.database);
208
209     // assign explain config XML DOM node if database is known
210     const xmlNode *explainnode = 0;
211     if (idbexp != m_database_explain.end())
212     {
213         explainnode = idbexp->second;
214     }
215
216     // decode SRU request
217     Z_SOAP *soap = 0;
218     char *charset = 0;
219     const char *stylesheet = 0;
220     Z_SRW_diagnostic *diagnostic = 0;
221     int num_diagnostic = 0;
222
223     // filter acts as sink for non-valid SRU requests
224     if (! (sru_pdu_req = mp_util::decode_sru_request(package, odr_de, odr_en,
225                                                      &diagnostic,
226                                                      &num_diagnostic, &soap,
227                                                      charset)))
228     {
229         if (soap)
230         {
231             Z_SRW_PDU *sru_pdu_res = yaz_srw_get(odr_en,
232                                                  Z_SRW_explain_response);
233             sru_pdu_res->u.explain_response->diagnostics = diagnostic;
234             sru_pdu_res->u.explain_response->num_diagnostics = num_diagnostic;
235             mp_util::build_sru_explain(package, odr_en, sru_pdu_res,
236                                        sruinfo, explainnode);
237             mp_util::build_sru_response(package, odr_en, soap,
238                                         sru_pdu_res, charset, stylesheet);
239         }
240         else
241         {
242             metaproxy_1::odr odr;
243             Z_GDU *zgdu_res =
244                 odr.create_HTTP_Response(package.session(),
245                                          zgdu_req->u.HTTP_Request, 400);
246             package.response() = zgdu_res;
247         }
248         return;
249     }
250
251     bool enable_package_log = false;
252     std::string zurl;
253     std::string dbargs;
254     Z_SRW_extra_arg *arg;
255
256     for ( arg = sru_pdu_req->extra_args; arg; arg = arg->next)
257         if (!strcmp(arg->name, "x-target"))
258         {
259             zurl = std::string(arg->value);
260         }
261         else if (!strcmp(arg->name, "x-max-sockets"))
262         {
263             package.origin().set_max_sockets(atoi(arg->value));
264         }
265         else if (!strcmp(arg->name, "x-session-id"))
266         {
267             package.origin().set_custom_session(arg->value);
268         }
269         else if (!strcmp(arg->name, "x-log-enable"))
270         {
271             if (*arg->value == '1')
272             {
273                 enable_package_log = true;
274                 package.log_enable();
275             }
276         }
277         else if (!strncmp(arg->name, "x-client-", 9) && arg->value)
278         {
279             if (dbargs.length())
280                 dbargs += '&';
281             dbargs += mp_util::uri_encode(arg->name + 9);
282             dbargs += '=';
283             dbargs += mp_util::uri_encode(arg->value);
284         }
285
286     assert(sru_pdu_req);
287
288     Package z3950_package(package.session(), package.origin());
289     z3950_package.copy_filter(package);
290
291     Z_SRW_PDU *sru_pdu_res = 0;
292     if (sru_pdu_req->which == Z_SRW_explain_request)
293     {
294         Z_SRW_explainRequest *er_req = sru_pdu_req->u.explain_request;
295         stylesheet = er_req->stylesheet;
296         sru_pdu_res = yaz_srw_get_pdu_e(odr_en, Z_SRW_explain_response,
297                                         sru_pdu_req);
298         sru_pdu_res->u.explain_response->diagnostics = diagnostic;
299         sru_pdu_res->u.explain_response->num_diagnostics = num_diagnostic;
300         mp_util::build_sru_explain(package, odr_en, sru_pdu_res,
301                                    sruinfo, explainnode, er_req);
302     }
303     else if (sru_pdu_req->which == Z_SRW_searchRetrieve_request)
304     {
305         Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;
306         stylesheet = sr_req->stylesheet;
307         sru_pdu_res = yaz_srw_get_pdu_e(odr_en, Z_SRW_searchRetrieve_response,
308                                         sru_pdu_req);
309         sru_pdu_res->u.response->diagnostics = diagnostic;
310         sru_pdu_res->u.response->num_diagnostics = num_diagnostic;
311
312         // checking that we have a query
313         ok = mp_util::check_sru_query_exists(package, odr_en,
314                                              sru_pdu_res, sr_req);
315
316         if (ok && z3950_init_request(package, odr_en,
317                                      zurl, sru_pdu_res, sru_pdu_req))
318         {
319             ok = z3950_search_request(package, z3950_package, odr_en,
320                                       sru_pdu_res, sr_req, zurl, dbargs);
321
322             if (ok
323                 && sru_pdu_res->u.response->numberOfRecords
324                 && *(sru_pdu_res->u.response->numberOfRecords))
325
326                 ok = z3950_present_request(package, odr_en,
327                                            sru_pdu_res,
328                                            sr_req);
329             z3950_close_request(package);
330         }
331     }
332     else if (sru_pdu_req->which == Z_SRW_scan_request)
333     {
334         stylesheet = sru_pdu_req->u.scan_request->stylesheet;
335         sru_pdu_res = yaz_srw_get_pdu_e(odr_en, Z_SRW_scan_response,
336                                         sru_pdu_req);
337         sru_pdu_res->u.scan_response->diagnostics = diagnostic;
338         sru_pdu_res->u.scan_response->num_diagnostics = num_diagnostic;
339
340         // we do not do scan at the moment, therefore issuing a diagnostic
341         yaz_add_srw_diagnostic(odr_en, 
342                                &sru_pdu_res->u.scan_response->diagnostics,
343                                &sru_pdu_res->u.scan_response->num_diagnostics,
344                                YAZ_SRW_UNSUPP_OPERATION, "scan");
345     }
346     else
347     {
348         sru_pdu_res =
349             yaz_srw_get_pdu_e(odr_en, Z_SRW_explain_response, sru_pdu_req);
350         sru_pdu_res->u.explain_response->diagnostics = diagnostic;
351         sru_pdu_res->u.explain_response->num_diagnostics = num_diagnostic;
352         yaz_add_srw_diagnostic(odr_en, &diagnostic, &num_diagnostic,
353                                YAZ_SRW_UNSUPP_OPERATION, "unknown");
354     }
355     if (enable_package_log)
356     {
357         std::string l;
358         package.log_reset(l);
359         if (l.length())
360         {
361             mp::wrbuf w;
362
363             wrbuf_puts(w, "<log>\n");
364             wrbuf_xmlputs(w, l.c_str());
365             wrbuf_puts(w, "</log>");
366
367             sru_pdu_res->extraResponseData_len = w.len();
368             sru_pdu_res->extraResponseData_buf =
369                 odr_strdup(odr_en, wrbuf_cstr(w));
370         }
371     }
372     if (!stylesheet && default_stylesheet.length())
373         stylesheet = default_stylesheet.c_str();
374
375     // build and send SRU response
376     mp_util::build_sru_response(package, odr_en, soap,
377                                 sru_pdu_res, charset, stylesheet);
378 }
379
380
381 yf::SRUtoZ3950::Frontend::Frontend() :  m_in_use(true)
382 {
383 }
384
385 yf::SRUtoZ3950::Frontend::~Frontend()
386 {
387 }
388
389
390 yf::SRUtoZ3950::FrontendPtr yf::SRUtoZ3950::Impl::get_frontend(
391     mp::Package &package)
392 {
393     boost::mutex::scoped_lock lock(m_mutex_session);
394
395     std::map<mp::Session,yf::SRUtoZ3950::FrontendPtr>::iterator it;
396
397     while (true)
398     {
399         it = m_clients.find(package.session());
400         if (it == m_clients.end())
401             break;
402
403         if (!it->second->m_in_use)
404         {
405             it->second->m_in_use = true;
406             return it->second;
407         }
408         m_cond_session_ready.wait(lock);
409     }
410     FrontendPtr f(new Frontend);
411     m_clients[package.session()] = f;
412     f->m_in_use = true;
413     return f;
414 }
415
416 void yf::SRUtoZ3950::Impl::release_frontend(mp::Package &package)
417 {
418     boost::mutex::scoped_lock lock(m_mutex_session);
419     std::map<mp::Session,FrontendPtr>::iterator it;
420
421     it = m_clients.find(package.session());
422     if (it != m_clients.end())
423     {
424         if (package.session().is_closed())
425         {
426             m_clients.erase(it);
427         }
428         else
429         {
430             it->second->m_in_use = false;
431         }
432         m_cond_session_ready.notify_all();
433     }
434 }
435
436 void yf::SRUtoZ3950::Impl::process(mp::Package &package)
437 {
438     FrontendPtr f = get_frontend(package);
439
440     Z_GDU *zgdu_req = package.request().get();
441
442     if (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
443     {
444         if (zgdu_req->u.HTTP_Request->content_len == 0)
445         {
446             const char *path = zgdu_req->u.HTTP_Request->path;
447             boost::mutex::scoped_lock lock(m_url_mutex);
448             while (1)
449             {
450                 ActiveUrlMap::iterator it = m_active_urls.find(path);
451                 if (it == m_active_urls.end())
452                 {
453                     m_active_urls[path] = 1;
454                     break;
455                 }
456                 yaz_log(YLOG_LOG, "Waiting for %s to complete", path);
457                 m_cond_url_ready.wait(lock);
458             }
459         }
460         sru(package, zgdu_req);
461         if (zgdu_req && zgdu_req->u.HTTP_Request->content_len == 0)
462         {
463             const char *path = zgdu_req->u.HTTP_Request->path;
464             boost::mutex::scoped_lock lock(m_url_mutex);
465
466             ActiveUrlMap::iterator it = m_active_urls.find(path);
467
468             m_active_urls.erase(it);
469             m_cond_url_ready.notify_all();
470         }
471     }
472     else
473     {
474         package.move();
475     }
476     release_frontend(package);
477 }
478
479 bool
480 yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package,
481                                          mp::odr &odr_en,
482                                          std::string zurl,
483                                          Z_SRW_PDU *sru_pdu_res,
484                                          const Z_SRW_PDU *sru_pdu_req) const
485 {
486     // prepare Z3950 package
487     Package z3950_package(package.session(), package.origin());
488     z3950_package.copy_filter(package);
489
490     // set initRequest APDU
491     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
492     Z_InitRequest *init_req = apdu->u.initRequest;
493
494     Z_IdAuthentication *auth = NULL;
495     if (sru_pdu_req->username && !sru_pdu_req->password)
496     {
497         auth = (Z_IdAuthentication *) odr_malloc(odr_en, sizeof(Z_IdAuthentication));
498         auth->which = Z_IdAuthentication_open;
499         auth->u.open = odr_strdup(odr_en, sru_pdu_req->username);
500     }
501     else if (sru_pdu_req->username && sru_pdu_req->password)
502     {
503         auth = (Z_IdAuthentication *) odr_malloc(odr_en, sizeof(Z_IdAuthentication));
504         auth->which = Z_IdAuthentication_idPass;
505         auth->u.idPass = (Z_IdPass *) odr_malloc(odr_en, sizeof(Z_IdPass));
506         auth->u.idPass->groupId = NULL;
507         auth->u.idPass->password = odr_strdup(odr_en, sru_pdu_req->password);
508         auth->u.idPass->userId = odr_strdup(odr_en, sru_pdu_req->username);
509     }
510
511     init_req->idAuthentication = auth;
512
513     *init_req->preferredMessageSize = 10*1024*1024;
514     *init_req->maximumRecordSize = 10*1024*1024;
515
516     ODR_MASK_SET(init_req->options, Z_Options_search);
517     ODR_MASK_SET(init_req->options, Z_Options_present);
518     ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
519     ODR_MASK_SET(init_req->options, Z_Options_scan);
520
521     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
522     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
523     ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
524
525     if (zurl.length())
526     {
527         std::string host;
528         std::list<std::string> dblist;
529         mp_util::split_zurl(zurl, host, dblist);
530         mp_util::set_vhost_otherinfo(&init_req->otherInfo, odr_en, host, 1);
531     }
532
533     Z_GDU *zgdu_req = package.request().get();
534     if (zgdu_req->which == Z_GDU_HTTP_Request)
535     {
536         Z_HTTP_Request *hreq = zgdu_req->u.HTTP_Request;
537         const char *peer_name =
538             z_HTTP_header_lookup(hreq->headers, "X-Forwarded-For");
539         if (peer_name)
540         {
541             yaz_oi_set_string_oid(&init_req->otherInfo, odr_en,
542                                   yaz_oid_userinfo_client_ip, 1, peer_name);
543         }
544     }
545
546     z3950_package.request() = apdu;
547
548     // send Z3950 package
549     z3950_package.move();
550
551     // check successful initResponse
552     Z_GDU *z3950_gdu = z3950_package.response().get();
553
554     int error = YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE;
555     const char *addinfo = 0;
556     if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
557         && z3950_gdu->u.z3950->which == Z_APDU_initResponse)
558     {
559         Z_InitResponse *initrs = z3950_gdu->u.z3950->u.initResponse;
560         if (*initrs->result)
561             return true;
562         int no = 0;
563         while (1)
564         {
565             Z_DefaultDiagFormat *df = yaz_decode_init_diag(no, initrs);
566
567             if (!df)
568                 break;
569             yaz_add_srw_diagnostic(odr_en,
570                                    &(sru_pdu_res->u.response->diagnostics),
571                                    &(sru_pdu_res->u.response->num_diagnostics),
572                                    yaz_diag_bib1_to_srw(*df->condition),
573                                    df->u.v2Addinfo);
574             no++;
575         }
576         if (no)
577             return false; // got least one diagnostic from init
578
579         // we just have result=false.
580         error = YAZ_SRW_AUTHENTICATION_ERROR;
581     }
582     else
583         addinfo = "sru_z3950: expected initResponse";
584     yaz_add_srw_diagnostic(odr_en,
585                            &(sru_pdu_res->u.response->diagnostics),
586                            &(sru_pdu_res->u.response->num_diagnostics),
587                            error, addinfo);
588     return false;
589 }
590
591 bool yf::SRUtoZ3950::Impl::z3950_close_request(mp::Package &package) const
592 {
593     Package z3950_package(package.session(), package.origin());
594     z3950_package.copy_filter(package);
595     z3950_package.session().close();
596
597     z3950_package.move();
598
599     if (z3950_package.session().is_closed())
600     {
601         return true;
602     }
603     return false;
604 }
605
606 bool yf::SRUtoZ3950::Impl::z3950_search_request(mp::Package &package,
607                                                 mp::Package &z3950_package,
608                                                 mp::odr &odr_en,
609                                                 Z_SRW_PDU *sru_pdu_res,
610                                                 Z_SRW_searchRetrieveRequest
611                                                 const *sr_req,
612                                                 std::string zurl,
613                                                 std::string dbappend) const
614 {
615
616     assert(sru_pdu_res->u.response);
617
618     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
619     Z_SearchRequest *z_searchRequest = apdu->u.searchRequest;
620
621     // RecordSyntax will always be XML
622     z_searchRequest->preferredRecordSyntax
623         = odr_oiddup(odr_en, yaz_oid_recsyn_xml);
624
625     if (!mp_util::set_databases_from_zurl(odr_en, zurl,
626                                           &z_searchRequest->num_databaseNames,
627                                           &z_searchRequest->databaseNames))
628     {
629         std::string db;
630
631         if (sr_req->database)
632             db = sr_req->database;
633         else
634             db = "Default";
635
636         if (dbappend.length())
637         {
638             db += ",";
639             db += dbappend;
640         }
641         z_searchRequest->num_databaseNames = 1;
642         z_searchRequest->databaseNames = (char**)
643             odr_malloc(odr_en, sizeof(char *));
644         z_searchRequest->databaseNames[0] = odr_strdup(odr_en, db.c_str());
645     }
646 #if YAZ_VERSIONL >= 0x50000
647     // yaz_oi_set_facetlist not public in YAZ 4.2.66
648     if (sr_req->facetList)
649     {
650         Z_OtherInformation **oi = &z_searchRequest->additionalSearchInfo;
651         yaz_oi_set_facetlist(oi, odr_en, sr_req->facetList);
652     }
653 #endif
654
655     Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
656     z_searchRequest->query = z_query;
657
658     int sru_diagnostic = z3950_build_query(odr_en, z_query, sr_req);
659     if (sru_diagnostic)
660     {
661         yaz_add_srw_diagnostic(odr_en,
662                                &(sru_pdu_res->u.response->diagnostics),
663                                &(sru_pdu_res->u.response->num_diagnostics),
664                                sru_diagnostic,
665                                "query");
666         return false;
667     }
668
669     z3950_package.request() = apdu;
670
671     z3950_package.move();
672
673     Z_GDU *z3950_gdu = z3950_package.response().get();
674
675     if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950
676         || z3950_gdu->u.z3950->which != Z_APDU_searchResponse
677         || !z3950_gdu->u.z3950->u.searchResponse
678         || !z3950_gdu->u.z3950->u.searchResponse->searchStatus)
679     {
680         yaz_add_srw_diagnostic(odr_en,
681                                &(sru_pdu_res->u.response->diagnostics),
682                                &(sru_pdu_res->u.response->num_diagnostics),
683                                YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, 0);
684         return false;
685     }
686
687     Z_SearchResponse *sr = z3950_gdu->u.z3950->u.searchResponse;
688
689     if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response,
690                                      sr->records))
691     {
692         return false;
693     }
694 #if YAZ_VERSIONL >= 0x50000
695     Z_FacetList *fl = yaz_oi_get_facetlist(&sr->additionalSearchInfo);
696     if (!fl)
697         fl =  yaz_oi_get_facetlist(&sr->otherInfo);
698     sru_pdu_res->u.response->facetList = fl;
699 #endif
700     sru_pdu_res->u.response->numberOfRecords
701         = odr_intdup(odr_en, *sr->resultCount);
702     return true;
703 }
704
705 bool yf::SRUtoZ3950::Impl::z3950_present_request(
706     mp::Package &package,
707     mp::odr &odr_en,
708     Z_SRW_PDU *sru_pdu_res,
709     const Z_SRW_searchRetrieveRequest *sr_req)
710     const
711 {
712     assert(sru_pdu_res->u.response);
713     int start = 1;
714     int max_recs = 0;
715
716     if (!sr_req)
717         return false;
718
719     if (sr_req->maximumRecords)
720         max_recs = *sr_req->maximumRecords;
721     if (sr_req->startRecord)
722         start = *sr_req->startRecord;
723
724     // no need to work if nobody wants record ..
725     if (max_recs == 0)
726         return true;
727
728     bool send_z3950_present = true;
729
730     // recordXPath unsupported.
731     if (sr_req->recordXPath)
732     {
733         send_z3950_present = false;
734         yaz_add_srw_diagnostic(odr_en,
735                                &(sru_pdu_res->u.response->diagnostics),
736                                &(sru_pdu_res->u.response->num_diagnostics),
737                                YAZ_SRW_XPATH_RETRIEVAL_UNSUPP, 0);
738     }
739
740     // resultSetTTL unsupported.
741     // resultSetIdleTime in response
742     if (sr_req->resultSetTTL)
743     {
744         send_z3950_present = false;
745         yaz_add_srw_diagnostic(odr_en,
746                                &(sru_pdu_res->u.response->diagnostics),
747                                &(sru_pdu_res->u.response->num_diagnostics),
748                                YAZ_SRW_RESULT_SETS_UNSUPP, 0);
749     }
750
751     // sort unsupported
752     if (sr_req->sort_type != Z_SRW_sort_type_none)
753     {
754         send_z3950_present = false;
755         yaz_add_srw_diagnostic(odr_en,
756                                &(sru_pdu_res->u.response->diagnostics),
757                                &(sru_pdu_res->u.response->num_diagnostics),
758                                YAZ_SRW_SORT_UNSUPP, 0);
759     }
760
761     // start record requested negative, or larger than number of records
762     if (start < 0 || start > *sru_pdu_res->u.response->numberOfRecords)
763     {
764         send_z3950_present = false;
765         yaz_add_srw_diagnostic(odr_en,
766                                &(sru_pdu_res->u.response->diagnostics),
767                                &(sru_pdu_res->u.response->num_diagnostics),
768                                YAZ_SRW_FIRST_RECORD_POSITION_OUT_OF_RANGE, 0);
769     }
770
771     // maximumRecords requested negative
772     if (max_recs < 0)
773     {
774         send_z3950_present = false;
775         yaz_add_srw_diagnostic(odr_en,
776                                &(sru_pdu_res->u.response->diagnostics),
777                                &(sru_pdu_res->u.response->num_diagnostics),
778                                YAZ_SRW_UNSUPP_PARAMETER_VALUE,
779                                "maximumRecords");
780     }
781
782     // exit on all these above diagnostics
783     if (!send_z3950_present)
784         return false;
785
786     if (max_recs > *sru_pdu_res->u.response->numberOfRecords - start)
787         max_recs = *sru_pdu_res->u.response->numberOfRecords - start + 1;
788
789     Z_SRW_searchRetrieveResponse *sru_res = sru_pdu_res->u.response;
790     sru_res->records = (Z_SRW_record *)
791         odr_malloc(odr_en, max_recs * sizeof(Z_SRW_record));
792     int num = 0;
793     while (num < max_recs)
794     {
795         // now packaging the z3950 present request
796         Package z3950_package(package.session(), package.origin());
797         z3950_package.copy_filter(package);
798         Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
799
800         assert(apdu->u.presentRequest);
801
802         *apdu->u.presentRequest->resultSetStartPoint = start + num;
803         *apdu->u.presentRequest->numberOfRecordsRequested = max_recs - num;
804
805         // set response packing to be same as "request" packing..
806         int record_packing = Z_SRW_recordPacking_XML;
807         if (sr_req->recordPacking && 's' == *(sr_req->recordPacking))
808             record_packing = Z_SRW_recordPacking_string;
809
810         // RecordSyntax will always be XML
811         apdu->u.presentRequest->preferredRecordSyntax
812             = odr_oiddup(odr_en, yaz_oid_recsyn_xml);
813
814         // z3950'fy record schema
815         if (sr_req->recordSchema)
816         {
817             apdu->u.presentRequest->recordComposition
818                 = (Z_RecordComposition *)
819                 odr_malloc(odr_en, sizeof(Z_RecordComposition));
820             apdu->u.presentRequest->recordComposition->which
821                 = Z_RecordComp_simple;
822             apdu->u.presentRequest->recordComposition->u.simple
823                 = mp_util::build_esn_from_schema(odr_en,
824                                                  (const char *)
825                                                  sr_req->recordSchema);
826         }
827
828         // attaching Z3950 package to filter chain
829         z3950_package.request() = apdu;
830
831         // sending Z30.50 present request
832         z3950_package.move();
833
834         //check successful Z3950 present response
835         Z_GDU *z3950_gdu = z3950_package.response().get();
836         if (!z3950_gdu || z3950_gdu->which != Z_GDU_Z3950
837             || z3950_gdu->u.z3950->which != Z_APDU_presentResponse
838             || !z3950_gdu->u.z3950->u.presentResponse)
839
840         {
841             yaz_add_srw_diagnostic(odr_en,
842                                    &(sru_pdu_res->u.response->diagnostics),
843                                    &(sru_pdu_res->u.response->num_diagnostics),
844                                    YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, 0);
845             return false;
846         }
847         // everything fine, continuing
848
849         Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
850
851         // checking non surrogate diagnostics in Z3950 present response package
852         if (!z3950_to_srw_diagnostics_ok(odr_en, sru_pdu_res->u.response,
853                                          pr->records))
854             return false;
855
856         // if anything but database or surrogate diagnostics, stop
857         if (!pr->records || pr->records->which != Z_Records_DBOSD)
858             break;
859         else
860         {
861             // inserting all records
862             int returned_recs =
863                 pr->records->u.databaseOrSurDiagnostics->num_records;
864             for (int i = 0; i < returned_recs; i++)
865             {
866                 int position = i + *apdu->u.presentRequest->resultSetStartPoint;
867                 Z_NamePlusRecord *npr
868                     = pr->records->u.databaseOrSurDiagnostics->records[i];
869
870                 sru_res->records[i + num].recordPacking = record_packing;
871
872                 if (npr->which == Z_NamePlusRecord_surrogateDiagnostic)
873                 {
874                     Z_DiagRec *p = npr->u.surrogateDiagnostic;
875                     if (p->which == Z_DiagRec_defaultFormat)
876                     {
877                         Z_DefaultDiagFormat *df = p->u.defaultFormat;
878                         int c = yaz_diag_bib1_to_srw(*df->condition);
879
880                         yaz_mk_sru_surrogate(
881                             odr_en, sru_res->records + i + num, position,
882                             c, df->u.v2Addinfo);
883                     }
884                     else
885                     {
886                         yaz_mk_sru_surrogate(
887                             odr_en, sru_res->records + i + num, position,
888                             YAZ_SRW_RECORD_TEMPORARILY_UNAVAILABLE, 0);
889                     }
890                 }
891                 else if (npr->which == Z_NamePlusRecord_databaseRecord &&
892                     npr->u.databaseRecord->direct_reference
893                     && !oid_oidcmp(npr->u.databaseRecord->direct_reference,
894                                    yaz_oid_recsyn_xml))
895                 {
896                     // got XML record back
897                     Z_External *r = npr->u.databaseRecord;
898                     sru_res->records[i + num].recordPosition =
899                         odr_intdup(odr_en, position);
900                     sru_res->records[i + num].recordSchema = sr_req->recordSchema;
901                     sru_res->records[i + num].recordData_buf
902                         = odr_strdupn(odr_en,
903                                       (const char *)r->u.octet_aligned->buf,
904                                       r->u.octet_aligned->len);
905                     sru_res->records[i + num].recordData_len
906                         = r->u.octet_aligned->len;
907                 }
908                 else
909                 {
910                     // not XML or no database record at all
911                     yaz_mk_sru_surrogate(
912                         odr_en, sru_res->records + i + num, position,
913                         YAZ_SRW_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA, 0);
914                 }
915             }
916             num += returned_recs;
917         }
918     }
919     sru_res->num_records = num;
920     if (start - 1 + num < *sru_pdu_res->u.response->numberOfRecords)
921         sru_res->nextRecordPosition =
922             odr_intdup(odr_en, start + num);
923     return true;
924 }
925
926 int yf::SRUtoZ3950::Impl::z3950_build_query(
927     mp::odr &odr_en, Z_Query *z_query,
928     const Z_SRW_searchRetrieveRequest *req
929     ) const
930 {
931     if (
932 #ifdef Z_SRW_query_type_cql
933         req->query_type == Z_SRW_query_type_cql
934 #else
935         !strcmp(req->queryType, "cql")
936 #endif
937         )
938     {
939         Z_External *ext = (Z_External *)
940             odr_malloc(odr_en, sizeof(*ext));
941         ext->direct_reference =
942             odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
943         ext->indirect_reference = 0;
944         ext->descriptor = 0;
945         ext->which = Z_External_CQL;
946         ext->u.cql = odr_strdup(odr_en,
947 #ifdef Z_SRW_query_type_cql
948         req->query.cql
949 #else
950         req->query
951 #endif
952             );
953
954         z_query->which = Z_Query_type_104;
955         z_query->u.type_104 =  ext;
956         return 0;
957     }
958
959     if (
960 #ifdef Z_SRW_query_type_pqf
961         req->query_type == Z_SRW_query_type_pqf
962 #else
963         !strcmp(req->queryType, "pqf")
964 #endif
965         )
966     {
967         Z_RPNQuery *RPNquery;
968         YAZ_PQF_Parser pqf_parser;
969
970         pqf_parser = yaz_pqf_create ();
971
972         RPNquery = yaz_pqf_parse (pqf_parser, odr_en,
973 #ifdef Z_SRW_query_type_pqf
974         req->query.pqf
975 #else
976         req->query
977 #endif
978             );
979         yaz_pqf_destroy(pqf_parser);
980
981         if (!RPNquery)
982             return YAZ_SRW_QUERY_SYNTAX_ERROR;
983
984         z_query->which = Z_Query_type_1;
985         z_query->u.type_1 =  RPNquery;
986
987         return 0;
988     }
989     return YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED;
990 }
991
992 bool yf::SRUtoZ3950::Impl::z3950_to_srw_diagnostics_ok(
993     mp::odr &odr_en,
994     Z_SRW_searchRetrieveResponse
995     *sru_res,
996     Z_Records *records) const
997 {
998     // checking non surrogate diagnostics in Z3950 present response package
999     if (records
1000         && records->which == Z_Records_NSD
1001         && records->u.nonSurrogateDiagnostic)
1002     {
1003         z3950_to_srw_diag(odr_en, sru_res,
1004                           records->u.nonSurrogateDiagnostic);
1005         return false;
1006     }
1007     return true;
1008 }
1009
1010 int yf::SRUtoZ3950::Impl::z3950_to_srw_diag(
1011     mp::odr &odr_en,
1012     Z_SRW_searchRetrieveResponse *sru_res,
1013     Z_DefaultDiagFormat *ddf) const
1014 {
1015     int bib1_code = *ddf->condition;
1016     sru_res->num_diagnostics = 1;
1017     sru_res->diagnostics = (Z_SRW_diagnostic *)
1018         odr_malloc(odr_en, sizeof(*sru_res->diagnostics));
1019     yaz_mk_std_diagnostic(odr_en, sru_res->diagnostics,
1020                           yaz_diag_bib1_to_srw(bib1_code),
1021                           ddf->u.v2Addinfo);
1022     return 0;
1023 }
1024
1025 static mp::filter::Base* filter_creator()
1026 {
1027     return new mp::filter::SRUtoZ3950;
1028 }
1029
1030 extern "C" {
1031     struct metaproxy_1_filter_struct metaproxy_1_filter_sru_z3950 = {
1032         0,
1033         "sru_z3950",
1034         filter_creator
1035     };
1036 }
1037
1038
1039 /*
1040  * Local variables:
1041  * c-basic-offset: 4
1042  * c-file-style: "Stroustrup"
1043  * indent-tabs-mode: nil
1044  * End:
1045  * vim: shiftwidth=4 tabstop=8 expandtab
1046  */
1047