metaproxy 1.22.1
filter_zoom.cpp
Go to the documentation of this file.
1/* This file is part of Metaproxy.
2 Copyright (C) Index Data
3
4Metaproxy is free software; you can redistribute it and/or modify it under
5the terms of the GNU General Public License as published by the Free
6Software Foundation; either version 2, or (at your option) any later
7version.
8
9Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or
11FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18
19#include "config.hpp"
20
21#include <stdlib.h>
22#include <sys/types.h>
23#include "filter_zoom.hpp"
24#include <metaproxy/package.hpp>
25#include <metaproxy/util.hpp>
26#include <metaproxy/xmlutil.hpp>
27#include <yaz/comstack.h>
28#include <yaz/poll.h>
29#include "torus.hpp"
30
31#include <libxslt/xsltutils.h>
32#include <libxslt/transform.h>
33
34#include <boost/thread/mutex.hpp>
35#include <boost/thread/condition.hpp>
36
37#include <yaz/yaz-version.h>
38#include <yaz/tpath.h>
39#include <yaz/srw.h>
40#include <yaz/ccl_xml.h>
41#include <yaz/ccl.h>
42#include <yaz/rpn2cql.h>
43#include <yaz/rpn2solr.h>
44#include <yaz/pquery.h>
45#include <yaz/cql.h>
46#include <yaz/oid_db.h>
47#include <yaz/diagbib1.h>
48#include <yaz/log.h>
49#include <yaz/zgdu.h>
50#include <yaz/querytowrbuf.h>
51#include <yaz/sortspec.h>
52#include <yaz/tokenizer.h>
53#include <yaz/zoom.h>
54#include <yaz/otherinfo.h>
55#include <yaz/match_glob.h>
56
57namespace mp = metaproxy_1;
58namespace yf = mp::filter;
59
60namespace metaproxy_1 {
61 namespace filter {
62 class Zoom::Searchable : boost::noncopyable {
63 public:
64 std::string authentication;
65 std::string authenticationMode;
67 std::string cfAuth;
68 std::string cfProxy;
69 std::string cfSubDB;
70 std::string udb;
71 std::string target;
72 std::string query_encoding;
73 std::string sru;
74 std::string sru_version;
75 std::string request_syntax;
76 std::string element_set;
77 std::string record_encoding;
80 std::string urlRecipe;
81 std::string contentConnector;
82 std::string sortStrategy;
83 std::string extraArgs;
84 std::string rpn2cql_fname;
85 std::string retry_on_failure;
86 std::string no_cproxy;
87 std::map<std::string, std::string> cf_param;
90 CCL_bibset ccl_bibset;
91 std::map<std::string, std::string> sortmap;
92 Searchable(CCL_bibset base);
94 };
95 class Zoom::Backend : boost::noncopyable {
96 friend class Impl;
97 friend class Frontend;
98 mp::wrbuf m_apdu_wrbuf;
99 ZOOM_connection m_connection;
100 ZOOM_resultset m_resultset;
103 xsltStylesheetPtr xsp;
104 std::string cproxy_host;
107 xmlDoc *explain_doc;
108 std::string m_proxy;
109 cql_transform_t cqlt;
110 std::string retry_on_failure;
111 public:
112 Backend();
113 ~Backend();
114 void connect(std::string zurl, int *error, char **addinfo,
115 ODR odr);
116 void search(ZOOM_query q, Odr_int *hits,
117 int *error, char **addinfo, Z_FacetList **fl, ODR odr);
118 void present(Odr_int start, Odr_int number, ZOOM_record *recs,
119 int *error, char **addinfo, ODR odr);
120 void set_option(const char *name, const char *value);
121 void set_option(const char *name, const char *value, size_t l);
122 void set_option(const char *name, std::string value);
123 const char *get_option(const char *name);
124 void get_zoom_error(int *error, char **addinfo, ODR odr);
125 };
126 class Zoom::Frontend : boost::noncopyable {
127 friend class Impl;
131 std::string session_realm;
132 yazpp_1::GDU m_init_gdu;
134 void handle_package(mp::Package &package);
135 void handle_search(mp::Package &package);
136 void auth(mp::Package &package, Z_InitRequest *req,
137 int *error, char **addinfo, ODR odr);
138
139 BackendPtr explain_search(mp::Package &package,
140 std::string &database,
141 int *error,
142 char **addinfo,
143 mp::odr &odr,
144 std::string torus_url,
145 std::string &torus_db,
146 std::string &realm);
147 void handle_present(mp::Package &package);
148 BackendPtr get_backend_from_databases(mp::Package &package,
149 std::string &database,
150 int *error,
151 char **addinfo,
152 mp::odr &odr,
153 int *proxy_step);
154
155 bool create_content_session(mp::Package &package,
156 BackendPtr b,
157 int *error,
158 char **addinfo,
159 ODR odr,
160 std::string authentication,
161 std::string proxy,
162 std::string realm);
163
165 Odr_oid *preferredRecordSyntax,
166 const char *element_set_name,
167 bool &enable_pz2_retrieval,
168 bool &enable_pz2_transform,
169 bool &enable_record_transform,
170 bool &assume_marc8_charset,
171 bool &use_turbomarc);
172
173 Z_Records *get_records(Package &package,
174 Odr_int start,
175 Odr_int number_to_present,
176 int *error,
177 char **addinfo,
178 Odr_int *number_of_records_returned,
179 ODR odr, BackendPtr b,
180 Odr_oid *preferredRecordSyntax,
181 const char *element_set_name);
182 Z_Records *get_explain_records(Package &package,
183 Odr_int start,
184 Odr_int number_to_present,
185 int *error,
186 char **addinfo,
187 Odr_int *number_of_records_returned,
188 ODR odr, BackendPtr b,
189 Odr_oid *preferredRecordSyntax,
190 const char *element_set_name);
191 bool retry(mp::Package &package,
192 mp::odr &odr,
193 BackendPtr b,
194 int &error, char **addinfo,
195 int &proxy_step, int &same_retries,
196 int &proxy_retries);
197 void log_diagnostic(mp::Package &package,
198 int error, const char *addinfo);
199 public:
200 Frontend(Impl *impl);
201 ~Frontend();
202 };
204 friend class Frontend;
205 public:
206 Impl();
207 ~Impl();
208 void process(metaproxy_1::Package & package);
209 void configure(const xmlNode * ptr, bool test_only,
210 const char *path);
211 private:
212 void configure_local_records(const xmlNode * ptr, bool test_only);
213 bool check_proxy(const char *proxy);
214
215
216
217 FrontendPtr get_frontend(mp::Package &package);
218 void release_frontend(mp::Package &package);
219 SearchablePtr parse_torus_record(const xmlNode *ptr);
220 struct cql_node *convert_cql_fields(struct cql_node *cn, ODR odr);
221 std::map<mp::Session, FrontendPtr> m_clients;
222 boost::mutex m_mutex;
223 boost::condition m_cond_session_ready;
225 std::string torus_content_url;
226 std::string torus_auth_url;
227 std::string torus_allow_ip;
228 std::string default_realm;
230 std::map<std::string,std::string> fieldmap;
231 std::string xsldir;
232 std::string file_path;
234 std::string content_tmp_file;
237 CCL_bibset bibset;
238 std::string element_transform;
239 std::string element_raw;
240 std::string element_marcxml;
241 std::string element_passthru;
242 std::string proxy;
243 xsltStylesheetPtr explain_xsp;
244 xsltStylesheetPtr record_xsp;
245 std::map<std::string,SearchablePtr> s_map;
246 std::string zoom_timeout;
248 };
249 }
250}
251
252
253static xmlNode *xml_node_search(xmlNode *ptr, int *num, int m)
254{
255 while (ptr)
256 {
257 if (ptr->type == XML_ELEMENT_NODE &&
258 !strcmp((const char *) ptr->name, "recordData"))
259 {
260 (*num)++;
261 if (m == *num)
262 return ptr;
263 }
264 else // else: we don't want to find nested nodes
265 {
266 xmlNode *ret_node = xml_node_search(ptr->children, num, m);
267 if (ret_node)
268 return ret_node;
269 }
270 ptr = ptr->next;
271 }
272 return 0;
273}
274
275// define Pimpl wrapper forwarding to Impl
276
277yf::Zoom::Zoom() : m_p(new Impl)
278{
279}
280
281yf::Zoom::~Zoom()
282{ // must have a destructor because of boost::scoped_ptr
283}
284
285void yf::Zoom::configure(const xmlNode *xmlnode, bool test_only,
286 const char *path)
287{
288 m_p->configure(xmlnode, test_only, path);
289}
290
291void yf::Zoom::process(mp::Package &package) const
292{
293 m_p->process(package);
294}
295
296
297// define Implementation stuff
298
299yf::Zoom::Backend::Backend()
300{
301 m_connection = ZOOM_connection_create(0);
302 ZOOM_connection_save_apdu_wrbuf(m_connection, m_apdu_wrbuf);
303 m_resultset = 0;
304 xsp = 0;
305 enable_cproxy = true;
306 enable_explain = false;
307 explain_doc = 0;
308 cqlt = 0;
309}
310
311yf::Zoom::Backend::~Backend()
312{
313 if (xsp)
314 xsltFreeStylesheet(xsp);
315 if (explain_doc)
316 xmlFreeDoc(explain_doc);
317 cql_transform_close(cqlt);
318 ZOOM_connection_destroy(m_connection);
319 ZOOM_resultset_destroy(m_resultset);
320}
321
322
323void yf::Zoom::Backend::get_zoom_error(int *error, char **addinfo,
324 ODR odr)
325{
326 const char *msg = 0;
327 const char *zoom_addinfo = 0;
328 const char *dset = 0;
329 int error0 = ZOOM_connection_error_x(m_connection, &msg,
330 &zoom_addinfo, &dset);
331 if (error0)
332 {
333 if (!dset)
334 dset = "Unknown";
335
336 if (!strcmp(dset, "info:srw/diagnostic/1"))
337 *error = yaz_diag_srw_to_bib1(error0);
338 else if (!strcmp(dset, "Bib-1"))
339 *error = error0;
340 else if (!strcmp(dset, "ZOOM"))
341 {
342 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
343 if (error0 == ZOOM_ERROR_INIT)
344 *error = YAZ_BIB1_INIT_AC_AUTHENTICATION_SYSTEM_ERROR;
345 else if (error0 == ZOOM_ERROR_DECODE)
346 {
347 if (zoom_addinfo)
348 {
349 if (strstr(zoom_addinfo, "Authentication") ||
350 strstr(zoom_addinfo, "authentication"))
351 *error = YAZ_BIB1_INIT_AC_AUTHENTICATION_SYSTEM_ERROR;
352 }
353 }
354 }
355 else
356 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
357
358 *addinfo = (char *) odr_malloc(
359 odr, 30 + strlen(dset) + strlen(msg) +
360 (zoom_addinfo ? strlen(zoom_addinfo) : 0));
361 **addinfo = '\0';
362 if (zoom_addinfo && *zoom_addinfo)
363 {
364 strcpy(*addinfo, zoom_addinfo);
365 strcat(*addinfo, " ");
366 }
367 sprintf(*addinfo + strlen(*addinfo), "(%s %d %s)", dset, error0, msg);
368 }
369}
370
371void yf::Zoom::Backend::connect(std::string zurl,
372 int *error, char **addinfo,
373 ODR odr)
374{
375 size_t h = zurl.find_first_of('#');
376 if (h != std::string::npos)
377 zurl.erase(h);
378 ZOOM_connection_connect(m_connection, zurl.length() ? zurl.c_str() : 0, 0);
379 get_zoom_error(error, addinfo, odr);
380
381}
382
383void yf::Zoom::Backend::search(ZOOM_query q, Odr_int *hits,
384 int *error, char **addinfo, Z_FacetList **flp,
385 ODR odr)
386{
387 ZOOM_resultset_destroy(m_resultset);
388 m_resultset = 0;
389 if (*flp)
390 {
391 WRBUF w = wrbuf_alloc();
392 yaz_facet_list_to_wrbuf(w, *flp);
393 set_option("facets", wrbuf_cstr(w));
394 wrbuf_destroy(w);
395 }
396 m_resultset = ZOOM_connection_search(m_connection, q);
397 get_zoom_error(error, addinfo, odr);
398 if (*error == 0)
399 *hits = ZOOM_resultset_size(m_resultset);
400 else
401 *hits = 0;
402 *flp = 0;
403 size_t num_facets = ZOOM_resultset_facets_size(m_resultset);
404 if (num_facets > 0)
405 {
406 size_t i;
407 Z_FacetList *fl = (Z_FacetList *) odr_malloc(odr, sizeof(*fl));
408 fl->elements = (Z_FacetField **)
409 odr_malloc(odr, num_facets * sizeof(*fl->elements));
410 for (i = 0; i < num_facets; i++)
411 {
412 ZOOM_facet_field ff =
413 ZOOM_resultset_get_facet_field_by_index(m_resultset, i);
414 if (!ff)
415 break;
416 Z_AttributeList *al = (Z_AttributeList *)
417 odr_malloc(odr, sizeof(*al));
418 al->num_attributes = 1;
419 al->attributes = (Z_AttributeElement **)
420 odr_malloc(odr, sizeof(*al->attributes));
421 Z_AttributeElement *ae = al->attributes[0] = (Z_AttributeElement *)
422 odr_malloc(odr, sizeof(**al->attributes));
423 ae->attributeSet = 0;
424 ae->attributeType = odr_intdup(odr, 1);
425 ae->which = Z_AttributeValue_complex;
426 ae->value.complex = (Z_ComplexAttribute *)
427 odr_malloc(odr, sizeof(*ae->value.complex));
428 ae->value.complex->num_list = 1;
429 ae->value.complex->list = (Z_StringOrNumeric **)
430 odr_malloc(odr, sizeof(**ae->value.complex->list));
431 ae->value.complex->list[0] = (Z_StringOrNumeric *)
432 odr_malloc(odr, sizeof(**ae->value.complex->list));
433 ae->value.complex->list[0]->which = Z_StringOrNumeric_string;
434 ae->value.complex->list[0]->u.string =
435 odr_strdup(odr, ZOOM_facet_field_name(ff));
436 ae->value.complex->num_semanticAction = 0;
437 ae->value.complex->semanticAction = 0;
438
439 int num_terms = ZOOM_facet_field_term_count(ff);
440 fl->elements[i] = (Z_FacetField *)
441 odr_malloc(odr, sizeof(Z_FacetField));
442 fl->elements[i]->attributes = al;
443 fl->elements[i]->num_terms = num_terms;
444 fl->elements[i]->terms = (Z_FacetTerm **)
445 odr_malloc(odr, num_terms * sizeof(Z_FacetTerm *));
446 int j;
447 for (j = 0; j < num_terms; j++)
448 {
449 int freq;
450 const char *a_term = ZOOM_facet_field_get_term(ff, j, &freq);
451 Z_FacetTerm *ft = (Z_FacetTerm *) odr_malloc(odr, sizeof(*ft));
452 ft->term = z_Term_create(odr, Z_Term_general, a_term,
453 strlen(a_term));
454 ft->count = odr_intdup(odr, freq);
455
456 fl->elements[i]->terms[j] = ft;
457 }
458 }
459 fl->num = i;
460 *flp = fl;
461 }
462}
463
464void yf::Zoom::Backend::present(Odr_int start, Odr_int number,
465 ZOOM_record *recs,
466 int *error, char **addinfo, ODR odr)
467{
468 ZOOM_resultset_records(m_resultset, recs, start, number);
469 get_zoom_error(error, addinfo, odr);
470}
471
472
473void yf::Zoom::Backend::set_option(const char *name, const char *value, size_t l)
474{
475 ZOOM_connection_option_setl(m_connection, name, value, l);
476}
477
478void yf::Zoom::Backend::set_option(const char *name, const char *value)
479{
480 ZOOM_connection_option_set(m_connection, name, value);
481 if (m_resultset)
482 ZOOM_resultset_option_set(m_resultset, name, value);
483}
484
485void yf::Zoom::Backend::set_option(const char *name, std::string value)
486{
487 set_option(name, value.c_str());
488}
489
490const char *yf::Zoom::Backend::get_option(const char *name)
491{
492 return ZOOM_connection_option_get(m_connection, name);
493}
494
495yf::Zoom::Searchable::Searchable(CCL_bibset base)
496{
497 piggyback = true;
498 use_turbomarc = true;
499 sortStrategy = "embed";
500 retry_on_failure = "1"; // existing default (should have been false)
501 ccl_bibset = ccl_qual_dup(base);
502}
503
504yf::Zoom::Searchable::~Searchable()
505{
506 ccl_qual_rm(&ccl_bibset);
507}
508
509yf::Zoom::Frontend::Frontend(Impl *impl) :
510 m_p(impl), m_is_virtual(false), m_in_use(true)
511{
512}
513
514yf::Zoom::Frontend::~Frontend()
515{
516}
517
518yf::Zoom::FrontendPtr yf::Zoom::Impl::get_frontend(mp::Package &package)
519{
520 boost::mutex::scoped_lock lock(m_mutex);
521
522 std::map<mp::Session,yf::Zoom::FrontendPtr>::iterator it;
523
524 while(true)
525 {
526 it = m_clients.find(package.session());
527 if (it == m_clients.end())
528 break;
529
530 if (!it->second->m_in_use)
531 {
532 it->second->m_in_use = true;
533 return it->second;
534 }
535 m_cond_session_ready.wait(lock);
536 }
537 FrontendPtr f(new Frontend(this));
538 m_clients[package.session()] = f;
539 f->m_in_use = true;
540 return f;
541}
542
543void yf::Zoom::Impl::release_frontend(mp::Package &package)
544{
545 boost::mutex::scoped_lock lock(m_mutex);
546 std::map<mp::Session,yf::Zoom::FrontendPtr>::iterator it;
547
548 it = m_clients.find(package.session());
549 if (it != m_clients.end())
550 {
551 if (package.session().is_closed())
552 {
553 m_clients.erase(it);
554 }
555 else
556 {
557 it->second->m_in_use = false;
558 }
559 m_cond_session_ready.notify_all();
560 }
561}
562
563yf::Zoom::Impl::Impl() :
564 apdu_log(false), element_transform("pz2") , element_raw("raw") ,
565 element_marcxml("marcxml"), element_passthru("F"),
566 zoom_timeout("40"), proxy_timeout(1)
567{
568 bibset = ccl_qual_mk();
569
570 explain_xsp = 0;
571 record_xsp = 0;
572 srand((unsigned int) time(0));
573}
574
575yf::Zoom::Impl::~Impl()
576{
577 if (explain_xsp)
578 xsltFreeStylesheet(explain_xsp);
579 ccl_qual_rm(&bibset);
580}
581
582yf::Zoom::SearchablePtr yf::Zoom::Impl::parse_torus_record(const xmlNode *ptr)
583{
584 Zoom::SearchablePtr s(new Searchable(bibset));
585
586 for (ptr = ptr->children; ptr; ptr = ptr->next)
587 {
588 if (ptr->type != XML_ELEMENT_NODE)
589 continue;
590 if (!strcmp((const char *) ptr->name, "layer"))
591 ptr = ptr->children;
592 else if (!strcmp((const char *) ptr->name,
593 "authentication"))
594 {
595 s->authentication = mp::xml::get_text(ptr);
596 }
597 else if (!strcmp((const char *) ptr->name,
598 "authenticationMode"))
599 {
600 s->authenticationMode = mp::xml::get_text(ptr);
601 }
602 else if (!strcmp((const char *) ptr->name,
603 "contentAuthentication"))
604 {
605 s->contentAuthentication = mp::xml::get_text(ptr);
606 }
607 else if (!strcmp((const char *) ptr->name,
608 "cfAuth"))
609 {
610 s->cfAuth = mp::xml::get_text(ptr);
611 }
612 else if (!strcmp((const char *) ptr->name,
613 "cfProxy"))
614 {
615 s->cfProxy = mp::xml::get_text(ptr);
616 }
617 else if (!strcmp((const char *) ptr->name,
618 "cfSubDB"))
619 {
620 s->cfSubDB = mp::xml::get_text(ptr);
621 }
622 else if (!strcmp((const char *) ptr->name,
623 "contentConnector"))
624 {
625 s->contentConnector = mp::xml::get_text(ptr);
626 }
627 else if (!strcmp((const char *) ptr->name, "udb"))
628 {
629 s->udb = mp::xml::get_text(ptr);
630 }
631 else if (!strcmp((const char *) ptr->name, "zurl"))
632 {
633 s->target = mp::xml::get_text(ptr);
634 }
635 else if (!strcmp((const char *) ptr->name, "sru"))
636 {
637 s->sru = mp::xml::get_text(ptr);
638 }
639 else if (!strcmp((const char *) ptr->name, "SRUVersion") ||
640 !strcmp((const char *) ptr->name, "sruVersion"))
641 {
642 s->sru_version = mp::xml::get_text(ptr);
643 }
644 else if (!strcmp((const char *) ptr->name,
645 "queryEncoding"))
646 {
647 s->query_encoding = mp::xml::get_text(ptr);
648 }
649 else if (!strcmp((const char *) ptr->name,
650 "piggyback"))
651 {
652 s->piggyback = mp::xml::get_bool(ptr, true);
653 }
654 else if (!strcmp((const char *) ptr->name,
655 "requestSyntax"))
656 {
657 s->request_syntax = mp::xml::get_text(ptr);
658 }
659 else if (!strcmp((const char *) ptr->name,
660 "elementSet"))
661 {
662 s->element_set = mp::xml::get_text(ptr);
663 }
664 else if (!strcmp((const char *) ptr->name,
665 "recordEncoding"))
666 {
667 s->record_encoding = mp::xml::get_text(ptr);
668 }
669 else if (!strcmp((const char *) ptr->name,
670 "transform"))
671 {
672 s->transform_xsl_fname = mp::xml::get_text(ptr);
673 }
674 else if (!strcmp((const char *) ptr->name,
675 "literalTransform"))
676 {
677 s->transform_xsl_content = mp::xml::get_text(ptr);
678 }
679 else if (!strcmp((const char *) ptr->name,
680 "urlRecipe"))
681 {
682 s->urlRecipe = mp::xml::get_text(ptr);
683 }
684 else if (!strcmp((const char *) ptr->name,
685 "useTurboMarc"))
686 {
687 ; // useTurboMarc is ignored
688 }
689 else if (!strncmp((const char *) ptr->name,
690 "cclmap_", 7))
691 {
692 std::string value = mp::xml::get_text(ptr);
693 if (value.length() > 0)
694 {
695 ccl_qual_fitem(s->ccl_bibset, value.c_str(),
696 (const char *) ptr->name + 7);
697 }
698 }
699 else if (!strncmp((const char *) ptr->name,
700 "sortmap_", 8))
701 {
702 std::string value = mp::xml::get_text(ptr);
703 s->sortmap[(const char *) ptr->name + 8] = value;
704 }
705 else if (!strcmp((const char *) ptr->name,
706 "sortStrategy"))
707 {
708 s->sortStrategy = mp::xml::get_text(ptr);
709 }
710 else if (!strcmp((const char *) ptr->name,
711 "extraArgs"))
712 {
713 s->extraArgs = mp::xml::get_text(ptr);
714 }
715 else if (!strcmp((const char *) ptr->name, "rpn2cql"))
716 s->rpn2cql_fname = mp::xml::get_text(ptr);
717 else if (!strcmp((const char *) ptr->name,
718 "retryOnFailure"))
719 {
720 s->retry_on_failure = mp::xml::get_text(ptr);
721 }
722 else if (!strcmp((const char *) ptr->name, "noCproxy"))
723 {
724 s->no_cproxy = mp::xml::get_text(ptr);
725 }
726 else if (strlen((const char *) ptr->name) > 3 &&
727 !memcmp((const char *) ptr->name, "cf_", 3))
728 {
729 s->cf_param[(const char *) ptr->name + 3] = mp::xml::get_text(ptr);
730 }
731 }
732 return s;
733}
734
735void yf::Zoom::Impl::configure_local_records(const xmlNode *ptr, bool test_only)
736{
737 while (ptr && ptr->type != XML_ELEMENT_NODE)
738 ptr = ptr->next;
739
740 if (ptr)
741 {
742 if (!strcmp((const char *) ptr->name, "records"))
743 {
744 for (ptr = ptr->children; ptr; ptr = ptr->next)
745 {
746 if (ptr->type != XML_ELEMENT_NODE)
747 continue;
748 if (!strcmp((const char *) ptr->name, "record"))
749 {
750 SearchablePtr s = parse_torus_record(ptr);
751 if (s)
752 {
753 std::string udb = s->udb;
754 if (udb.length())
755 s_map[s->udb] = s;
756 else
757 {
758 throw mp::filter::FilterException
759 ("No udb for local torus record");
760 }
761 }
762 }
763 else
764 {
765 throw mp::filter::FilterException
766 ("Bad element "
767 + std::string((const char *) ptr->name)
768 + " in zoom filter inside element "
769 "<torus><records>");
770 }
771 }
772 }
773 else
774 {
775 throw mp::filter::FilterException
776 ("Bad element "
777 + std::string((const char *) ptr->name)
778 + " in zoom filter inside element <torus>");
779 }
780 }
781}
782
783void yf::Zoom::Impl::configure(const xmlNode *ptr, bool test_only,
784 const char *path)
785{
786 std::string explain_xslt_fname;
787 std::string record_xslt_fname;
788
789 if (path && *path)
790 {
791 file_path = path;
792 }
793 for (ptr = ptr->children; ptr; ptr = ptr->next)
794 {
795 if (ptr->type != XML_ELEMENT_NODE)
796 continue;
797 else if (!strcmp((const char *) ptr->name, "torus"))
798 {
799 const struct _xmlAttr *attr;
800 for (attr = ptr->properties; attr; attr = attr->next)
801 {
802 if (!strcmp((const char *) attr->name, "url"))
803 torus_searchable_url = mp::xml::get_text(attr->children);
804 else if (!strcmp((const char *) attr->name, "content_url"))
805 torus_content_url = mp::xml::get_text(attr->children);
806 else if (!strcmp((const char *) attr->name, "auth_url"))
807 torus_auth_url = mp::xml::get_text(attr->children);
808 else if (!strcmp((const char *) attr->name, "allow_ip"))
809 torus_allow_ip = mp::xml::get_text(attr->children);
810 else if (!strcmp((const char *) attr->name, "realm"))
811 default_realm = mp::xml::get_text(attr->children);
812 else if (!strcmp((const char *) attr->name, "auth_hostname"))
813 torus_auth_hostname = mp::xml::get_text(attr->children);
814 else if (!strcmp((const char *) attr->name, "xsldir"))
815 xsldir = mp::xml::get_text(attr->children);
816 else if (!strcmp((const char *) attr->name, "element_transform"))
817 element_transform = mp::xml::get_text(attr->children);
818 else if (!strcmp((const char *) attr->name, "element_raw"))
819 element_raw = mp::xml::get_text(attr->children);
820 else if (!strcmp((const char *) attr->name, "element_marcxml"))
821 element_marcxml = mp::xml::get_text(attr->children);
822 else if (!strcmp((const char *) attr->name, "element_passthru"))
823 element_passthru = mp::xml::get_text(attr->children);
824 else if (!strcmp((const char *) attr->name, "proxy"))
825 proxy = mp::xml::get_text(attr->children);
826 else if (!strcmp((const char *) attr->name, "explain_xsl"))
827 explain_xslt_fname = mp::xml::get_text(attr->children);
828 else if (!strcmp((const char *) attr->name, "record_xsl"))
829 record_xslt_fname = mp::xml::get_text(attr->children);
830 else
831 throw mp::filter::FilterException(
832 "Bad attribute " + std::string((const char *)
833 attr->name));
834 }
835 // If content_url is not given, use value of searchable, to
836 // ensure backwards compatibility
837 if (!torus_content_url.length())
838 torus_content_url = torus_searchable_url;
839 configure_local_records(ptr->children, test_only);
840 }
841 else if (!strcmp((const char *) ptr->name, "cclmap"))
842 {
843 const char *addinfo = 0;
844 ccl_xml_config(bibset, ptr, &addinfo);
845 }
846 else if (!strcmp((const char *) ptr->name, "fieldmap"))
847 {
848 const struct _xmlAttr *attr;
849 std::string ccl_field;
850 std::string cql_field;
851 for (attr = ptr->properties; attr; attr = attr->next)
852 {
853 if (!strcmp((const char *) attr->name, "ccl"))
854 ccl_field = mp::xml::get_text(attr->children);
855 else if (!strcmp((const char *) attr->name, "cql"))
856 cql_field = mp::xml::get_text(attr->children);
857 else
858 throw mp::filter::FilterException(
859 "Bad attribute " + std::string((const char *)
860 attr->name));
861 }
862 if (cql_field.length())
863 fieldmap[cql_field] = ccl_field;
864 }
865 else if (!strcmp((const char *) ptr->name, "contentProxy"))
866 {
867 const struct _xmlAttr *attr;
868 for (attr = ptr->properties; attr; attr = attr->next)
869 {
870 if (!strcmp((const char *) attr->name, "server"))
871 {
872 yaz_log(YLOG_WARN,
873 "contentProxy's server attribute is deprecated");
874 yaz_log(YLOG_LOG,
875 "Specify config_file instead. For example:");
876 yaz_log(YLOG_LOG,
877 " content_file=\"/etc/cf-proxy/cproxy.cfg\"");
878 content_proxy_server = mp::xml::get_text(attr->children);
879 }
880 else if (!strcmp((const char *) attr->name, "tmp_file"))
881 content_tmp_file = mp::xml::get_text(attr->children);
882 else if (!strcmp((const char *) attr->name, "config_file"))
883 content_config_file = mp::xml::get_text(attr->children);
884 else
885 throw mp::filter::FilterException(
886 "Bad attribute " + std::string((const char *)
887 attr->name));
888 }
889 }
890 else if (!strcmp((const char *) ptr->name, "log"))
891 {
892 const struct _xmlAttr *attr;
893 for (attr = ptr->properties; attr; attr = attr->next)
894 {
895 if (!strcmp((const char *) attr->name, "apdu"))
896 apdu_log = mp::xml::get_bool(attr->children, false);
897 else
898 throw mp::filter::FilterException(
899 "Bad attribute " + std::string((const char *)
900 attr->name));
901 }
902 }
903 else if (!strcmp((const char *) ptr->name, "zoom"))
904 {
905 const struct _xmlAttr *attr;
906 for (attr = ptr->properties; attr; attr = attr->next)
907 {
908 if (!strcmp((const char *) attr->name, "timeout"))
909 zoom_timeout = mp::xml::get_text(attr->children);
910 else if (!strcmp((const char *) attr->name, "proxy_timeout"))
911 proxy_timeout = mp::xml::get_int(attr->children, 1);
912 else
913 throw mp::filter::FilterException(
914 "Bad attribute " + std::string((const char *)
915 attr->name));
916 }
917 }
918 else
919 {
920 throw mp::filter::FilterException
921 ("Bad element "
922 + std::string((const char *) ptr->name)
923 + " in zoom filter");
924 }
925 }
926
927 if (explain_xslt_fname.length())
928 {
929 const char *path = 0;
930
931 if (xsldir.length())
932 path = xsldir.c_str();
933 else
934 path = file_path.c_str();
935
936 char fullpath[1024];
937 char *cp = yaz_filepath_resolve(explain_xslt_fname.c_str(),
938 path, 0, fullpath);
939 if (!cp)
940 {
941 throw mp::filter::FilterException
942 ("Cannot read XSLT " + explain_xslt_fname);
943 }
944
945 xmlDoc *xsp_doc = xmlParseFile(cp);
946 if (!xsp_doc)
947 {
948 throw mp::filter::FilterException
949 ("Cannot parse XSLT " + explain_xslt_fname);
950 }
951
952 explain_xsp = xsltParseStylesheetDoc(xsp_doc);
953 if (!explain_xsp)
954 {
955 xmlFreeDoc(xsp_doc);
956 throw mp::filter::FilterException
957 ("Cannot parse XSLT " + explain_xslt_fname);
958
959 }
960 }
961
962 if (record_xslt_fname.length())
963 {
964 const char *path = 0;
965
966 if (xsldir.length())
967 path = xsldir.c_str();
968 else
969 path = file_path.c_str();
970
971 char fullpath[1024];
972 char *cp = yaz_filepath_resolve(record_xslt_fname.c_str(),
973 path, 0, fullpath);
974 if (!cp)
975 {
976 throw mp::filter::FilterException
977 ("Cannot read XSLT " + record_xslt_fname);
978 }
979
980 xmlDoc *xsp_doc = xmlParseFile(cp);
981 if (!xsp_doc)
982 {
983 throw mp::filter::FilterException
984 ("Cannot parse XSLT " + record_xslt_fname);
985 }
986
987 record_xsp = xsltParseStylesheetDoc(xsp_doc);
988 if (!record_xsp)
989 {
990 xmlFreeDoc(xsp_doc);
991 throw mp::filter::FilterException
992 ("Cannot parse XSLT " + record_xslt_fname);
993
994 }
995 }
996}
997
998bool yf::Zoom::Frontend::create_content_session(mp::Package &package,
999 BackendPtr b,
1000 int *error, char **addinfo,
1001 ODR odr,
1002 std::string authentication,
1003 std::string proxy,
1004 std::string realm)
1005{
1006 if (b->sptr->contentConnector.length())
1007 {
1008 std::string proxyhostname;
1009 std::string tmp_file;
1010 bool legacy_format = false;
1011
1012 if (m_p->content_proxy_server.length())
1013 {
1014 proxyhostname = m_p->content_proxy_server;
1015 legacy_format = true;
1016 }
1017
1018 if (m_p->content_tmp_file.length())
1019 tmp_file = m_p->content_tmp_file;
1020
1021 if (m_p->content_config_file.length())
1022 {
1023 FILE *inf = fopen(m_p->content_config_file.c_str(), "r");
1024 if (inf)
1025 {
1026 char buf[1024];
1027 while (fgets(buf, sizeof(buf)-1, inf))
1028 {
1029 char *cp;
1030 cp = strchr(buf, '#');
1031 if (cp)
1032 *cp = '\0';
1033 cp = strchr(buf, '\n');
1034 if (cp)
1035 *cp = '\0';
1036 cp = strchr(buf, ':');
1037 if (cp)
1038 {
1039 char *cp1 = cp;
1040 while (cp1 != buf && cp1[-1] == ' ')
1041 cp1--;
1042 *cp1 = '\0';
1043 cp++;
1044 while (*cp == ' ')
1045 cp++;
1046 if (!strcmp(buf, "proxyhostname"))
1047 proxyhostname = cp;
1048 if (!strcmp(buf, "sessiondir") && *cp)
1049 {
1050 if (cp[strlen(cp)-1] == '/')
1051 cp[strlen(cp)-1] = '\0';
1052 tmp_file = std::string(cp) + std::string("/cf.XXXXXX.p");
1053 }
1054 }
1055 }
1056 fclose(inf);
1057 }
1058 else
1059 {
1060 package.log("zoom", YLOG_WARN|YLOG_ERRNO,
1061 "unable to open content config %s",
1062 m_p->content_config_file.c_str());
1063 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1064 *addinfo = (char *) odr_malloc(odr, 70 + tmp_file.length());
1065 sprintf(*addinfo, "zoom: unable to open content config %s",
1066 m_p->content_config_file.c_str());
1067 return false;
1068 }
1069 }
1070
1071 if (proxyhostname.length() == 0)
1072 {
1073 package.log("zoom", YLOG_WARN, "no proxyhostname");
1074 return true;
1075 }
1076 if (tmp_file.length() == 0)
1077 {
1078 package.log("zoom", YLOG_WARN, "no tmp_file");
1079 return true;
1080 }
1081
1082 char *fname = xstrdup(tmp_file.c_str());
1083 char *xx = strstr(fname, "XXXXXX");
1084 if (!xx)
1085 {
1086 package.log("zoom", YLOG_WARN, "bad tmp_file %s", tmp_file.c_str());
1087 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1088 *addinfo = (char *) odr_malloc(odr, 60 + tmp_file.length());
1089 sprintf(*addinfo, "zoom: bad format of content tmp_file: %s",
1090 tmp_file.c_str());
1091 xfree(fname);
1092 return false;
1093 }
1094 char tmp_char = xx[6];
1095 sprintf(xx, "%06d", ((unsigned) rand()) % 1000000);
1096 if (legacy_format)
1097 b->cproxy_host = std::string(xx) + "." + proxyhostname;
1098 else
1099 b->cproxy_host = proxyhostname + "/" + xx;
1100 xx[6] = tmp_char;
1101
1102 FILE *file = fopen(fname, "w");
1103 if (!file)
1104 {
1105 package.log("zoom", YLOG_WARN|YLOG_ERRNO, "create %s", fname);
1106 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1107 *addinfo = (char *) odr_malloc(odr, 50 + strlen(fname));
1108 sprintf(*addinfo, "zoom: could not create %s", fname);
1109 xfree(fname);
1110 return false;
1111 }
1112 mp::wrbuf w;
1113 wrbuf_puts(w, "#content_proxy\n");
1114 wrbuf_printf(w, "connector: %s\n", b->sptr->contentConnector.c_str());
1115 if (authentication.length())
1116 wrbuf_printf(w, "auth: %s\n", authentication.c_str());
1117 if (proxy.length())
1118 wrbuf_printf(w, "proxy: %s\n", proxy.c_str());
1119 if (realm.length())
1120 wrbuf_printf(w, "realm: %s\n", realm.c_str());
1121
1122 fwrite(w.buf(), 1, w.len(), file);
1123 fclose(file);
1124 package.log("zoom", YLOG_LOG, "content file: %s", fname);
1125 xfree(fname);
1126 }
1127 return true;
1128}
1129
1130yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
1131 mp::Package &package,
1132 std::string &database, int *error, char **addinfo, mp::odr &odr,
1133 int *proxy_step)
1134{
1135 bool connection_reuse = false;
1136 std::string proxy;
1137
1138 std::list<BackendPtr>::const_iterator map_it;
1139 if (m_backend && !m_backend->enable_explain &&
1140 m_backend->m_frontend_database == database)
1141 {
1142 connection_reuse = true;
1143 proxy = m_backend->m_proxy;
1144 }
1145
1146 std::string input_args;
1147 std::string torus_db;
1148 size_t db_arg_pos = database.find(',');
1149 if (db_arg_pos != std::string::npos)
1150 {
1151 torus_db = database.substr(0, db_arg_pos);
1152 input_args = database.substr(db_arg_pos + 1);
1153 }
1154 else
1155 torus_db = database;
1156
1157 std::string content_proxy;
1158 std::string realm = session_realm;
1159 if (realm.length() == 0)
1160 realm = m_p->default_realm;
1161
1162 const char *param_user = 0;
1163 const char *param_password = 0;
1164 const char *param_content_user = 0;
1165 const char *param_content_password = 0;
1166 const char *param_nocproxy = 0;
1167 const char *param_retry = 0;
1168 int no_parms = 0;
1169
1170 char **names;
1171 char **values;
1172 int no_out_args = 0;
1173 if (input_args.length())
1174 no_parms = yaz_uri_to_array(input_args.c_str(),
1175 odr, &names, &values);
1176 // adding 20 because we'll be adding other URL args
1177 const char **out_names = (const char **)
1178 odr_malloc(odr, (20 + no_parms) * sizeof(*out_names));
1179 const char **out_values = (const char **)
1180 odr_malloc(odr, (20 + no_parms) * sizeof(*out_values));
1181
1182 // may be changed if it's a content connection
1183 std::string torus_url = m_p->torus_searchable_url;
1184 int i;
1185 for (i = 0; i < no_parms; i++)
1186 {
1187 const char *name = names[i];
1188 const char *value = values[i];
1189 assert(name);
1190 assert(value);
1191 if (!strcmp(name, "user"))
1192 param_user = value;
1193 else if (!strcmp(name, "password"))
1194 param_password = value;
1195 else if (!strcmp(name, "content-user"))
1196 param_content_user = value;
1197 else if (!strcmp(name, "content-password"))
1198 param_content_password = value;
1199 else if (!strcmp(name, "content-proxy"))
1200 content_proxy = value;
1201 else if (!strcmp(name, "nocproxy"))
1202 param_nocproxy = value;
1203 else if (!strcmp(name, "retry"))
1204 param_retry = value;
1205 else if (!strcmp(name, "proxy"))
1206 {
1207 char **dstr;
1208 int dnum = 0;
1209 nmem_strsplit(((ODR) odr)->mem, ",", value, &dstr, &dnum);
1210 if (connection_reuse)
1211 {
1212 // find the step after our current proxy
1213 int i;
1214 for (i = 0; i < dnum; i++)
1215 if (!strcmp(proxy.c_str(), dstr[i]))
1216 break;
1217 if (i >= dnum - 1)
1218 *proxy_step = 0;
1219 else
1220 *proxy_step = i + 1;
1221 }
1222 else
1223 {
1224 // step is known.. Guess our proxy from it
1225 if (*proxy_step >= dnum)
1226 *proxy_step = 0;
1227 else
1228 {
1229 proxy = dstr[*proxy_step];
1230
1231 (*proxy_step)++;
1232 if (*proxy_step == dnum)
1233 *proxy_step = 0;
1234 }
1235 }
1236 }
1237 else if (!strcmp(name, "cproxysession"))
1238 {
1239 out_names[no_out_args] = name;
1240 out_values[no_out_args++] = value;
1241 torus_url = m_p->torus_content_url;
1242 }
1243 else if (!strcmp(name, "realm") && session_realm.length() == 0)
1244 realm = value;
1245 else if (!strcmp(name, "torus_url") && session_realm.length() == 0)
1246 torus_url = value;
1247 else if (name[0] == 'x' && name[1] == '-')
1248 {
1249 out_names[no_out_args] = name;
1250 out_values[no_out_args++] = value;
1251 }
1252 else
1253 {
1254 BackendPtr notfound;
1255 char *msg = (char*) odr_malloc(odr, strlen(name) + 30);
1256 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1257 sprintf(msg, "zoom: bad database argument: %s", name);
1258 *addinfo = msg;
1259 return notfound;
1260 }
1261 }
1262 if (proxy.length())
1263 package.log("zoom", YLOG_LOG, "proxy: %s", proxy.c_str());
1264
1265 if (connection_reuse)
1266 {
1267 m_backend->connect("", error, addinfo, odr);
1268 return m_backend;
1269 }
1270
1271 if (torus_db.compare("IR-Explain---1") == 0)
1272 return explain_search(package, database, error, addinfo, odr, torus_url,
1273 torus_db, realm);
1274
1275 SearchablePtr sptr;
1276
1277 std::map<std::string,SearchablePtr>::iterator it;
1278 it = m_p->s_map.find(torus_db);
1279 if (it != m_p->s_map.end())
1280 sptr = it->second;
1281 else if (torus_url.length() > 0)
1282 {
1283 std::string torus_addinfo;
1284 std::string torus_query = "udb==" + torus_db;
1285 xmlDoc *doc = mp::get_searchable(package,torus_url, torus_db,
1286 torus_query,
1287 realm, m_p->proxy,
1288 torus_addinfo);
1289 if (!doc)
1290 {
1291 *error = YAZ_BIB1_UNSPECIFIED_ERROR;
1292 if (torus_addinfo.length())
1293 *addinfo = odr_strdup(odr, torus_addinfo.c_str());
1294 BackendPtr b;
1295 return b;
1296 }
1297 const xmlNode *ptr = xmlDocGetRootElement(doc);
1298 if (ptr && ptr->type == XML_ELEMENT_NODE)
1299 {
1300 if (!strcmp((const char *) ptr->name, "record"))
1301 {
1302 sptr = m_p->parse_torus_record(ptr);
1303 }
1304 else if (!strcmp((const char *) ptr->name, "records"))
1305 {
1306 for (ptr = ptr->children; ptr; ptr = ptr->next)
1307 {
1308 if (ptr->type == XML_ELEMENT_NODE
1309 && !strcmp((const char *) ptr->name, "record"))
1310 {
1311 if (sptr)
1312 {
1313 *error = YAZ_BIB1_UNSPECIFIED_ERROR;
1314 *addinfo = (char*)
1315 odr_malloc(odr, 40 + torus_db.length());
1316 sprintf(*addinfo, "multiple records for udb=%s",
1317 database.c_str());
1318 xmlFreeDoc(doc);
1319 BackendPtr b;
1320 return b;
1321 }
1322 sptr = m_p->parse_torus_record(ptr);
1323 }
1324 }
1325 }
1326 else
1327 {
1328 *error = YAZ_BIB1_UNSPECIFIED_ERROR;
1329 *addinfo = (char*) odr_malloc(
1330 odr, 40 + strlen((const char *) ptr->name));
1331 sprintf(*addinfo, "bad root element for torus: %s", ptr->name);
1332 xmlFreeDoc(doc);
1333 BackendPtr b;
1334 return b;
1335 }
1336 }
1337 xmlFreeDoc(doc);
1338 }
1339
1340 if (!sptr)
1341 {
1342 *error = YAZ_BIB1_DATABASE_DOES_NOT_EXIST;
1343 *addinfo = odr_strdup(odr, torus_db.c_str());
1344 BackendPtr b;
1345 return b;
1346 }
1347
1348 xsltStylesheetPtr xsp = 0;
1349 if (sptr->transform_xsl_content.length())
1350 {
1351 xmlDoc *xsp_doc = xmlParseMemory(sptr->transform_xsl_content.c_str(),
1352 sptr->transform_xsl_content.length());
1353 if (!xsp_doc)
1354 {
1355 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1356 *addinfo = odr_strdup(odr, "zoom: xmlParseMemory failed "
1357 "for literalTransform XSL");
1358 BackendPtr b;
1359 return b;
1360 }
1361 xsp = xsltParseStylesheetDoc(xsp_doc);
1362 if (!xsp)
1363 {
1364 *error = YAZ_BIB1_DATABASE_DOES_NOT_EXIST;
1365 *addinfo =
1366 odr_strdup(odr,"zoom: xsltParseStylesheetDoc failed "
1367 "for literalTransform XSL");
1368 BackendPtr b;
1369 xmlFreeDoc(xsp_doc);
1370 return b;
1371 }
1372 }
1373 else if (sptr->transform_xsl_fname.length())
1374 {
1375 const char *path = 0;
1376
1377 if (m_p->xsldir.length())
1378 path = m_p->xsldir.c_str();
1379 else
1380 path = m_p->file_path.c_str();
1381 std::string fname;
1382
1383 char fullpath[1024];
1384 char *cp = yaz_filepath_resolve(sptr->transform_xsl_fname.c_str(),
1385 path, 0, fullpath);
1386 if (cp)
1387 fname.assign(cp);
1388 else
1389 {
1390 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1391 *addinfo = (char *)
1392 odr_malloc(odr, 40 + sptr->transform_xsl_fname.length());
1393 sprintf(*addinfo, "zoom: could not open file %s",
1394 sptr->transform_xsl_fname.c_str());
1395 BackendPtr b;
1396 return b;
1397 }
1398 xmlDoc *xsp_doc = xmlParseFile(fname.c_str());
1399 if (!xsp_doc)
1400 {
1401 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1402 *addinfo = (char *) odr_malloc(odr, 50 + fname.length());
1403 sprintf(*addinfo, "zoom: xmlParseFile failed for file %s",
1404 fname.c_str());
1405 BackendPtr b;
1406 return b;
1407 }
1408 xsp = xsltParseStylesheetDoc(xsp_doc);
1409 if (!xsp)
1410 {
1411 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1412 *addinfo = (char *) odr_malloc(odr, 50 + fname.length());
1413 sprintf(*addinfo, "zoom: xsltParseStylesheetDoc failed "
1414 "for file %s", fname.c_str());
1415 BackendPtr b;
1416 xmlFreeDoc(xsp_doc);
1417 return b;
1418 }
1419 }
1420
1421 cql_transform_t cqlt = 0;
1422 if (sptr->rpn2cql_fname.length())
1423 {
1424 char fullpath[1024];
1425 char *cp = yaz_filepath_resolve(sptr->rpn2cql_fname.c_str(),
1426 m_p->file_path.c_str(), 0, fullpath);
1427 if (cp)
1428 cqlt = cql_transform_open_fname(fullpath);
1429 }
1430 else
1431 cqlt = cql_transform_create();
1432
1433 if (!cqlt)
1434 {
1435 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
1436 *addinfo = odr_strdup(odr, "zoom: missing/invalid cql2rpn file");
1437 BackendPtr b;
1438 xsltFreeStylesheet(xsp);
1439 return b;
1440 }
1441
1442 m_backend.reset();
1443
1444 BackendPtr b(new Backend);
1445
1446 b->cqlt = cqlt;
1447 b->sptr = sptr;
1448 b->xsp = xsp;
1449 b->m_frontend_database = database;
1450
1451 if (!param_nocproxy)
1452 {
1453 if (sptr->no_cproxy.compare("1") == 0)
1454 param_nocproxy = "1";
1455 }
1456 b->enable_cproxy = param_nocproxy ? false : true;
1457
1458 if (param_retry)
1459 b->retry_on_failure = param_retry;
1460 else
1461 b->retry_on_failure = b->sptr->retry_on_failure;
1462
1463 if (sptr->query_encoding.length())
1464 b->set_option("rpnCharset", sptr->query_encoding);
1465
1466 std::string extraArgs = sptr->extraArgs;
1467
1468 b->set_option("timeout", m_p->zoom_timeout.c_str());
1469
1470 if (m_p->apdu_log)
1471 b->set_option("apdulog", "1");
1472
1473 if (sptr->piggyback && sptr->sru.length())
1474 b->set_option("count", "1"); /* some SRU servers INSIST on getting
1475 maximumRecords > 0 */
1476 b->set_option("piggyback", sptr->piggyback ? "1" : "0");
1477
1478 std::string authentication = sptr->authentication;
1479 if (param_user)
1480 {
1481 authentication = std::string(param_user);
1482 if (param_password)
1483 authentication += "/" + std::string(param_password);
1484 }
1485 std::string content_authentication = sptr->contentAuthentication;
1486 if (param_content_user)
1487 {
1488 content_authentication = std::string(param_content_user);
1489 if (param_content_password)
1490 content_authentication += "/" + std::string(param_content_password);
1491 }
1492
1493 if (proxy.length() == 0)
1494 proxy = sptr->cfProxy;
1495 b->m_proxy = proxy;
1496
1497 if (sptr->cfAuth.length())
1498 {
1499 // A CF target
1500 b->set_option("user", sptr->cfAuth);
1501 if (param_user)
1502 {
1503 out_names[no_out_args] = "user";
1504 out_values[no_out_args++] = odr_strdup(odr, param_user);
1505 if (param_password)
1506 {
1507 out_names[no_out_args] = "password";
1508 out_values[no_out_args++] = odr_strdup(odr, param_password);
1509 }
1510 }
1511 else if (authentication.length())
1512 {
1513 size_t found = authentication.find('/');
1514 if (found != std::string::npos)
1515 {
1516 out_names[no_out_args] = "user";
1517 out_values[no_out_args++] =
1518 odr_strdup(odr, authentication.substr(0, found).c_str());
1519
1520 out_names[no_out_args] = "password";
1521 out_values[no_out_args++] =
1522 odr_strdup(odr, authentication.substr(found+1).c_str());
1523 }
1524 else
1525 {
1526 out_names[no_out_args] = "user";
1527 out_values[no_out_args++] =
1528 odr_strdup(odr, authentication.c_str());
1529 }
1530 }
1531 if (proxy.length())
1532 {
1533 out_names[no_out_args] = "proxy";
1534 out_values[no_out_args++] = odr_strdup(odr, proxy.c_str());
1535 }
1536 if (sptr->cfSubDB.length())
1537 {
1538 out_names[no_out_args] = "subdatabase";
1539 out_values[no_out_args++] = odr_strdup(odr, sptr->cfSubDB.c_str());
1540 }
1541 if (!param_nocproxy && b->sptr->contentConnector.length())
1542 param_nocproxy = "1";
1543
1544 if (param_nocproxy)
1545 {
1546 out_names[no_out_args] = "nocproxy";
1547 out_values[no_out_args++] = odr_strdup(odr, param_nocproxy);
1548 }
1549 std::map<std::string,std::string>::const_iterator it;
1550 for (it = sptr->cf_param.begin(); it != sptr->cf_param.end(); it++)
1551 {
1552 int i;
1553 const char *n = it->first.c_str();
1554 for (i = 0; i < no_out_args; i++)
1555 if (!strcmp(n, out_names[i]))
1556 break;
1557 if (i == no_out_args)
1558 {
1559 out_names[no_out_args] = odr_strdup(odr, n);
1560 out_values[no_out_args++] = odr_strdup(odr, it->second.c_str());
1561 }
1562 }
1563 }
1564 else
1565 {
1566 const char *auth = authentication.c_str();
1567 const char *cp1 = strchr(auth, ' ');
1568 if (!cp1 && sptr->sru.length())
1569 cp1 = strchr(auth, '/');
1570 if (!cp1)
1571 {
1572 /* Z39.50 user/password style, or no password for SRU */
1573 b->set_option("user", auth);
1574 }
1575 else
1576 {
1577 /* now consider group as well */
1578 const char *cp2 = strchr(cp1 + 1, ' ');
1579
1580 b->set_option("user", auth, cp1 - auth);
1581 if (!cp2)
1582 b->set_option("password", cp1 + 1);
1583 else
1584 {
1585 b->set_option("group", cp1 + 1, cp2 - cp1 - 1);
1586 b->set_option("password", cp2 + 1);
1587 }
1588 }
1589 if (sptr->authenticationMode.length())
1590 b->set_option("authenticationMode", sptr->authenticationMode);
1591 if (proxy.length())
1592 b->set_option("proxy", proxy);
1593 }
1594 if (extraArgs.length())
1595 b->set_option("extraArgs", extraArgs);
1596
1597 std::string url(sptr->target);
1598 if (sptr->sru.length())
1599 {
1600 b->set_option("sru", sptr->sru);
1601 if (url.find("://") == std::string::npos)
1602 url = "http://" + url;
1603 if (sptr->sru_version.length())
1604 b->set_option("sru_version", sptr->sru_version);
1605 }
1606 if (no_out_args)
1607 {
1608 char *x_args = 0;
1609 out_names[no_out_args] = 0; // terminate list
1610
1611 yaz_array_to_uri(&x_args, odr, (char **) out_names,
1612 (char **) out_values);
1613 url += "," + std::string(x_args);
1614 }
1615 package.log("zoom", YLOG_LOG, "url: %s", url.c_str());
1616 b->connect(url, error, addinfo, odr);
1617 if (*error == 0 && b->enable_cproxy)
1618 create_content_session(package, b, error, addinfo, odr,
1619 content_authentication.length() ?
1620 content_authentication : authentication,
1621 content_proxy.length() ? content_proxy : proxy,
1622 realm);
1623 if (*error == 0)
1624 m_backend = b;
1625 return b;
1626}
1627
1628static bool match_element(const char *actual_element_set_name,
1629 const std::string &config_element)
1630{
1631 if (config_element.length() == 0)
1632 return false;
1633 const char *cp = config_element.c_str();
1634 if (*cp == '?')
1635 {
1636 if (actual_element_set_name == 0)
1637 return true;
1638 cp++;
1639 }
1640 if (actual_element_set_name && !strcmp(cp, actual_element_set_name))
1641 return true;
1642 return false;
1643}
1644
1645void yf::Zoom::Frontend::prepare_elements(BackendPtr b,
1646 Odr_oid *preferredRecordSyntax,
1647 const char *element_set_name,
1648 bool &enable_pz2_retrieval,
1649 bool &enable_pz2_transform,
1650 bool &enable_record_transform,
1651 bool &assume_marc8_charset,
1652 bool &use_turbomarc)
1653{
1654 char oid_name_str[OID_STR_MAX];
1655 const char *syntax_name = 0;
1656
1657 if (preferredRecordSyntax &&
1658 !oid_oidcmp(preferredRecordSyntax, yaz_oid_recsyn_xml))
1659 {
1660 if (match_element(element_set_name, m_p->element_transform))
1661 {
1662 enable_pz2_retrieval = true;
1663 enable_pz2_transform = true;
1664 use_turbomarc = true;
1665 }
1666 else if (match_element(element_set_name, m_p->element_raw))
1667 {
1668 enable_pz2_retrieval = true;
1669 use_turbomarc = true;
1670 }
1671 else if (match_element(element_set_name, m_p->element_marcxml))
1672 {
1673 enable_pz2_retrieval = true;
1674 use_turbomarc = false;
1675 }
1676 else if (m_p->record_xsp)
1677 {
1678 enable_pz2_retrieval = true;
1679 enable_pz2_transform = true;
1680 enable_record_transform = true;
1681 use_turbomarc = true;
1682 }
1683 }
1684
1685 if (enable_pz2_retrieval)
1686 {
1687 std::string configured_request_syntax = b->sptr->request_syntax;
1688 if (configured_request_syntax.length())
1689 {
1690 syntax_name = configured_request_syntax.c_str();
1691 const Odr_oid *syntax_oid =
1692 yaz_string_to_oid(yaz_oid_std(), CLASS_RECSYN, syntax_name);
1693 if (!oid_oidcmp(syntax_oid, yaz_oid_recsyn_usmarc)
1694 || !oid_oidcmp(syntax_oid, yaz_oid_recsyn_opac))
1695 assume_marc8_charset = true;
1696 }
1697 }
1698 else if (preferredRecordSyntax)
1699 syntax_name =
1700 yaz_oid_to_string_buf(preferredRecordSyntax, 0, oid_name_str);
1701
1702 if (b->sptr->sru.length())
1703 syntax_name = "XML";
1704
1705 b->set_option("preferredRecordSyntax", syntax_name);
1706
1707 if (enable_pz2_retrieval)
1708 {
1709 if (element_set_name && !strcmp(element_set_name,
1710 m_p->element_passthru.c_str()))
1711 ;
1712 else
1713 {
1714 element_set_name = 0;
1715 if (b->sptr->element_set.length())
1716 element_set_name = b->sptr->element_set.c_str();
1717 }
1718 }
1719
1720 b->set_option("elementSetName", element_set_name);
1721 if (b->sptr->sru.length() && element_set_name)
1722 b->set_option("schema", element_set_name);
1723}
1724
1725Z_Records *yf::Zoom::Frontend::get_explain_records(
1726 mp::Package &package,
1727 Odr_int start,
1728 Odr_int number_to_present,
1729 int *error,
1730 char **addinfo,
1731 Odr_int *number_of_records_returned,
1732 ODR odr,
1733 BackendPtr b,
1734 Odr_oid *preferredRecordSyntax,
1735 const char *element_set_name)
1736{
1737 Odr_int i;
1738 Z_Records *records = 0;
1739
1740 if (!b->explain_doc)
1741 {
1742 return records;
1743 }
1744 if (number_to_present > 10000)
1745 number_to_present = 10000;
1746
1747 xmlNode *ptr = xmlDocGetRootElement(b->explain_doc);
1748
1749 Z_NamePlusRecordList *npl = (Z_NamePlusRecordList *)
1750 odr_malloc(odr, sizeof(*npl));
1751 npl->records = (Z_NamePlusRecord **)
1752 odr_malloc(odr, number_to_present * sizeof(*npl->records));
1753
1754 for (i = 0; i < number_to_present; i++)
1755 {
1756 int num = 0;
1757 xmlNode *res = xml_node_search(ptr, &num, start + i + 1);
1758 if (!res)
1759 break;
1760 xmlBufferPtr xml_buf = xmlBufferCreate();
1761 xmlNode *tmp_node = xmlCopyNode(res->children, 1);
1762 xmlNodeDump(xml_buf, tmp_node->doc, tmp_node, 0, 0);
1763
1764 Z_NamePlusRecord *npr =
1765 (Z_NamePlusRecord *) odr_malloc(odr, sizeof(*npr));
1766 npr->databaseName = odr_strdup(odr, b->m_frontend_database.c_str());
1767 npr->which = Z_NamePlusRecord_databaseRecord;
1768 npr->u.databaseRecord =
1769 z_ext_record_xml(odr,
1770 (const char *) xml_buf->content, xml_buf->use);
1771 npl->records[i] = npr;
1772 xmlFreeNode(tmp_node);
1773 xmlBufferFree(xml_buf);
1774 }
1775 records = (Z_Records*) odr_malloc(odr, sizeof(*records));
1776 records->which = Z_Records_DBOSD;
1777 records->u.databaseOrSurDiagnostics = npl;
1778
1779 npl->num_records = i;
1780 *number_of_records_returned = i;
1781 return records;
1782}
1783
1784
1785Z_Records *yf::Zoom::Frontend::get_records(mp::Package &package,
1786 Odr_int start,
1787 Odr_int number_to_present,
1788 int *error,
1789 char **addinfo,
1790 Odr_int *number_of_records_returned,
1791 ODR odr,
1792 BackendPtr b,
1793 Odr_oid *preferredRecordSyntax,
1794 const char *element_set_name)
1795{
1796 *number_of_records_returned = 0;
1797 Z_Records *records = 0;
1798 bool enable_pz2_retrieval = false; // whether target profile is used
1799 bool enable_pz2_transform = false; // whether XSLT is used as well
1800 bool assume_marc8_charset = false;
1801 bool enable_record_transform = false;
1802 bool use_turbomarc = false;
1803
1804 prepare_elements(b, preferredRecordSyntax,
1805 element_set_name,
1806 enable_pz2_retrieval,
1807 enable_pz2_transform,
1808 enable_record_transform,
1809 assume_marc8_charset,
1810 use_turbomarc);
1811
1812 package.log("zoom", YLOG_LOG, "pz2_retrieval: %s . pz2_transform: %s",
1813 enable_pz2_retrieval ? "yes" : "no",
1814 enable_pz2_transform ? "yes" : "no");
1815
1816 if (start < 0 || number_to_present <=0)
1817 return records;
1818
1819 if (number_to_present > 10000)
1820 number_to_present = 10000;
1821
1822 ZOOM_record *recs = (ZOOM_record *)
1823 odr_malloc(odr, (size_t) number_to_present * sizeof(*recs));
1824
1825 b->present(start, number_to_present, recs, error, addinfo, odr);
1826
1827 int i = 0;
1828 if (!*error)
1829 {
1830 for (i = 0; i < number_to_present; i++)
1831 {
1832 if (!recs[i])
1833 break;
1834
1835 const char *addinfo;
1836 int sur_error = ZOOM_record_error(recs[i], 0 /* msg */,
1837 &addinfo, 0 /* diagset */);
1838 if (sur_error ==
1839 YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS && addinfo &&
1840 !strcmp(addinfo,
1841 "ZOOM C generated. Present phase and no records"))
1842 break;
1843 }
1844 }
1845 if (i > 0)
1846 { // only return records if no error and at least one record
1847
1848 const char *xsl_parms[3];
1849 mp::wrbuf cproxy_host;
1850
1851 if (b->enable_cproxy && b->cproxy_host.length())
1852 {
1853 wrbuf_puts(cproxy_host, "\"");
1854 wrbuf_puts(cproxy_host, b->cproxy_host.c_str());
1855 wrbuf_puts(cproxy_host, "/\"");
1856
1857 xsl_parms[0] = "cproxyhost";
1858 xsl_parms[1] = wrbuf_cstr(cproxy_host);
1859 xsl_parms[2] = 0;
1860 }
1861 else
1862 {
1863 xsl_parms[0] = 0;
1864 }
1865
1866 char *odr_database = odr_strdup(odr,
1867 b->m_frontend_database.c_str());
1868 Z_NamePlusRecordList *npl = (Z_NamePlusRecordList *)
1869 odr_malloc(odr, sizeof(*npl));
1870 *number_of_records_returned = i;
1871 npl->num_records = i;
1872 npl->records = (Z_NamePlusRecord **)
1873 odr_malloc(odr, i * sizeof(*npl->records));
1874 for (i = 0; i < npl->num_records; i++)
1875 {
1876 Z_NamePlusRecord *npr = 0;
1877 const char *addinfo;
1878
1879 int sur_error = ZOOM_record_error(recs[i], 0 /* msg */,
1880 &addinfo, 0 /* diagset */);
1881
1882 if (sur_error)
1883 {
1884 log_diagnostic(package, sur_error, addinfo);
1885 npr = zget_surrogateDiagRec(odr, odr_database, sur_error,
1886 addinfo);
1887 }
1888 else if (enable_pz2_retrieval)
1889 {
1890 char rec_type_str[100];
1891 const char *record_encoding = 0;
1892
1893 if (b->sptr->record_encoding.length())
1894 record_encoding = b->sptr->record_encoding.c_str();
1895 else if (assume_marc8_charset)
1896 record_encoding = "marc8";
1897
1898 strcpy(rec_type_str, use_turbomarc ? "txml" : "xml");
1899 if (record_encoding)
1900 {
1901 strcat(rec_type_str, "; charset=");
1902 strcat(rec_type_str, record_encoding);
1903 }
1904
1905 package.log("zoom", YLOG_LOG, "Getting record of type %s",
1906 rec_type_str);
1907 int rec_len;
1908 xmlChar *xmlrec_buf = 0;
1909 const char *rec_buf = ZOOM_record_get(recs[i], rec_type_str,
1910 &rec_len);
1911 if (!rec_buf && !npr)
1912 {
1913 std::string addinfo("ZOOM_record_get failed for type ");
1914
1915 int error = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
1916 addinfo += rec_type_str;
1917 log_diagnostic(package, error, addinfo.c_str());
1918 npr = zget_surrogateDiagRec(odr, odr_database,
1919 error, addinfo.c_str());
1920 }
1921 else
1922 {
1923 package.log_write(rec_buf, rec_len);
1924 package.log_write("\r\n", 2);
1925 }
1926
1927 if (rec_buf && b->xsp && enable_pz2_transform)
1928 {
1929 xmlDoc *rec_doc = xmlParseMemory(rec_buf, rec_len);
1930 if (!rec_doc)
1931 {
1932 const char *addinfo = "xml parse failed for record";
1933 int error = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
1934 log_diagnostic(package, error, addinfo);
1935 npr = zget_surrogateDiagRec(
1936 odr, odr_database, error, addinfo);
1937 }
1938 else
1939 {
1940 // first stage XSLT - per target
1941 xsltStylesheetPtr xsp = b->xsp;
1942 xmlDoc *rec_res = xsltApplyStylesheet(xsp, rec_doc,
1943 xsl_parms);
1944 // insert generated-url
1945 if (rec_res)
1946 {
1947 std::string res =
1948 mp::xml::url_recipe_handle(rec_res,
1949 b->sptr->urlRecipe);
1950 if (res.length())
1951 {
1952 xmlNode *ptr = xmlDocGetRootElement(rec_res);
1953 while (ptr && ptr->type != XML_ELEMENT_NODE)
1954 ptr = ptr->next;
1955 xmlNode *c =
1956 xmlNewChild(ptr, 0, BAD_CAST "metadata", 0);
1957 xmlNewProp(c, BAD_CAST "type", BAD_CAST
1958 "generated-url");
1959 xmlNode * t = xmlNewText(BAD_CAST res.c_str());
1960 xmlAddChild(c, t);
1961 }
1962 }
1963 // second stage XSLT - common
1964 if (rec_res && m_p->record_xsp &&
1965 enable_record_transform)
1966 {
1967 xmlDoc *tmp_doc = rec_res;
1968
1969 xsp = m_p->record_xsp;
1970 rec_res = xsltApplyStylesheet(xsp, tmp_doc,
1971 xsl_parms);
1972 xmlFreeDoc(tmp_doc);
1973 }
1974 // get result out of it
1975 if (rec_res)
1976 {
1977 xsltSaveResultToString(&xmlrec_buf, &rec_len,
1978 rec_res, xsp);
1979 rec_buf = (const char *) xmlrec_buf;
1980 package.log_write(rec_buf, rec_len);
1981
1982 xmlFreeDoc(rec_res);
1983 }
1984 if (!rec_buf)
1985 {
1986 std::string addinfo;
1987 int error =
1988 YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
1989
1990 addinfo = "xslt apply failed for "
1991 + b->sptr->transform_xsl_fname;
1992 log_diagnostic(package, error, addinfo.c_str());
1993 npr = zget_surrogateDiagRec(
1994 odr, odr_database, error, addinfo.c_str());
1995 }
1996 xmlFreeDoc(rec_doc);
1997 }
1998 }
1999
2000 if (!npr)
2001 {
2002 if (!rec_buf)
2003 npr = zget_surrogateDiagRec(
2004 odr, odr_database,
2005 YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
2006 rec_type_str);
2007 else
2008 {
2009 npr = (Z_NamePlusRecord *)
2010 odr_malloc(odr, sizeof(*npr));
2011 npr->databaseName = odr_database;
2012 npr->which = Z_NamePlusRecord_databaseRecord;
2013 npr->u.databaseRecord =
2014 z_ext_record_xml(odr, rec_buf, rec_len);
2015 }
2016 }
2017 if (xmlrec_buf)
2018 xmlFree(xmlrec_buf);
2019 }
2020 else
2021 {
2022 Z_External *ext =
2023 (Z_External *) ZOOM_record_get(recs[i], "ext", 0);
2024 if (ext)
2025 {
2026 npr = (Z_NamePlusRecord *) odr_malloc(odr, sizeof(*npr));
2027 npr->databaseName = odr_database;
2028 npr->which = Z_NamePlusRecord_databaseRecord;
2029 npr->u.databaseRecord = ext;
2030 }
2031 else
2032 {
2033 npr = zget_surrogateDiagRec(
2034 odr, odr_database,
2035 YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
2036 "ZOOM_record, type ext");
2037 }
2038 }
2039 npl->records[i] = npr;
2040 }
2041 records = (Z_Records*) odr_malloc(odr, sizeof(*records));
2042 records->which = Z_Records_DBOSD;
2043 records->u.databaseOrSurDiagnostics = npl;
2044 }
2045 return records;
2046}
2047
2048struct cql_node *yf::Zoom::Impl::convert_cql_fields(struct cql_node *cn,
2049 ODR odr)
2050{
2051 struct cql_node *r = 0;
2052 if (!cn)
2053 return 0;
2054 switch (cn->which)
2055 {
2056 case CQL_NODE_ST:
2057 if (cn->u.st.index)
2058 {
2059 std::map<std::string,std::string>::const_iterator it;
2060 it = fieldmap.find(cn->u.st.index);
2061 if (it == fieldmap.end())
2062 return cn;
2063 if (it->second.length())
2064 cn->u.st.index = odr_strdup(odr, it->second.c_str());
2065 else
2066 cn->u.st.index = 0;
2067 }
2068 break;
2069 case CQL_NODE_BOOL:
2070 r = convert_cql_fields(cn->u.boolean.left, odr);
2071 if (!r)
2072 r = convert_cql_fields(cn->u.boolean.right, odr);
2073 break;
2074 case CQL_NODE_SORT:
2075 r = convert_cql_fields(cn->u.sort.search, odr);
2076 break;
2077 }
2078 return r;
2079}
2080
2081void yf::Zoom::Frontend::log_diagnostic(mp::Package &package,
2082 int error, const char *addinfo)
2083{
2084 const char *err_msg = yaz_diag_bib1_str(error);
2085 if (addinfo)
2086 package.log("zoom", YLOG_WARN, "Diagnostic %d %s: %s",
2087 error, err_msg, addinfo);
2088 else
2089 package.log("zoom", YLOG_WARN, "Diagnostic %d %s:",
2090 error, err_msg);
2091}
2092
2093yf::Zoom::BackendPtr yf::Zoom::Frontend::explain_search(mp::Package &package,
2094 std::string &database,
2095 int *error,
2096 char **addinfo,
2097 mp::odr &odr,
2098 std::string torus_url,
2099 std::string &torus_db,
2100 std::string &realm)
2101{
2102 m_backend.reset();
2103
2104 BackendPtr b(new Backend);
2105
2106 b->m_frontend_database = database;
2107 b->enable_explain = true;
2108
2109 Z_GDU *gdu = package.request().get();
2110 Z_APDU *apdu_req = gdu->u.z3950;
2111 Z_SearchRequest *sr = apdu_req->u.searchRequest;
2112 Z_Query *query = sr->query;
2113
2114 if (!m_p->explain_xsp)
2115 {
2116 *error = YAZ_BIB1_UNSPECIFIED_ERROR;
2117 *addinfo =
2118 odr_strdup(odr, "IR-Explain---1 unsupported. "
2119 "Torus explain_xsl not defined");
2120 return m_backend;
2121 }
2122 else if (query->which == Z_Query_type_104 &&
2123 query->u.type_104->which == Z_External_CQL)
2124 {
2125 std::string torus_addinfo;
2126 std::string torus_query(query->u.type_104->u.cql);
2127 xmlDoc *doc = mp::get_searchable(package, torus_url, "",
2128 torus_query,
2129 realm, m_p->proxy,
2130 torus_addinfo);
2131 if (m_p->explain_xsp)
2132 {
2133 xmlDoc *rec_res = xsltApplyStylesheet(m_p->explain_xsp, doc, 0);
2134
2135 xmlFreeDoc(doc);
2136 doc = rec_res;
2137 }
2138 if (!doc)
2139 {
2140 *error = YAZ_BIB1_UNSPECIFIED_ERROR;
2141 if (torus_addinfo.length())
2142 *addinfo = odr_strdup(odr, torus_addinfo.c_str());
2143 }
2144 else
2145 {
2146 xmlNode *ptr = xmlDocGetRootElement(doc);
2147 int hits = 0;
2148
2149 xml_node_search(ptr, &hits, 0);
2150
2151 Z_APDU *apdu_res = odr.create_searchResponse(apdu_req, 0, 0);
2152 apdu_res->u.searchResponse->resultCount = odr_intdup(odr, hits);
2153 package.response() = apdu_res;
2154 m_backend = b;
2155 }
2156 if (b->explain_doc)
2157 xmlFreeDoc(b->explain_doc);
2158 b->explain_doc = doc;
2159 return m_backend;
2160 }
2161 else
2162 {
2163 *error = YAZ_BIB1_QUERY_TYPE_UNSUPP;
2164 *addinfo = odr_strdup(odr, "IR-Explain---1 only supports CQL");
2165 return m_backend;
2166 }
2167}
2168
2169static bool wait_conn(COMSTACK cs, int secs)
2170{
2171 struct yaz_poll_fd pfd;
2172
2173 pfd.input_mask = yaz_poll_none;
2174 pfd.output_mask = yaz_poll_none;
2175 yaz_poll_add(pfd.input_mask, yaz_poll_except);
2176 if (cs->io_pending & CS_WANT_WRITE)
2177 yaz_poll_add(pfd.input_mask, yaz_poll_write);
2178 if (cs->io_pending & CS_WANT_READ)
2179 yaz_poll_add(pfd.input_mask, yaz_poll_read);
2180
2181 pfd.fd = cs_fileno(cs);
2182 pfd.client_data = 0;
2183
2184 int ret = yaz_poll(&pfd, 1, secs, 0);
2185 return ret > 0;
2186}
2187
2188bool yf::Zoom::Impl::check_proxy(const char *proxy)
2189{
2190 COMSTACK conn = 0;
2191 const char *uri = "http://localhost/";
2192 void *add;
2193 mp::odr odr;
2194 bool outcome = false;
2195 conn = cs_create_host_proxy(uri, 0, &add, proxy);
2196
2197 if (!conn)
2198 return false;
2199
2200 Z_GDU *gdu = z_get_HTTP_Request_uri(odr, uri, 0, 1);
2201 gdu->u.HTTP_Request->method = odr_strdup(odr, "GET");
2202
2203 if (z_GDU(odr, &gdu, 0, 0))
2204 {
2205 int len;
2206 char *buf = odr_getbuf(odr, &len, 0);
2207
2208 int ret = cs_connect(conn, add);
2209 if (ret > 0 || (ret == 0 && wait_conn(conn, 1)))
2210 {
2211 while (1)
2212 {
2213 ret = cs_put(conn, buf, len);
2214 if (ret != 1)
2215 break;
2216 if (!wait_conn(conn, proxy_timeout))
2217 break;
2218 }
2219 if (ret == 0)
2220 outcome = true;
2221 }
2222 }
2223 cs_close(conn);
2224 return outcome;
2225}
2226
2227bool yf::Zoom::Frontend::retry(mp::Package &package,
2228 mp::odr &odr,
2229 BackendPtr b,
2230 int &error, char **addinfo,
2231 int &proxy_step, int &same_retries,
2232 int &proxy_retries)
2233{
2234 if (b && b->m_proxy.length() && !m_p->check_proxy(b->m_proxy.c_str()))
2235 {
2236 log_diagnostic(package, error, *addinfo);
2237 package.log("zoom", YLOG_LOG, "proxy %s fails", b->m_proxy.c_str());
2238 m_backend.reset();
2239 if (proxy_step) // there is a failover
2240 {
2241 proxy_retries++;
2242 package.log("zoom", YLOG_WARN, "search failed: trying next proxy");
2243 return true;
2244 }
2245 error = YAZ_BIB1_PROXY_FAILURE;
2246 *addinfo = odr_strdup(odr, b->m_proxy.c_str());
2247 }
2248 else if (b && b->retry_on_failure.compare("0")
2249 && same_retries == 0 && proxy_retries == 0)
2250 {
2251 log_diagnostic(package, error, *addinfo);
2252 same_retries++;
2253 package.log("zoom", YLOG_WARN, "search failed: retry");
2254 m_backend.reset();
2255 proxy_step = 0;
2256 return true;
2257 }
2258 return false;
2259}
2260
2261void yf::Zoom::Frontend::handle_search(mp::Package &package)
2262{
2263 Z_GDU *gdu = package.request().get();
2264 Z_APDU *apdu_req = gdu->u.z3950;
2265 Z_APDU *apdu_res = 0;
2266 mp::odr odr;
2267 Z_SearchRequest *sr = apdu_req->u.searchRequest;
2268 if (sr->num_databaseNames != 1)
2269 {
2270 int error = YAZ_BIB1_TOO_MANY_DATABASES_SPECIFIED;
2271 log_diagnostic(package, error, 0);
2272 apdu_res = odr.create_searchResponse(apdu_req, error, 0);
2273 package.response() = apdu_res;
2274 return;
2275 }
2276 int proxy_step = 0;
2277 int same_retries = 0;
2278 int proxy_retries = 0;
2279
2280next_proxy:
2281
2282 int error = 0;
2283 char *addinfo = 0;
2284 std::string db(sr->databaseNames[0]);
2285
2286 BackendPtr b = get_backend_from_databases(package, db, &error,
2287 &addinfo, odr, &proxy_step);
2288 if (error)
2289 {
2290 if (retry(package, odr, b, error, &addinfo, proxy_step,
2291 same_retries, proxy_retries))
2292 goto next_proxy;
2293 }
2294 if (error)
2295 {
2296 log_diagnostic(package, error, addinfo);
2297 apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
2298 package.response() = apdu_res;
2299 return;
2300 }
2301 if (!b || b->enable_explain)
2302 return;
2303
2304 b->set_option("setname", "default");
2305
2306 bool enable_pz2_retrieval = false;
2307 bool enable_pz2_transform = false;
2308 bool enable_record_transform = false;
2309 bool assume_marc8_charset = false;
2310 bool use_turbomarc = false;
2311 prepare_elements(b, sr->preferredRecordSyntax, 0 /*element_set_name */,
2312 enable_pz2_retrieval,
2313 enable_pz2_transform,
2314 enable_record_transform,
2315 assume_marc8_charset,
2316 use_turbomarc);
2317
2318 Odr_int hits = 0;
2319 Z_Query *query = sr->query;
2320 mp::wrbuf ccl_wrbuf;
2321 mp::wrbuf pqf_wrbuf;
2322 std::string sortkeys;
2323
2324 if (query->which == Z_Query_type_1 || query->which == Z_Query_type_101)
2325 {
2326 // RPN
2327 yaz_rpnquery_to_wrbuf(pqf_wrbuf, query->u.type_1);
2328 }
2329 else if (query->which == Z_Query_type_2)
2330 {
2331 // CCL
2332 wrbuf_write(ccl_wrbuf, (const char *) query->u.type_2->buf,
2333 query->u.type_2->len);
2334 }
2335 else if (query->which == Z_Query_type_104 &&
2336 query->u.type_104->which == Z_External_CQL)
2337 {
2338 // CQL
2339 const char *cql = query->u.type_104->u.cql;
2340 CQL_parser cp = cql_parser_create();
2341 int r = cql_parser_string(cp, cql);
2342 package.log("zoom", YLOG_LOG, "CQL: %s", cql);
2343 if (r)
2344 {
2345 cql_parser_destroy(cp);
2346 error = YAZ_BIB1_MALFORMED_QUERY;
2347 const char *addinfo = "CQL syntax error";
2348 log_diagnostic(package, error, addinfo);
2349 apdu_res =
2350 odr.create_searchResponse(apdu_req, error, addinfo);
2351 package.response() = apdu_res;
2352 return;
2353 }
2354 struct cql_node *cn = cql_parser_result(cp);
2355 struct cql_node *cn_error = m_p->convert_cql_fields(cn, odr);
2356 if (cn_error)
2357 {
2358 // hopefully we are getting a ptr to a index+relation+term node
2359 error = YAZ_BIB1_UNSUPP_USE_ATTRIBUTE;
2360 addinfo = 0;
2361 if (cn_error->which == CQL_NODE_ST)
2362 addinfo = cn_error->u.st.index;
2363
2364 log_diagnostic(package, error, addinfo);
2365 apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
2366 package.response() = apdu_res;
2367 cql_parser_destroy(cp);
2368 return;
2369 }
2370 r = cql_to_ccl(cn, wrbuf_vp_puts, ccl_wrbuf);
2371 if (r)
2372 {
2373 error = YAZ_BIB1_MALFORMED_QUERY;
2374 const char *addinfo = "CQL to CCL conversion error";
2375
2376 log_diagnostic(package, error, addinfo);
2377 apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
2378 package.response() = apdu_res;
2379 cql_parser_destroy(cp);
2380 return;
2381 }
2382
2383 mp::wrbuf sru_sortkeys_wrbuf;
2384 if (cql_sortby_to_sortkeys(cn, wrbuf_vp_puts, sru_sortkeys_wrbuf))
2385 {
2386 error = YAZ_BIB1_ILLEGAL_SORT_RELATION;
2387 const char *addinfo = "CQL to CCL sortby conversion";
2388
2389 log_diagnostic(package, error, addinfo);
2390 apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
2391 package.response() = apdu_res;
2392 cql_parser_destroy(cp);
2393 return;
2394 }
2395 mp::wrbuf sort_spec_wrbuf;
2396 yaz_srw_sortkeys_to_sort_spec(wrbuf_cstr(sru_sortkeys_wrbuf),
2397 sort_spec_wrbuf);
2398 yaz_tok_cfg_t tc = yaz_tok_cfg_create();
2399 yaz_tok_parse_t tp =
2400 yaz_tok_parse_buf(tc, wrbuf_cstr(sort_spec_wrbuf));
2401 yaz_tok_cfg_destroy(tc);
2402
2403 /* go through sortspec and map fields */
2404 int token = yaz_tok_move(tp);
2405 while (token != YAZ_TOK_EOF)
2406 {
2407 if (token == YAZ_TOK_STRING)
2408 {
2409 const char *field = yaz_tok_parse_string(tp);
2410 std::map<std::string,std::string>::iterator it;
2411 it = b->sptr->sortmap.find(field);
2412 if (it != b->sptr->sortmap.end())
2413 sortkeys += it->second;
2414 else
2415 sortkeys += field;
2416 }
2417 sortkeys += " ";
2418 token = yaz_tok_move(tp);
2419 if (token == YAZ_TOK_STRING)
2420 {
2421 sortkeys += yaz_tok_parse_string(tp);
2422 }
2423 if (token != YAZ_TOK_EOF)
2424 {
2425 sortkeys += " ";
2426 token = yaz_tok_move(tp);
2427 }
2428 }
2429 yaz_tok_parse_destroy(tp);
2430 cql_parser_destroy(cp);
2431 }
2432 else
2433 {
2434 error = YAZ_BIB1_QUERY_TYPE_UNSUPP;
2435 const char *addinfo = 0;
2436 log_diagnostic(package, error, addinfo);
2437 apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
2438 package.response() = apdu_res;
2439 return;
2440 }
2441
2442 if (ccl_wrbuf.len())
2443 {
2444 // CCL to PQF
2445 assert(pqf_wrbuf.len() == 0);
2446 int cerror, cpos;
2447 struct ccl_rpn_node *cn;
2448 package.log("zoom", YLOG_LOG, "CCL: %s", wrbuf_cstr(ccl_wrbuf));
2449 cn = ccl_find_str(b->sptr->ccl_bibset, wrbuf_cstr(ccl_wrbuf),
2450 &cerror, &cpos);
2451 if (!cn)
2452 {
2453 char *addinfo = odr_strdup_null(odr, ccl_err_msg(cerror));
2454 error = YAZ_BIB1_MALFORMED_QUERY;
2455
2456 switch (cerror)
2457 {
2458 case CCL_ERR_UNKNOWN_QUAL:
2459 case CCL_ERR_TRUNC_NOT_LEFT:
2460 case CCL_ERR_TRUNC_NOT_RIGHT:
2461 case CCL_ERR_TRUNC_NOT_BOTH:
2462 case CCL_ERR_TRUNC_NOT_EMBED:
2463 case CCL_ERR_TRUNC_NOT_SINGLE:
2464 error = YAZ_BIB1_UNSUPP_SEARCH;
2465 break;
2466 }
2467 log_diagnostic(package, error, addinfo);
2468 apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
2469 package.response() = apdu_res;
2470 return;
2471 }
2472 ccl_pquery(pqf_wrbuf, cn);
2473 package.log("zoom", YLOG_LOG, "RPN: %s", wrbuf_cstr(pqf_wrbuf));
2474 ccl_rpn_delete(cn);
2475 }
2476
2477 assert(pqf_wrbuf.len());
2478
2479 ZOOM_query q = ZOOM_query_create();
2480 ZOOM_query_sortby2(q, b->sptr->sortStrategy.c_str(), sortkeys.c_str());
2481
2482 Z_FacetList *fl = 0;
2483
2484 // Facets for request.. And later for reponse
2485 if (!fl)
2486 fl = yaz_oi_get_facetlist(&sr->otherInfo);
2487 if (!fl)
2488 fl = yaz_oi_get_facetlist(&sr->additionalSearchInfo);
2489
2490 if (b->get_option("sru"))
2491 {
2492 Z_RPNQuery *zquery;
2493 zquery = p_query_rpn(odr, wrbuf_cstr(pqf_wrbuf));
2494 mp::wrbuf wrb_cql;
2495 mp::wrbuf wrb_addinfo;
2496
2497 if (!strcmp(b->get_option("sru"), "solr"))
2498 error = solr_transform_rpn2solr_stream_r(b->cqlt, wrb_addinfo,
2499 wrbuf_vp_puts, wrb_cql,
2500 zquery);
2501 else
2502 error = cql_transform_rpn2cql_stream_r(b->cqlt, wrb_addinfo,
2503 wrbuf_vp_puts, wrb_cql,
2504 zquery);
2505 if (error)
2506 {
2507 log_diagnostic(package, error, wrb_addinfo.c_str_null());
2508 apdu_res = odr.create_searchResponse(apdu_req, error,
2509 wrb_addinfo.c_str_null());
2510 package.response() = apdu_res;
2511 return;
2512 }
2513 ZOOM_query_cql(q, wrb_cql.c_str());
2514 package.log("zoom", YLOG_LOG, "search CQL: %s", wrb_cql.c_str());
2515 b->search(q, &hits, &error, &addinfo, &fl, odr);
2516 ZOOM_query_destroy(q);
2517 }
2518 else
2519 {
2520 ZOOM_query_prefix(q, pqf_wrbuf.c_str());
2521 package.log("zoom", YLOG_LOG, "search PQF: %s", pqf_wrbuf.c_str());
2522 b->search(q, &hits, &error, &addinfo, &fl, odr);
2523 ZOOM_query_destroy(q);
2524 }
2525
2526 if (error)
2527 {
2528 if (retry(package, odr, b, error, &addinfo, proxy_step,
2529 same_retries, proxy_retries))
2530 goto next_proxy;
2531 }
2532
2533 const char *element_set_name = 0;
2534 Odr_int number_to_present = 0;
2535 if (!error)
2536 mp::util::piggyback_sr(sr, hits, number_to_present, &element_set_name);
2537
2538 Odr_int number_of_records_returned = 0;
2539 Z_Records *records = get_records(
2540 package,
2541 0, number_to_present, &error, &addinfo,
2542 &number_of_records_returned, odr, b, sr->preferredRecordSyntax,
2543 element_set_name);
2544 if (error)
2545 log_diagnostic(package, error, addinfo);
2546 apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
2547 if (records)
2548 {
2549 apdu_res->u.searchResponse->records = records;
2550 apdu_res->u.searchResponse->numberOfRecordsReturned =
2551 odr_intdup(odr, number_of_records_returned);
2552 }
2553 apdu_res->u.searchResponse->resultCount = odr_intdup(odr, hits);
2554 if (fl)
2555 yaz_oi_set_facetlist(&apdu_res->u.searchResponse->additionalSearchInfo,
2556 odr, fl);
2557 package.response() = apdu_res;
2558}
2559
2560void yf::Zoom::Frontend::handle_present(mp::Package &package)
2561{
2562 Z_GDU *gdu = package.request().get();
2563 Z_APDU *apdu_req = gdu->u.z3950;
2564 Z_APDU *apdu_res = 0;
2565 Z_PresentRequest *pr = apdu_req->u.presentRequest;
2566
2567 mp::odr odr;
2568 if (!m_backend)
2569 {
2570 package.response() = odr.create_presentResponse(
2571 apdu_req, YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST, 0);
2572 return;
2573 }
2574 const char *element_set_name = 0;
2575 Z_RecordComposition *comp = pr->recordComposition;
2576 if (comp && comp->which != Z_RecordComp_simple)
2577 {
2578 package.response() = odr.create_presentResponse(
2579 apdu_req,
2580 YAZ_BIB1_PRESENT_COMP_SPEC_PARAMETER_UNSUPP, 0);
2581 return;
2582 }
2583 if (comp && comp->u.simple->which == Z_ElementSetNames_generic)
2584 element_set_name = comp->u.simple->u.generic;
2585 Odr_int number_of_records_returned = 0;
2586 int error = 0;
2587 char *addinfo = 0;
2588
2589 if (m_backend->enable_explain)
2590 {
2591 Z_Records *records =
2592 get_explain_records(
2593 package,
2594 *pr->resultSetStartPoint - 1, *pr->numberOfRecordsRequested,
2595 &error, &addinfo, &number_of_records_returned, odr, m_backend,
2596 pr->preferredRecordSyntax, element_set_name);
2597
2598 apdu_res = odr.create_presentResponse(apdu_req, error, addinfo);
2599 if (records)
2600 {
2601 apdu_res->u.presentResponse->records = records;
2602 apdu_res->u.presentResponse->numberOfRecordsReturned =
2603 odr_intdup(odr, number_of_records_returned);
2604 }
2605 package.response() = apdu_res;
2606 }
2607 else
2608 {
2609 Z_Records *records =
2610 get_records(package,
2611 *pr->resultSetStartPoint - 1, *pr->numberOfRecordsRequested,
2612 &error, &addinfo, &number_of_records_returned, odr, m_backend,
2613 pr->preferredRecordSyntax, element_set_name);
2614
2615 apdu_res = odr.create_presentResponse(apdu_req, error, addinfo);
2616 if (records)
2617 {
2618 apdu_res->u.presentResponse->records = records;
2619 apdu_res->u.presentResponse->numberOfRecordsReturned =
2620 odr_intdup(odr, number_of_records_returned);
2621 }
2622 package.response() = apdu_res;
2623 }
2624}
2625
2626void yf::Zoom::Frontend::handle_package(mp::Package &package)
2627{
2628 Z_GDU *gdu = package.request().get();
2629 if (!gdu)
2630 ;
2631 else if (gdu->which == Z_GDU_Z3950)
2632 {
2633 Z_APDU *apdu_req = gdu->u.z3950;
2634
2635 if (m_backend)
2636 wrbuf_rewind(m_backend->m_apdu_wrbuf);
2637 if (apdu_req->which == Z_APDU_initRequest)
2638 {
2639 mp::odr odr;
2640 package.response() = odr.create_close(
2641 apdu_req,
2642 Z_Close_protocolError,
2643 "double init");
2644 }
2645 else if (apdu_req->which == Z_APDU_searchRequest)
2646 {
2647 handle_search(package);
2648 }
2649 else if (apdu_req->which == Z_APDU_presentRequest)
2650 {
2651 handle_present(package);
2652 }
2653 else
2654 {
2655 mp::odr odr;
2656 package.response() = odr.create_close(
2657 apdu_req,
2658 Z_Close_protocolError,
2659 "zoom filter cannot handle this APDU");
2660 package.session().close();
2661 }
2662 if (m_backend)
2663 {
2664 WRBUF w = m_backend->m_apdu_wrbuf;
2665 package.log_write(wrbuf_buf(w), wrbuf_len(w));
2666 }
2667 }
2668 else
2669 {
2670 package.session().close();
2671 }
2672}
2673
2674std::string escape_cql_term(std::string inp)
2675{
2676 std::string res;
2677 size_t l = inp.length();
2678 size_t i;
2679 for (i = 0; i < l; i++)
2680 {
2681 if (strchr("*?^\"", inp[i]))
2682 res += "\\";
2683 res += inp[i];
2684 }
2685 return res;
2686}
2687
2688void yf::Zoom::Frontend::auth(mp::Package &package, Z_InitRequest *req,
2689 int *error, char **addinfo, ODR odr)
2690{
2691 if (m_p->torus_auth_url.length() == 0)
2692 return;
2693
2694 std::string user;
2695 std::string password;
2696 if (req->idAuthentication)
2697 {
2698 Z_IdAuthentication *auth = req->idAuthentication;
2699 switch (auth->which)
2700 {
2701 case Z_IdAuthentication_open:
2702 if (auth->u.open)
2703 {
2704 const char *cp = strchr(auth->u.open, '/');
2705 if (cp)
2706 {
2707 user.assign(auth->u.open, cp - auth->u.open);
2708 password.assign(cp + 1);
2709 }
2710 }
2711 break;
2712 case Z_IdAuthentication_idPass:
2713 if (auth->u.idPass->userId)
2714 user.assign(auth->u.idPass->userId);
2715 if (auth->u.idPass->password)
2716 password.assign(auth->u.idPass->password);
2717 break;
2718 }
2719 }
2720
2721 Z_OtherInformation **oi = &req->otherInfo;
2722 const char *ip_cstr =
2723 yaz_oi_get_string_oid(oi, yaz_oid_userinfo_client_ip, 1, 0);
2724 std::string ip;
2725 if (ip_cstr)
2726 ip = ip_cstr;
2727 else
2728 ip = package.origin().get_address();
2729
2730 yaz_log(YLOG_LOG, "IP=%s", ip.c_str());
2731
2732 {
2733 NMEM nmem = nmem_create();
2734 char **darray;
2735 int i, num;
2736 nmem_strsplit_blank(nmem, m_p->torus_allow_ip.c_str(), &darray, &num);
2737 for (i = 0; i < num; i++)
2738 {
2739 yaz_log(YLOG_LOG, "check against %s+%s", darray[i], ip.c_str());
2740 if (yaz_match_glob(darray[i], ip.c_str()))
2741 break;
2742 }
2743 nmem_destroy(nmem);
2744 if (i < num)
2745 return; /* allow this IP */
2746 }
2747 std::string torus_query;
2748 int failure_code;
2749
2750 if (user.length() && password.length())
2751 {
2752 torus_query = "userName==\"" + escape_cql_term(user) +
2753 "\" and password==\"" + escape_cql_term(password) + "\"";
2754 failure_code = YAZ_BIB1_INIT_AC_BAD_USERID_AND_OR_PASSWORD;
2755 }
2756 else
2757 {
2758 torus_query = "ipRanges encloses/net.ipaddress \"";
2759 torus_query += escape_cql_term(std::string(ip));
2760 torus_query += "\"";
2761
2762 if (m_p->torus_auth_hostname.length())
2763 {
2764 torus_query += " AND hostName == \"";
2765 torus_query += escape_cql_term(m_p->torus_auth_hostname);
2766 torus_query += "\"";
2767 }
2768 failure_code = YAZ_BIB1_INIT_AC_BLOCKED_NETWORK_ADDRESS;
2769 }
2770
2771 std::string dummy_db;
2772 std::string dummy_realm;
2773 std::string torus_addinfo;
2774 xmlDoc *doc = mp::get_searchable(package, m_p->torus_auth_url, dummy_db,
2775 torus_query, dummy_realm, m_p->proxy,
2776 torus_addinfo);
2777 if (!doc)
2778 {
2779 // something fundamental broken in lookup.
2780 *error = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
2781 if (torus_addinfo.length())
2782 *addinfo = odr_strdup(odr, torus_addinfo.c_str());
2783 return;
2784 }
2785 const xmlNode *ptr = xmlDocGetRootElement(doc);
2786 if (ptr && ptr->type == XML_ELEMENT_NODE)
2787 {
2788 if (strcmp((const char *) ptr->name, "records") == 0)
2789 {
2790 ptr = ptr->children;
2791 while (ptr && ptr->type != XML_ELEMENT_NODE)
2792 ptr = ptr->next;
2793 }
2794 if (ptr && strcmp((const char *) ptr->name, "record") == 0)
2795 {
2796 ptr = ptr->children;
2797 while (ptr && ptr->type != XML_ELEMENT_NODE)
2798 ptr = ptr->next;
2799 }
2800 if (ptr && strcmp((const char *) ptr->name, "layer") == 0)
2801 {
2802 ptr = ptr->children;
2803 while (ptr && ptr->type != XML_ELEMENT_NODE)
2804 ptr = ptr->next;
2805 }
2806 while (ptr)
2807 {
2808 if (ptr && ptr->type == XML_ELEMENT_NODE &&
2809 !strcmp((const char *) ptr->name, "identityId"))
2810 break;
2811 ptr = ptr->next;
2812 }
2813 }
2814 if (!ptr)
2815 {
2816 *error = failure_code;
2817 return;
2818 }
2819 session_realm = mp::xml::get_text(ptr);
2820}
2821
2822void yf::Zoom::Impl::process(mp::Package &package)
2823{
2824 FrontendPtr f = get_frontend(package);
2825 Z_GDU *gdu = package.request().get();
2826
2827 if (f->m_is_virtual)
2828 {
2829 f->handle_package(package);
2830 }
2831 else if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
2832 Z_APDU_initRequest)
2833 {
2834 Z_InitRequest *req = gdu->u.z3950->u.initRequest;
2835 f->m_init_gdu = gdu;
2836
2837 mp::odr odr;
2838 Z_APDU *apdu = odr.create_initResponse(gdu->u.z3950, 0, 0);
2839 Z_InitResponse *resp = apdu->u.initResponse;
2840
2841 int i;
2842 static const int masks[] = {
2843 Z_Options_search,
2844 Z_Options_present,
2845 -1
2846 };
2847 for (i = 0; masks[i] != -1; i++)
2848 if (ODR_MASK_GET(req->options, masks[i]))
2849 ODR_MASK_SET(resp->options, masks[i]);
2850
2851 static const int versions[] = {
2852 Z_ProtocolVersion_1,
2853 Z_ProtocolVersion_2,
2854 Z_ProtocolVersion_3,
2855 -1
2856 };
2857 for (i = 0; versions[i] != -1; i++)
2858 if (ODR_MASK_GET(req->protocolVersion, versions[i]))
2859 ODR_MASK_SET(resp->protocolVersion, versions[i]);
2860 else
2861 break;
2862
2863 *resp->preferredMessageSize = *req->preferredMessageSize;
2864 *resp->maximumRecordSize = *req->maximumRecordSize;
2865
2866 int error = 0;
2867 char *addinfo = 0;
2868 f->auth(package, req, &error, &addinfo, odr);
2869 if (error)
2870 {
2871 resp->userInformationField =
2872 zget_init_diagnostics(odr, error, addinfo);
2873 *resp->result = 0;
2874 package.session().close();
2875 }
2876 else
2877 f->m_is_virtual = true;
2878 package.response() = apdu;
2879 }
2880 else
2881 package.move();
2882
2883 release_frontend(package);
2884}
2885
2886
2887static mp::filter::Base* filter_creator()
2888{
2889 return new mp::filter::Zoom;
2890}
2891
2892extern "C" {
2893 struct metaproxy_1_filter_struct metaproxy_1_filter_zoom = {
2894 0,
2895 "zoom",
2897 };
2898}
2899
2900
2901/*
2902 * Local variables:
2903 * c-basic-offset: 4
2904 * c-file-style: "Stroustrup"
2905 * indent-tabs-mode: nil
2906 * End:
2907 * vim: shiftwidth=4 tabstop=8 expandtab
2908 */
2909
void search(ZOOM_query q, Odr_int *hits, int *error, char **addinfo, Z_FacetList **fl, ODR odr)
const char * get_option(const char *name)
void connect(std::string zurl, int *error, char **addinfo, ODR odr)
void set_option(const char *name, const char *value)
void get_zoom_error(int *error, char **addinfo, ODR odr)
void present(Odr_int start, Odr_int number, ZOOM_record *recs, int *error, char **addinfo, ODR odr)
void auth(mp::Package &package, Z_InitRequest *req, int *error, char **addinfo, ODR odr)
void handle_present(mp::Package &package)
Z_Records * get_explain_records(Package &package, Odr_int start, Odr_int number_to_present, int *error, char **addinfo, Odr_int *number_of_records_returned, ODR odr, BackendPtr b, Odr_oid *preferredRecordSyntax, const char *element_set_name)
bool retry(mp::Package &package, mp::odr &odr, BackendPtr b, int &error, char **addinfo, int &proxy_step, int &same_retries, int &proxy_retries)
BackendPtr get_backend_from_databases(mp::Package &package, std::string &database, int *error, char **addinfo, mp::odr &odr, int *proxy_step)
BackendPtr explain_search(mp::Package &package, std::string &database, int *error, char **addinfo, mp::odr &odr, std::string torus_url, std::string &torus_db, std::string &realm)
Z_Records * get_records(Package &package, Odr_int start, Odr_int number_to_present, int *error, char **addinfo, Odr_int *number_of_records_returned, ODR odr, BackendPtr b, Odr_oid *preferredRecordSyntax, const char *element_set_name)
void prepare_elements(BackendPtr b, Odr_oid *preferredRecordSyntax, const char *element_set_name, bool &enable_pz2_retrieval, bool &enable_pz2_transform, bool &enable_record_transform, bool &assume_marc8_charset, bool &use_turbomarc)
void handle_search(mp::Package &package)
bool create_content_session(mp::Package &package, BackendPtr b, int *error, char **addinfo, ODR odr, std::string authentication, std::string proxy, std::string realm)
void handle_package(mp::Package &package)
void log_diagnostic(mp::Package &package, int error, const char *addinfo)
std::map< std::string, std::string > fieldmap
void process(metaproxy_1::Package &package)
void release_frontend(mp::Package &package)
bool check_proxy(const char *proxy)
std::map< std::string, SearchablePtr > s_map
struct cql_node * convert_cql_fields(struct cql_node *cn, ODR odr)
SearchablePtr parse_torus_record(const xmlNode *ptr)
FrontendPtr get_frontend(mp::Package &package)
void configure(const xmlNode *ptr, bool test_only, const char *path)
boost::condition m_cond_session_ready
void configure_local_records(const xmlNode *ptr, bool test_only)
std::map< mp::Session, FrontendPtr > m_clients
std::map< std::string, std::string > cf_param
std::map< std::string, std::string > sortmap
boost::scoped_ptr< Impl > m_p
boost::shared_ptr< Frontend > FrontendPtr
boost::shared_ptr< Backend > BackendPtr
boost::shared_ptr< Searchable > SearchablePtr
static mp::filter::Base * filter_creator()
static bool match_element(const char *actual_element_set_name, const std::string &config_element)
static bool wait_conn(COMSTACK cs, int secs)
std::string escape_cql_term(std::string inp)
struct metaproxy_1_filter_struct metaproxy_1_filter_zoom
static xmlNode * xml_node_search(xmlNode *ptr, int *num, int m)
xmlDoc * get_searchable(Package &package, std::string url_template, const std::string &db, const std::string &query, const std::string &realm, const std::string &proxy, std::string &addinfo)