1 /* $Id: filter_sru_to_z3950.cpp,v 1.9 2006-09-20 15:08:27 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 bool build_sru_response(mp::Package &package,
80 const Z_SRW_PDU *sru_pdu_res,
82 const char *stylesheet) const;
83 Z_SRW_PDU * decode_sru_request(mp::Package &package,
87 const char *stylesheet) const;
88 bool z3950_build_query(mp::odr &odr_en, Z_Query *z_query,
89 const SRW_query &query,
90 SRW_query_type query_type) const;
91 bool z3950_init_request(mp::Package &package,
93 &database = "Default") const;
94 bool z3950_close_request(mp::Package &package) const;
95 bool z3950_search_request(mp::Package &package,
97 Z_SRW_PDU *sru_pdu_res,
98 Z_SRW_searchRetrieveRequest
100 //const std::string &database,
101 //const std::string &query,
102 //int query_type) const;
103 bool z3950_present_request(mp::Package &package,
105 Z_SRW_PDU *sru_pdu_res,
106 Z_SRW_searchRetrieveRequest
107 const *sr_req) const;
108 //unsigned int start_position,
109 //unsigned int records_requested) const;
110 bool z3950_scan_request(mp::Package &package,
112 Z_SRW_PDU *sru_pdu_res,
114 const *sr_req) const;
115 //const std::string &database,
116 //const std::string &query,
117 //int query_type) const;
122 yf::SRUtoZ3950::SRUtoZ3950() : m_p(new Rep)
126 yf::SRUtoZ3950::~SRUtoZ3950()
127 { // must have a destructor because of boost::scoped_ptr
130 void yf::SRUtoZ3950::configure(const xmlNode *xmlnode)
132 m_p->configure(xmlnode);
135 void yf::SRUtoZ3950::process(mp::Package &package) const
137 m_p->process(package);
140 void yf::SRUtoZ3950::Rep::configure(const xmlNode *xmlnode)
144 void yf::SRUtoZ3950::Rep::process(mp::Package &package) const
146 Z_GDU *zgdu_req = package.request().get();
148 // ignoring all non HTTP_Request packages
149 if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
154 // only working on HTTP_Request packages now
157 // TODO: Z3950 response parsing and translation to SRU package
160 Z_SRW_PDU *sru_pdu_req = 0;
161 mp::odr odr_de(ODR_DECODE);
163 Z_SRW_PDU *sru_pdu_res = 0;
164 mp::odr odr_en(ODR_ENCODE);
168 const char *stylesheet = 0;
170 if (! (sru_pdu_req = decode_sru_request(package, odr_de, soap,
171 charset, stylesheet)))
173 sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
174 std::cout << "TODO: correct error when SRW package could not be"
176 //build_sru_debug_package(package);
177 build_sru_response(package, odr_en, soap,
178 sru_pdu_res, charset, stylesheet);
183 // SRU request package translation to Z3950 package
185 std::cout << *sru_pdu_req << "\n";
187 std::cout << "SRU empty\n";
191 if (sru_pdu_req && sru_pdu_req->which == Z_SRW_explain_request)
193 //Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;
194 sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
195 std::cout << "TODO: implement explain response";
201 if (sru_pdu_req && sru_pdu_req->which == Z_SRW_searchRetrieve_request)
203 Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;
205 sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_searchRetrieve_response);
207 // TODO: checking of unsupported operations and proper diagnostic setup
209 // recordXPath unsupported.
210 //if (sr_req->recordXPath)
211 // yaz_add_srw_diagnostic(odr_decode(),
212 // &diag, &num_diags, 72, 0);
214 // if (sr_req->sort_type != Z_SRW_sort_type_none)
215 // yaz_add_srw_diagnostic(odr_decode(),
216 // &diag, &num_diags, 80, 0);
219 if (z3950_init_request(package))
222 ok = z3950_search_request(package, odr_en,
223 sru_pdu_res, sr_req);
225 if (ok && sru_pdu_res->u.response->numberOfRecords
226 && *(sru_pdu_res->u.response->numberOfRecords))
228 ok = z3950_present_request(package, odr_en,
231 z3950_close_request(package);
237 else if (sru_pdu_req && sru_pdu_req->which == Z_SRW_scan_request)
239 Z_SRW_scanRequest *sr_req = sru_pdu_req->u.scan_request;
241 sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_scan_response);
243 if (z3950_init_request(package))
245 z3950_scan_request(package, odr_en, sru_pdu_res, sr_req);
246 z3950_close_request(package);
251 std::cout << "SRU OPERATION NOT SUPPORTED \n";
252 sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
254 // TODO: make nice diagnostic return package
255 package.session().close();
259 //build_sru_debug_package(package);
260 build_sru_response(package, odr_en, soap,
261 sru_pdu_res, charset, stylesheet);
272 bool yf::SRUtoZ3950::Rep::build_sru_debug_package(mp::Package &package) const
274 Z_GDU *zgdu_req = package.request().get();
275 if (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
277 Z_HTTP_Request* http_req = zgdu_req->u.HTTP_Request;
278 std::string content = debug_http(*http_req);
280 http_response(package, content, http_code);
283 package.session().close();
288 bool yf::SRUtoZ3950::Rep::build_sru_response(mp::Package &package,
291 const Z_SRW_PDU *sru_pdu_res,
293 const char *stylesheet)
297 // SRU request package translation to Z3950 package
299 std::cout << *(const_cast<Z_SRW_PDU *>(sru_pdu_res)) << "\n";
301 std::cout << "SRU empty\n";
304 Z_GDU *zgdu_req = package.request().get();
305 if (zgdu_req && zgdu_req->which == Z_GDU_HTTP_Request)
307 Z_GDU *zgdu_res //= z_get_HTTP_Response(odr_en, 200);
308 = odr_en.create_HTTP_Response(package.session(),
309 zgdu_req->u.HTTP_Request,
311 Z_HTTP_Response * http_res = zgdu_res->u.HTTP_Response;
313 static Z_SOAP_Handler soap_handlers[4] = {
314 {"http://www.loc.gov/zing/srw/", 0,
315 (Z_SOAP_fun) yaz_srw_codec},
316 {"http://www.loc.gov/zing/srw/v1.0/", 0,
317 (Z_SOAP_fun) yaz_srw_codec},
318 {"http://www.loc.gov/zing/srw/update/", 0,
319 (Z_SOAP_fun) yaz_ucp_codec},
323 //if (!stylesheet && assoc->server)
324 //stylesheet = assoc->server->stylesheet;
326 /* empty stylesheet means NO stylesheet */
327 //if (stylesheet && *stylesheet == '\0')
329 //ret = z_soap_codec_enc_xsl(assoc->encode, &soap_package,
330 //&hres->content_buf, &hres->content_len,
331 // soap_handlers, charset, stylesheet);
333 // encoding SRU package
335 soap->u.generic->p = (void*) sru_pdu_res;
337 z_soap_codec_enc_xsl(odr_en, &soap,
338 &http_res->content_buf, &http_res->content_len,
339 soap_handlers, charset, 0);
341 package.response() = zgdu_res;
344 package.session().close();
350 Z_SRW_PDU * yf::SRUtoZ3950::Rep::decode_sru_request(mp::Package &package,
354 const char *stylesheet)
357 Z_GDU *zgdu_req = package.request().get();
358 Z_SRW_PDU *sru_pdu_req = 0;
360 // ignoring all non HTTP_Request packages
361 if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
365 Z_HTTP_Request* http_req = zgdu_req->u.HTTP_Request;
369 Z_SRW_diagnostic *diag = 0;
372 if (0 == yaz_sru_decode(http_req, &sru_pdu_req, &soap,
373 odr_de, &charset, &diag, &num_diags))
377 std::cout << "SRU DECODE DIAGNOSTICS " << num_diags << "\n";
378 // TODO: make nice diagnostic return package
379 //Z_SRW_PDU *srw_pdu_res =
380 // yaz_srw_get(odr(ODR_ENCODE),
381 // Z_SRW_searchRetrieve_response);
382 // Z_SRW_searchRetrieveResponse *srw_res = srw_pdu_res->u.response;
384 // srw_res->diagnostics = diagnostic;
385 // srw_res->num_diagnostics = num_diagnostic;
386 // send_srw_response(srw_pdu_res);
389 // package.session().close();
394 else if (0 == yaz_srw_decode(http_req, &sru_pdu_req, &soap,
399 //std::cout << "SRU DECODING ERROR - SHOULD NEVER HAPPEN\n";
400 package.session().close();
407 yf::SRUtoZ3950::Rep::z3950_init_request(mp::Package &package,
408 const std::string &database) const
410 // prepare Z3950 package
412 //Package z3950_package(s, package.origin());
413 Package z3950_package(package.session(), package.origin());
414 z3950_package.copy_filter(package);
416 // set initRequest APDU
417 mp::odr odr_en(ODR_ENCODE);
418 Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
419 Z_InitRequest *init_req = apdu->u.initRequest;
420 //TODO: add user name in apdu
421 //TODO: add user passwd in apdu
422 //init_req->idAuthentication = org_init->idAuthentication;
423 //init_req->implementationId = "IDxyz";
424 //init_req->implementationName = "NAMExyz";
425 //init_req->implementationVersion = "VERSIONxyz";
427 ODR_MASK_SET(init_req->options, Z_Options_search);
428 ODR_MASK_SET(init_req->options, Z_Options_present);
429 ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
430 ODR_MASK_SET(init_req->options, Z_Options_scan);
432 ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1);
433 ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2);
434 ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_3);
436 z3950_package.request() = apdu;
438 // send Z3950 package
439 // std::cout << "z3950_init_request " << *apdu <<"\n";
440 z3950_package.move();
442 // dead Z3950 backend detection
443 if (z3950_package.session().is_closed()){
444 package.session().close();
448 // check successful initResponse
449 Z_GDU *z3950_gdu = z3950_package.response().get();
451 if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
452 && z3950_gdu->u.z3950->which == Z_APDU_initResponse)
459 yf::SRUtoZ3950::Rep::z3950_close_request(mp::Package &package) const
461 // prepare Z3950 package
462 Package z3950_package(package.session(), package.origin());
463 z3950_package.copy_filter(package);
464 z3950_package.session().close();
467 //mp::odr odr_en(ODR_ENCODE);
468 //Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_close);
469 //z3950_package.request() = apdu;
471 z3950_package.move();
473 // check successful close response
474 //Z_GDU *z3950_gdu = z3950_package.response().get();
475 //if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
476 // && z3950_gdu->u.z3950->which == Z_APDU_close)
479 if (z3950_package.session().is_closed()){
480 //package.session().close();
487 yf::SRUtoZ3950::Rep::z3950_search_request(mp::Package &package,
489 Z_SRW_PDU *sru_pdu_res,
490 Z_SRW_searchRetrieveRequest
493 Package z3950_package(package.session(), package.origin());
494 z3950_package.copy_filter(package);
495 //mp::odr odr_en(ODR_ENCODE);
496 Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_searchRequest);
498 //add stuff in z3950 apdu
499 Z_SearchRequest *z_searchRequest = apdu->u.searchRequest;
502 z_searchRequest->num_databaseNames = 1;
503 z_searchRequest->databaseNames = (char**)
504 odr_malloc(odr_en, sizeof(char *));
505 if (sr_req->database)
506 z_searchRequest->databaseNames[0]
507 = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
509 z_searchRequest->databaseNames[0]
510 = odr_strdup(odr_en, "Default");
514 Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
515 z_searchRequest->query = z_query;
517 if (!z3950_build_query(odr_en, z_query,
518 (const SRW_query&)sr_req->query,
521 //send_to_srw_client_error(7, "query");
525 z3950_package.request() = apdu;
526 //std::cout << "z3950_search_request " << *apdu << "\n";
528 z3950_package.move();
530 //TODO: check success condition
532 //int yaz_diag_bib1_to_srw (int bib1_code);
533 //int yaz_diag_srw_to_bib1(int srw_code);
534 //Se kode i src/seshigh.c (srw_bend_search, srw_bend_init).
536 Z_GDU *z3950_gdu = z3950_package.response().get();
537 //std::cout << "z3950_search_request " << *z3950_gdu << "\n";
539 if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
540 && z3950_gdu->u.z3950->which == Z_APDU_searchResponse
541 && z3950_gdu->u.z3950->u.searchResponse->searchStatus)
544 Z_SearchResponse *sr = z3950_gdu->u.z3950->u.searchResponse;
547 // srw'fy number of records
548 sru_pdu_res->u.response->numberOfRecords
549 = (int *) odr_malloc(odr_en, sizeof(int *));
550 *(sru_pdu_res->u.response->numberOfRecords) = *(sr->resultCount);
560 yf::SRUtoZ3950::Rep::z3950_present_request(mp::Package &package,
562 Z_SRW_PDU *sru_pdu_res,
563 Z_SRW_searchRetrieveRequest
572 // no need to work if nobody wants records seen ..
573 if (!(sr_req->maximumRecords) || 0 == *(sr_req->maximumRecords))
576 // creating Z3950 package
577 Package z3950_package(package.session(), package.origin());
578 z3950_package.copy_filter(package);
579 //mp::odr odr_en(ODR_ENCODE);
580 Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_presentRequest);
582 //TODO: add stuff in apdu
583 assert(apdu->u.presentRequest);
585 // z3950'fy start record position
586 if (sr_req->startRecord)
587 *(apdu->u.presentRequest->resultSetStartPoint)
588 = *(sr_req->startRecord);
590 *(apdu->u.presentRequest->resultSetStartPoint) = 1;
592 // z3950'fy number of records requested
593 if (sr_req->maximumRecords)
594 *(apdu->u.presentRequest->numberOfRecordsRequested)
595 = *(sr_req->maximumRecords);
597 // TODO: z3950'fy record schema
598 //if (sr_req->recordSchema)
599 // *(apdu->u.presentRequest->preferredRecordSyntax)
600 // = *(sr_req->recordSchema);
602 z3950_package.request() = apdu;
604 //std::cout << "z3950_present_request " << *apdu << "\n";
605 z3950_package.move();
607 //TODO: check success condition
608 Z_GDU *z3950_gdu = z3950_package.response().get();
609 if (z3950_gdu && z3950_gdu->which == Z_GDU_Z3950
610 && z3950_gdu->u.z3950->which == Z_APDU_presentResponse)
613 //std::cout << "z3950_present_request OK\n";
615 Z_PresentResponse *pr = z3950_gdu->u.z3950->u.presentResponse;
619 // srw'fy number of returned records
620 //sru_pdu_res->u.response->num_records
621 // = *(pr->numberOfRecordsReturned);
622 //= (int *) odr_malloc(odr_en, sizeof(int *));
623 //*(sru_pdu_res->u.response->num_records)
624 // = *(pr->numberOfRecordsReturned);
626 // srw'fy nextRecordPosition
627 sru_pdu_res->u.response->nextRecordPosition
628 = (int *) odr_malloc(odr_en, sizeof(int *));
629 *(sru_pdu_res->u.response->nextRecordPosition)
630 = *(pr->nextResultSetPosition);
639 yf::SRUtoZ3950::Rep::z3950_scan_request(mp::Package &package,
641 Z_SRW_PDU *sru_pdu_res,
642 Z_SRW_scanRequest const *sr_req) const
644 Package z3950_package(package.session(), package.origin());
645 z3950_package.copy_filter(package);
646 //mp::odr odr_en(ODR_ENCODE);
647 Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
649 //TODO: add stuff in apdu
650 Z_ScanRequest *z_scanRequest = apdu->u.scanRequest;
652 // database repackaging
653 z_scanRequest->num_databaseNames = 1;
654 z_scanRequest->databaseNames = (char**)
655 odr_malloc(odr_en, sizeof(char *));
656 if (sr_req->database)
657 z_scanRequest->databaseNames[0]
658 = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
660 z_scanRequest->databaseNames[0]
661 = odr_strdup(odr_en, "Default");
665 // CQL or XCQL scan is not possible in Z3950, flagging a diagnostic
666 if (sr_req->query_type != Z_SRW_query_type_pqf)
668 //send_to_srw_client_error(7, "query");
672 // PQF query repackaging
673 // need to use Z_AttributesPlusTerm structure, not Z_Query
674 // this can be digget out of a
675 // Z_query->type1(Z_RPNQuery)->RPNStructure(Z_RPNStructure)
676 // ->u.simple(Z_Operand)->u.attributesPlusTerm(Z_AttributesPlusTerm )
678 //Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
679 //z_searchRequest->query = z_query;
681 //if (!z3950_build_query(odr_en, z_query,
682 // (const SRW_query&)sr_req->query,
683 // sr_req->query_type))
685 //send_to_srw_client_error(7, "query");
691 z3950_package.request() = apdu;
692 std::cout << "z3950_scan_request " << *apdu << "\n";
694 z3950_package.move();
695 //TODO: check success condition
700 bool yf::SRUtoZ3950::Rep::z3950_build_query(mp::odr &odr_en, Z_Query *z_query,
701 const SRW_query &query,
702 SRW_query_type query_type) const
704 if (query_type == Z_SRW_query_type_cql)
706 Z_External *ext = (Z_External *)
707 odr_malloc(odr_en, sizeof(*ext));
708 ext->direct_reference =
709 odr_getoidbystr(odr_en, "1.2.840.10003.16.2");
710 ext->indirect_reference = 0;
712 ext->which = Z_External_CQL;
713 ext->u.cql = const_cast<char *>(query.cql);
715 z_query->which = Z_Query_type_104;
716 z_query->u.type_104 = ext;
720 if (query_type == Z_SRW_query_type_pqf)
722 Z_RPNQuery *RPNquery;
723 YAZ_PQF_Parser pqf_parser;
725 pqf_parser = yaz_pqf_create ();
727 RPNquery = yaz_pqf_parse (pqf_parser, odr_en, query.pqf);
730 std::cout << "TODO: Handeling of bad PQF\n";
731 std::cout << "TODO: Diagnostic to be send\n";
733 z_query->which = Z_Query_type_1;
734 z_query->u.type_1 = RPNquery;
736 yaz_pqf_destroy(pqf_parser);
744 yf::SRUtoZ3950::Rep::sru_protocol(const Z_HTTP_Request &http_req) const
746 const std::string mime_urlencoded("application/x-www-form-urlencoded");
747 const std::string mime_text_xml("text/xml");
748 const std::string mime_soap_xml("application/soap+xml");
750 const std::string http_method(http_req.method);
751 const std::string http_type
752 = http_header_value(http_req.headers, "Content-Type");
754 if (http_method == "GET")
757 if (http_method == "POST"
758 && http_type == mime_urlencoded)
761 if ( http_method == "POST"
762 && (http_type == mime_text_xml
763 || http_type == mime_soap_xml))
770 yf::SRUtoZ3950::Rep::debug_http(const Z_HTTP_Request &http_req) const
772 std::string message("<html>\n<body>\n<h1>"
773 "Metaproxy SRUtoZ3950 filter"
776 message += "<h3>HTTP Info</h3><br/>\n";
778 message += "<b>Method: </b> " + std::string(http_req.method) + "<br/>\n";
779 message += "<b>Version:</b> " + std::string(http_req.version) + "<br/>\n";
780 message += "<b>Path: </b> " + std::string(http_req.path) + "<br/>\n";
782 message += "<b>Content-Type:</b>"
783 + http_header_value(http_req.headers, "Content-Type")
785 message += "<b>Content-Length:</b>"
786 + http_header_value(http_req.headers, "Content-Length")
790 message += "<h3>Headers</h3><br/>\n";
792 Z_HTTP_Header* header = http_req.headers;
794 message += "<b>Header: </b> <i>"
795 + std::string(header->name) + ":</i> "
796 + std::string(header->value) + "<br/>\n";
797 header = header->next;
800 message += "</body>\n</html>\n";
804 void yf::SRUtoZ3950::Rep::http_response(metaproxy_1::Package &package,
805 const std::string &content,
809 Z_GDU *zgdu_req = package.request().get();
813 = odr.create_HTTP_Response(package.session(),
814 zgdu_req->u.HTTP_Request,
817 zgdu_res->u.HTTP_Response->content_len = content.size();
818 zgdu_res->u.HTTP_Response->content_buf
819 = (char*) odr_malloc(odr, zgdu_res->u.HTTP_Response->content_len);
821 strncpy(zgdu_res->u.HTTP_Response->content_buf,
822 content.c_str(), zgdu_res->u.HTTP_Response->content_len);
824 //z_HTTP_header_add(odr, &hres->headers,
825 // "Content-Type", content_type.c_str());
826 package.response() = zgdu_res;
832 static mp::filter::Base* filter_creator()
834 return new mp::filter::SRUtoZ3950;
838 struct metaproxy_1_filter_struct metaproxy_1_filter_sru_to_z3950 = {
849 * indent-tabs-mode: nil
850 * c-file-style: "stroustrup"
852 * vim: shiftwidth=4 tabstop=8 expandtab