1 /* $Id: filter_sru_to_z3950.cpp,v 1.8 2006-09-19 13:50:17 marc Exp $
2 Copyright (c) 2005-2006, Index Data.
4 See the LICENSE file for details
11 #include "gduutil.hpp"
12 #include "filter_sru_to_z3950.hpp"
15 #include <yaz/z-core.h>
17 #include <yaz/pquery.h>
19 #include <boost/thread/mutex.hpp>
26 namespace mp = metaproxy_1;
27 namespace yf = mp::filter;
33 std::string to_string(const T& t)
42 std::string http_header_value(const Z_HTTP_Header* header,
43 const std::string name)
45 while (header && header->name
46 && std::string(header->name) != name)
47 header = header->next;
49 if (header && header->name && std::string(header->name) == name
51 return std::string(header->value);
60 namespace metaproxy_1 {
62 class SRUtoZ3950::Rep {
63 //friend class SRUtoZ3950;
65 union SRW_query {char * cql; char * xcql; char * pqf;};
66 typedef const int& SRW_query_type;
68 void configure(const xmlNode *xmlnode);
69 void process(metaproxy_1::Package &package) const;
71 std::string sru_protocol(const Z_HTTP_Request &http_req) const;
72 std::string debug_http(const Z_HTTP_Request &http_req) const;
73 void http_response(mp::Package &package,
74 const std::string &content,
75 int http_code = 200) const;
76 bool build_sru_debug_package(mp::Package &package) const;
77 Z_SRW_PDU * decode_sru_request(mp::Package &package,
78 mp::odr &odr_de) const;
79 bool z3950_build_query(mp::odr &odr_en, Z_Query *z_query,
80 const SRW_query &query,
81 SRW_query_type query_type) const;
82 bool z3950_init_request(mp::Package &package,
84 &database = "Default") const;
85 bool z3950_close_request(mp::Package &package) const;
86 bool z3950_search_request(mp::Package &package,
88 Z_SRW_searchRetrieveRequest
90 //const std::string &database,
91 //const std::string &query,
92 //int query_type) const;
93 bool z3950_present_request(mp::Package &package,
94 unsigned int &records_returned,
95 unsigned int &next_position,
96 Z_SRW_searchRetrieveRequest
98 //unsigned int start_position,
99 //unsigned int records_requested) const;
100 bool z3950_scan_request(mp::Package &package,
102 const *sr_req) const;
103 //const std::string &database,
104 //const std::string &query,
105 //int query_type) const;
110 yf::SRUtoZ3950::SRUtoZ3950() : m_p(new Rep)
114 yf::SRUtoZ3950::~SRUtoZ3950()
115 { // must have a destructor because of boost::scoped_ptr
118 void yf::SRUtoZ3950::configure(const xmlNode *xmlnode)
120 m_p->configure(xmlnode);
123 void yf::SRUtoZ3950::process(mp::Package &package) const
125 m_p->process(package);
128 void yf::SRUtoZ3950::Rep::configure(const xmlNode *xmlnode)
132 void yf::SRUtoZ3950::Rep::process(mp::Package &package) const
134 Z_GDU *zgdu_req = package.request().get();
136 // ignoring all non HTTP_Request packages
137 if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
142 // only working on HTTP_Request packages now
145 // TODO: Z3950 response parsing and translation to SRU package
148 Z_SRW_PDU *sru_pdu_req = 0;
149 mp::odr odr_de(ODR_DECODE);
150 mp::odr odr_en(ODR_ENCODE);
153 if (! (sru_pdu_req = decode_sru_request(package, odr_de)))
155 std::cout << "TODO: correct error when SRW package could not be"
161 // SRU request package translation to Z3950 package
163 std::cout << *sru_pdu_req << "\n";
165 std::cout << "SRU empty\n";
169 if (sru_pdu_req && sru_pdu_req->which == Z_SRW_searchRetrieve_request)
171 Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;
173 // TODO: checking of unsupported operations and proper diagnostic setup
175 // recordXPath unsupported.
176 //if (sr_req->recordXPath)
177 // yaz_add_srw_diagnostic(odr_decode(),
178 // &diag, &num_diags, 72, 0);
180 // if (sr_req->sort_type != Z_SRW_sort_type_none)
181 // yaz_add_srw_diagnostic(odr_decode(),
182 // &diag, &num_diags, 80, 0);
185 if (z3950_init_request(package))
187 unsigned int search_hits = 0;
188 unsigned int records_returned = 0;
189 unsigned int next_position = 0;
191 ok = z3950_search_request(package, search_hits, sr_req);
193 //"dc.title=bacillus",
194 //Z_SRW_query_type_cql);
195 if (ok && search_hits)
196 ok = z3950_present_request(package,
200 z3950_close_request(package);
206 else if (sru_pdu_req && sru_pdu_req->which == Z_SRW_scan_request)
208 Z_SRW_scanRequest *sr_req = sru_pdu_req->u.scan_request;
209 if (z3950_init_request(package))
211 z3950_scan_request(package, sr_req);
212 z3950_close_request(package);
217 std::cout << "SRU OPERATION NOT SUPPORTED \n";
218 // TODO: make nice diagnostic return package
219 package.session().close();
223 build_sru_debug_package(package);
234 bool yf::SRUtoZ3950::Rep::build_sru_debug_package(mp::Package &package) const
236 Z_GDU *zgdu_req = package.request().get();
237 if (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
239 Z_HTTP_Request* http_req = zgdu_req->u.HTTP_Request;
240 std::string content = debug_http(*http_req);
242 http_response(package, content, http_code);
249 Z_SRW_PDU * yf::SRUtoZ3950::Rep::decode_sru_request(mp::Package &package,
250 mp::odr &odr_de) const
252 Z_GDU *zgdu_req = package.request().get();
253 Z_SRW_PDU *sru_pdu_req = 0;
255 // ignoring all non HTTP_Request packages
256 if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
260 Z_HTTP_Request* http_req = zgdu_req->u.HTTP_Request;
264 Z_SOAP *soap_req = 0;
266 Z_SRW_diagnostic *diag = 0;
269 if (0 == yaz_sru_decode(http_req, &sru_pdu_req, &soap_req,
270 odr_de, &charset, &diag, &num_diags))
274 std::cout << "SRU DIAGNOSTICS " << num_diags << "\n";
275 // TODO: make nice diagnostic return package
276 //Z_SRW_PDU *srw_pdu_res =
277 // yaz_srw_get(odr(ODR_ENCODE),
278 // Z_SRW_searchRetrieve_response);
279 // Z_SRW_searchRetrieveResponse *srw_res = srw_pdu_res->u.response;
281 // srw_res->diagnostics = diagnostic;
282 // srw_res->num_diagnostics = num_diagnostic;
283 // send_srw_response(srw_pdu_res);
286 // package.session().close();
291 else if (0 == yaz_srw_decode(http_req, &sru_pdu_req, &soap_req,
296 //std::cout << "SRU DECODING ERROR - SHOULD NEVER HAPPEN\n";
297 package.session().close();
304 yf::SRUtoZ3950::Rep::z3950_init_request(mp::Package &package,
305 const std::string &database) const
307 // prepare Z3950 package
309 //Package z3950_package(s, package.origin());
310 Package z3950_package(package.session(), package.origin());
311 z3950_package.copy_filter(package);
313 // set initRequest APDU
314 mp::odr odr_en(ODR_ENCODE);
315 Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
316 Z_InitRequest *init_req = apdu->u.initRequest;
317 //TODO: add user name in apdu
318 //TODO: add user passwd in apdu
319 //init_req->idAuthentication = org_init->idAuthentication;
320 //init_req->implementationId = "IDxyz";
321 //init_req->implementationName = "NAMExyz";
322 //init_req->implementationVersion = "VERSIONxyz";
324 ODR_MASK_SET(init_req->options, Z_Options_search);
325 ODR_MASK_SET(init_req->options, Z_Options_present);
326 ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
327 ODR_MASK_SET(init_req->options, Z_Options_scan);
329 ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
330 ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
331 ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
333 z3950_package.request() = apdu;
335 // send Z3950 package
336 // std::cout << "z3950_init_request " << *apdu <<"\n";
337 z3950_package.move();
339 // dead Z3950 backend detection
340 if (z3950_package.session().is_closed()){
341 package.session().close();
345 // check successful initResponse
346 Z_GDU *z3950_gdu = z3950_package.response().get();
348 if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
349 && z3950_gdu->u.z3950->which == Z_APDU_initResponse)
356 yf::SRUtoZ3950::Rep::z3950_close_request(mp::Package &package) const
358 // prepare Z3950 package
359 Package z3950_package(package.session(), package.origin());
360 z3950_package.copy_filter(package);
361 z3950_package.session().close();
364 //mp::odr odr_en(ODR_ENCODE);
365 //Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_close);
366 //z3950_package.request() = apdu;
368 z3950_package.move();
370 // check successful close response
371 //Z_GDU *z3950_gdu = z3950_package.response().get();
372 //if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
373 // && z3950_gdu->u.z3950->which == Z_APDU_close)
376 if (z3950_package.session().is_closed()){
377 //package.session().close();
384 yf::SRUtoZ3950::Rep::z3950_search_request(mp::Package &package,
386 Z_SRW_searchRetrieveRequest
391 Package z3950_package(package.session(), package.origin());
392 z3950_package.copy_filter(package);
393 mp::odr odr_en(ODR_ENCODE);
394 Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
396 //TODO: add stuff in apdu
397 Z_SearchRequest *z_searchRequest = apdu->u.searchRequest;
399 // database repackaging
400 z_searchRequest->num_databaseNames = 1;
401 z_searchRequest->databaseNames = (char**)
402 odr_malloc(odr_en, sizeof(char *));
403 if (sr_req->database)
404 z_searchRequest->databaseNames[0]
405 = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
407 z_searchRequest->databaseNames[0]
408 = odr_strdup(odr_en, "Default");
412 Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
413 z_searchRequest->query = z_query;
415 if (!z3950_build_query(odr_en, z_query,
416 (const SRW_query&)sr_req->query,
419 //send_to_srw_client_error(7, "query");
423 z3950_package.request() = apdu;
424 //std::cout << "z3950_search_request " << *apdu << "\n";
426 z3950_package.move();
427 //TODO: check success condition
428 Z_GDU *z3950_gdu = z3950_package.response().get();
429 //std::cout << "z3950_search_request " << *z3950_gdu << "\n";
431 if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
432 && z3950_gdu->u.z3950->which == Z_APDU_searchResponse
433 && z3950_gdu->u.z3950->u.searchResponse->searchStatus)
435 hits = *(z3950_gdu->u.z3950->u.searchResponse->resultCount);
443 yf::SRUtoZ3950::Rep::z3950_present_request(mp::Package &package,
444 unsigned int &records_returned,
445 unsigned int &next_position,
446 Z_SRW_searchRetrieveRequest
448 // unsigned int start_position,
449 // unsigned int records_requested)
456 records_returned = 0;
459 // no need to work if nobody wants records seen ..
460 if (!(sr_req->maximumRecords) || 0 == *(sr_req->maximumRecords))
463 // creating Z3950 package
464 Package z3950_package(package.session(), package.origin());
465 z3950_package.copy_filter(package);
466 mp::odr odr_en(ODR_ENCODE);
467 Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
469 //TODO: add stuff in apdu
470 assert(apdu->u.presentRequest);
472 // z3950'fy start record position
473 if (sr_req->startRecord)
474 *(apdu->u.presentRequest->resultSetStartPoint)
475 = *(sr_req->startRecord);
477 *(apdu->u.presentRequest->resultSetStartPoint) = 1;
479 // z3950'fy number of records requested
480 if (sr_req->maximumRecords)
481 *(apdu->u.presentRequest->numberOfRecordsRequested)
482 = *(sr_req->maximumRecords);
484 // TODO: z3950'fy record schema
485 //if (sr_req->recordSchema)
486 // *(apdu->u.presentRequest->preferredRecordSyntax)
487 // = *(sr_req->recordSchema);
489 z3950_package.request() = apdu;
491 //std::cout << "z3950_present_request " << *apdu << "\n";
492 z3950_package.move();
494 //TODO: check success condition
495 Z_GDU *z3950_gdu = z3950_package.response().get();
496 if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
497 && z3950_gdu->u.z3950->which == Z_APDU_presentResponse)
498 //&& z3950_gdu->u.z3950->u.presentResponse->searchStatus)
500 //std::cout << "z3950_present_request OK\n";
501 records_returned = 0;
503 Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
506 if (pr->numberOfRecordsReturned)
507 records_returned = *(pr->numberOfRecordsReturned);
508 if (pr->nextResultSetPosition)
509 next_position = *(pr->nextResultSetPosition);
518 yf::SRUtoZ3950::Rep::z3950_scan_request(mp::Package &package,
519 Z_SRW_scanRequest const *sr_req) const
520 //const std::string &database,
521 //const std::string &query,
522 //int query_type) const
524 Package z3950_package(package.session(), package.origin());
525 z3950_package.copy_filter(package);
526 mp::odr odr_en(ODR_ENCODE);
527 Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
529 //TODO: add stuff in apdu
530 Z_ScanRequest *z_scanRequest = apdu->u.scanRequest;
532 // database repackaging
533 z_scanRequest->num_databaseNames = 1;
534 z_scanRequest->databaseNames = (char**)
535 odr_malloc(odr_en, sizeof(char *));
536 if (sr_req->database)
537 z_scanRequest->databaseNames[0]
538 = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
540 z_scanRequest->databaseNames[0]
541 = odr_strdup(odr_en, "Default");
545 // CQL or XCQL scan is not possible in Z3950, flagging a diagnostic
546 if (sr_req->query_type != Z_SRW_query_type_pqf)
548 //send_to_srw_client_error(7, "query");
552 // PQF query repackaging
553 // need to use Z_AttributesPlusTerm structure, not Z_Query
554 // this can be digget out of a
555 // Z_query->type1(Z_RPNQuery)->RPNStructure(Z_RPNStructure)
556 // ->u.simple(Z_Operand)->u.attributesPlusTerm(Z_AttributesPlusTerm )
558 //Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
559 //z_searchRequest->query = z_query;
561 //if (!z3950_build_query(odr_en, z_query,
562 // (const SRW_query&)sr_req->query,
563 // sr_req->query_type))
565 //send_to_srw_client_error(7, "query");
571 z3950_package.request() = apdu;
572 std::cout << "z3950_scan_request " << *apdu << "\n";
574 z3950_package.move();
575 //TODO: check success condition
580 bool yf::SRUtoZ3950::Rep::z3950_build_query(mp::odr &odr_en, Z_Query *z_query,
581 const SRW_query &query,
582 SRW_query_type query_type) const
584 if (query_type == Z_SRW_query_type_cql)
586 Z_External *ext = (Z_External *)
587 odr_malloc(odr_en, sizeof(*ext));
588 ext->direct_reference =
589 odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
590 ext->indirect_reference = 0;
592 ext->which = Z_External_CQL;
593 ext->u.cql = const_cast<char *>(query.cql);
595 z_query->which = Z_Query_type_104;
596 z_query->u.type_104 = ext;
600 if (query_type == Z_SRW_query_type_pqf)
602 Z_RPNQuery *RPNquery;
603 YAZ_PQF_Parser pqf_parser;
605 pqf_parser = yaz_pqf_create ();
607 RPNquery = yaz_pqf_parse (pqf_parser, odr_en, query.pqf);
610 std::cout << "TODO: Handeling of bad PQF\n";
611 std::cout << "TODO: Diagnostic to be send\n";
613 z_query->which = Z_Query_type_1;
614 z_query->u.type_1 = RPNquery;
616 yaz_pqf_destroy(pqf_parser);
624 yf::SRUtoZ3950::Rep::sru_protocol(const Z_HTTP_Request &http_req) const
626 const std::string mime_urlencoded("application/x-www-form-urlencoded");
627 const std::string mime_text_xml("text/xml");
628 const std::string mime_soap_xml("application/soap+xml");
630 const std::string http_method(http_req.method);
631 const std::string http_type
632 = http_header_value(http_req.headers, "Content-Type");
634 if (http_method == "GET")
637 if (http_method == "POST"
638 && http_type == mime_urlencoded)
641 if ( http_method == "POST"
642 && (http_type == mime_text_xml
643 || http_type == mime_soap_xml))
650 yf::SRUtoZ3950::Rep::debug_http(const Z_HTTP_Request &http_req) const
652 std::string message("<html>\n<body>\n<h1>"
653 "Metaproxy SRUtoZ3950 filter"
656 message += "<h3>HTTP Info</h3><br/>\n";
658 message += "<b>Method: </b> " + std::string(http_req.method) + "<br/>\n";
659 message += "<b>Version:</b> " + std::string(http_req.version) + "<br/>\n";
660 message += "<b>Path: </b> " + std::string(http_req.path) + "<br/>\n";
662 message += "<b>Content-Type:</b>"
663 + http_header_value(http_req.headers, "Content-Type")
665 message += "<b>Content-Length:</b>"
666 + http_header_value(http_req.headers, "Content-Length")
670 message += "<h3>Headers</h3><br/>\n";
672 Z_HTTP_Header* header = http_req.headers;
674 message += "<b>Header: </b> <i>"
675 + std::string(header->name) + ":</i> "
676 + std::string(header->value) + "<br/>\n";
677 header = header->next;
680 message += "</body>\n</html>\n";
684 void yf::SRUtoZ3950::Rep::http_response(metaproxy_1::Package &package,
685 const std::string &content,
689 Z_GDU *zgdu_req = package.request().get();
693 = odr.create_HTTP_Response(package.session(),
694 zgdu_req->u.HTTP_Request,
697 zgdu_res->u.HTTP_Response->content_len = content.size();
698 zgdu_res->u.HTTP_Response->content_buf
699 = (char*) odr_malloc(odr, zgdu_res->u.HTTP_Response->content_len);
701 strncpy(zgdu_res->u.HTTP_Response->content_buf,
702 content.c_str(), zgdu_res->u.HTTP_Response->content_len);
704 //z_HTTP_header_add(odr, &hres->headers,
705 // "Content-Type", content_type.c_str());
706 package.response() = zgdu_res;
712 static mp::filter::Base* filter_creator()
714 return new mp::filter::SRUtoZ3950;
718 struct metaproxy_1_filter_struct metaproxy_1_filter_sru_to_z3950 = {
729 * indent-tabs-mode: nil
730 * c-file-style: "stroustrup"
732 * vim: shiftwidth=4 tabstop=8 expandtab