Update to use Odr_int
[metaproxy-moved-to-github.git] / src / filter_sru_to_z3950.cpp
index bb39765..7ccebd3 100644 (file)
@@ -1,5 +1,5 @@
 /* This file is part of Metaproxy.
-   Copyright (C) 2005-2008 Index Data
+   Copyright (C) 2005-2009 Index Data
 
 Metaproxy is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
@@ -16,6 +16,9 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
 
+// make std::min actually work on Windows
+#define NOMINMAX 1
+
 #include "config.hpp"
 #include "filter.hpp"
 #include "package.hpp"
@@ -29,13 +32,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #include <yaz/srw.h>
 #include <yaz/pquery.h>
 #include <yaz/oid_db.h>
+#include <yaz/log.h>
 
 #include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
 
 #include <iostream>
 #include <sstream>
 #include <string>
-#include <algorithm>
+/* #include <algorithm> */
 #include <map>
 
 namespace mp = metaproxy_1;
@@ -49,19 +54,27 @@ namespace metaproxy_1 {
             void configure(const xmlNode *xmlnode);
             void process(metaproxy_1::Package &package);
         private:
-            union SRW_query {char * cql; char * xcql; char * pqf;};
-            typedef const int& SRW_query_type;
             std::map<std::string, const xmlNode *> m_database_explain;
-        private:
 
-            bool z3950_build_query(mp::odr &odr_en, Z_Query *z_query, 
-                                   const SRW_query &query, 
-                                   SRW_query_type query_type) const;
+            typedef std::map<std::string, int> ActiveUrlMap;
 
-            bool z3950_init_request(mp::Package &package, 
-                                    mp::odr &odr_en,
-                                    std::string zurl,
-                                    Z_SRW_PDU *sru_pdu_res) const;
+            boost::mutex m_mutex;
+            boost::condition m_cond_url_ready;
+            ActiveUrlMap m_active_urls;
+        private:
+            void sru(metaproxy_1::Package &package, Z_GDU *zgdu_req);
+            bool z3950_build_query(
+                mp::odr &odr_en, Z_Query *z_query, 
+                const Z_SRW_searchRetrieveRequest *req
+                ) const;
+
+            bool z3950_init_request(
+                mp::Package &package, 
+                mp::odr &odr_en,
+                std::string zurl,
+                Z_SRW_PDU *sru_pdu_res,
+                const Z_SRW_PDU *sru_pdu_req
+                ) const;
 
             bool z3950_close_request(mp::Package &package) const;
 
@@ -70,27 +83,28 @@ namespace metaproxy_1 {
                 mp::odr &odr_en,
                 Z_SRW_PDU *sru_pdu_res,
                 Z_SRW_searchRetrieveRequest const *sr_req,
-                std::string zurl) const;
+                std::string zurl
+                ) const;
 
             bool z3950_present_request(
                 mp::Package &package,
                 mp::odr &odr_en,
                 Z_SRW_PDU *sru_pdu_res,
-                Z_SRW_searchRetrieveRequest const *sr_req) const;
-
-            bool z3950_scan_request(mp::Package &package,
-                                    mp::odr &odr_en,
-                                    Z_SRW_PDU *sru_pdu_res,
-                                    Z_SRW_scanRequest 
-                                    const *sr_req) const;
-
-            bool z3950_to_srw_diagnostics_ok(mp::odr &odr_en, 
-                                  Z_SRW_searchRetrieveResponse *srw_res,
-                                  Z_Records *records) const;
+                Z_SRW_searchRetrieveRequest const *sr_req
+                ) const;
+            
+            bool z3950_to_srw_diagnostics_ok(
+                mp::odr &odr_en, 
+                Z_SRW_searchRetrieveResponse *srw_res,
+                Z_Records *records
+                ) const;
+            
+            int z3950_to_srw_diag(
+                mp::odr &odr_en, 
+                Z_SRW_searchRetrieveResponse *srw_res,
+                Z_DefaultDiagFormat *ddf
+                ) const;
 
-            int z3950_to_srw_diag(mp::odr &odr_en, 
-                                  Z_SRW_searchRetrieveResponse *srw_res,
-                                  Z_DefaultDiagFormat *ddf) const;
         };
     }
 }
@@ -117,7 +131,8 @@ void yf::SRUtoZ3950::Impl::configure(const xmlNode *confignode)
 {
     const xmlNode * dbnode;
     
-    for (dbnode = confignode->children; dbnode; dbnode = dbnode->next){
+    for (dbnode = confignode->children; dbnode; dbnode = dbnode->next)
+    {
         if (dbnode->type != XML_ELEMENT_NODE)
             continue;
         
@@ -125,14 +140,16 @@ void yf::SRUtoZ3950::Impl::configure(const xmlNode *confignode)
         mp::xml::check_element_mp(dbnode, "database");
 
         for (struct _xmlAttr *attr = dbnode->properties; 
-             attr; attr = attr->next){
+             attr; attr = attr->next)
+        {
             
             mp::xml::check_attribute(attr, "", "name");
             database = mp::xml::get_text(attr);
              
             const xmlNode *explainnode;
             for (explainnode = dbnode->children; 
-                 explainnode; explainnode = explainnode->next){
+                 explainnode; explainnode = explainnode->next)
+            {
                 if (explainnode->type != XML_ELEMENT_NODE)
                     continue;
                 if (explainnode)
@@ -144,18 +161,8 @@ void yf::SRUtoZ3950::Impl::configure(const xmlNode *confignode)
     }
 }
 
-void yf::SRUtoZ3950::Impl::process(mp::Package &package)
+void yf::SRUtoZ3950::Impl::sru(mp::Package &package, Z_GDU *zgdu_req)
 {
-    Z_GDU *zgdu_req = package.request().get();
-
-    // ignoring all non HTTP_Request packages
-    if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request)){
-        package.move();
-        return;
-    }
-    
-    // only working on  HTTP_Request packages now
-
     bool ok = true;    
 
     mp::odr odr_de(ODR_DECODE);
@@ -171,11 +178,13 @@ void yf::SRUtoZ3950::Impl::process(mp::Package &package)
 
     // assign explain config XML DOM node if database is known
     const xmlNode *explainnode = 0;
-    if (idbexp != m_database_explain.end()){
+    if (idbexp != m_database_explain.end())
+    {
         explainnode = idbexp->second;
     }
     // just moving package if database is not known
-    else {
+    else
+    {
         package.move();
         return;
     }
@@ -188,8 +197,8 @@ void yf::SRUtoZ3950::Impl::process(mp::Package &package)
 
     // filter acts as sink for non-valid SRU requests
     if (! (sru_pdu_req = mp_util::decode_sru_request(package, odr_de, odr_en, 
-                                            sru_pdu_res, &soap,
-                                            charset, stylesheet)))
+                                                     sru_pdu_res, &soap,
+                                                     charset, stylesheet)))
     {
         if (soap)
         {
@@ -217,10 +226,15 @@ void yf::SRUtoZ3950::Impl::process(mp::Package &package)
         {
             zurl = std::string(arg->value);
         }
+        else if (!strcmp(arg->name, "x-max-sockets"))
+        {
+            package.origin().set_max_sockets(atoi(arg->value));
+        }
 
+    assert(sru_pdu_req);
 
     // filter acts as sink for SRU explain requests
-    if (sru_pdu_req && sru_pdu_req->which == Z_SRW_explain_request)
+    if (sru_pdu_req->which == Z_SRW_explain_request)
     {
         Z_SRW_explainRequest *er_req = sru_pdu_req->u.explain_request;
         //mp_util::build_simple_explain(package, odr_en, sru_pdu_res, 
@@ -228,9 +242,8 @@ void yf::SRUtoZ3950::Impl::process(mp::Package &package)
         mp_util::build_sru_explain(package, odr_en, sru_pdu_res, 
                                    sruinfo, explainnode, er_req);
     }
-    else if (sru_pdu_req 
-        && sru_pdu_req->which == Z_SRW_searchRetrieve_request
-        && sru_pdu_req->u.request)
+    else if (sru_pdu_req->which == Z_SRW_searchRetrieve_request
+             && sru_pdu_req->u.request)
     {   // searchRetrieve
         Z_SRW_searchRetrieveRequest *sr_req = sru_pdu_req->u.request;   
         
@@ -240,7 +253,8 @@ void yf::SRUtoZ3950::Impl::process(mp::Package &package)
         ok = mp_util::check_sru_query_exists(package, odr_en, 
                                              sru_pdu_res, sr_req);
 
-        if (ok && z3950_init_request(package, odr_en, zurl, sru_pdu_res))
+        if (ok && z3950_init_request(package, odr_en,
+                                     zurl, sru_pdu_res, sru_pdu_req))
         {
             ok = z3950_search_request(package, odr_en,
                                       sru_pdu_res, sr_req, zurl);
@@ -259,12 +273,9 @@ void yf::SRUtoZ3950::Impl::process(mp::Package &package)
     }
 
     // scan
-    else if (sru_pdu_req 
-             && sru_pdu_req->which == Z_SRW_scan_request
+    else if (sru_pdu_req->which == Z_SRW_scan_request
              && sru_pdu_req->u.scan_request)
     {
-        Z_SRW_scanRequest  *sr_req = sru_pdu_req->u.scan_request;   
-
         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_scan_response);
         
         // we do not do scan at the moment, therefore issuing a diagnostic
@@ -272,21 +283,15 @@ void yf::SRUtoZ3950::Impl::process(mp::Package &package)
                                &(sru_pdu_res->u.scan_response->diagnostics), 
                                &(sru_pdu_res->u.scan_response->num_diagnostics), 
                                YAZ_SRW_UNSUPP_OPERATION, "scan");
-        // to be used when we do scan
-        if (false && z3950_init_request(package, odr_en, zurl, sru_pdu_res))
-        {
-            z3950_scan_request(package, odr_en, sru_pdu_res, sr_req);    
-            z3950_close_request(package);
-        }        
     }
     else
     {
-        //std::cout << "SRU OPERATION NOT SUPPORTED \n";
         sru_pdu_res = yaz_srw_get(odr_en, Z_SRW_explain_response);
         
-        // TODO: make nice diagnostic return package 
-        return;
+        yaz_add_srw_diagnostic(odr_en,
+                               &(sru_pdu_res->u.explain_response->diagnostics), 
+                               &(sru_pdu_res->u.explain_response->num_diagnostics), 
+                               YAZ_SRW_UNSUPP_OPERATION, "unknown");
     }
 
     // build and send SRU response
@@ -295,12 +300,57 @@ void yf::SRUtoZ3950::Impl::process(mp::Package &package)
 }
 
 
+void yf::SRUtoZ3950::Impl::process(mp::Package &package)
+{
+    Z_GDU *zgdu_req = package.request().get();
+
+    // ignoring all non HTTP_Request packages
+    if (!zgdu_req || !(zgdu_req->which == Z_GDU_HTTP_Request))
+    {
+        package.move();
+        return;
+    }
+    
+    // only working on HTTP_Request packages now
+
+    // see if HTTP request is already being executed..
+    // we consider only the SRU - GET case..
+    if (zgdu_req->u.HTTP_Request->content_len == 0)
+    {
+        const char *path = zgdu_req->u.HTTP_Request->path;
+        boost::mutex::scoped_lock lock(m_mutex);
+        while (1)
+        {
+            ActiveUrlMap::iterator it = m_active_urls.find(path);
+            if (it == m_active_urls.end())
+            {
+                m_active_urls[path] = 1;
+                break;
+            }
+            yaz_log(YLOG_LOG, "Waiting for %s to complete", path);
+            m_cond_url_ready.wait(lock);
+        }
+    }
+    sru(package, zgdu_req);
+    if (zgdu_req->u.HTTP_Request->content_len == 0)
+    {
+        const char *path = zgdu_req->u.HTTP_Request->path;
+        boost::mutex::scoped_lock lock(m_mutex);
+
+        ActiveUrlMap::iterator it = m_active_urls.find(path);
+
+        m_active_urls.erase(it);
+        m_cond_url_ready.notify_all();
+    }
+}
+
 
 bool 
 yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package, 
                                          mp::odr &odr_en,
                                          std::string zurl,
-                                         Z_SRW_PDU *sru_pdu_res) const
+                                         Z_SRW_PDU *sru_pdu_res,
+                                         const Z_SRW_PDU *sru_pdu_req) const
 {
     // prepare Z3950 package
     Package z3950_package(package.session(), package.origin());
@@ -309,13 +359,26 @@ yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package,
     // set initRequest APDU
     Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_initRequest);
     Z_InitRequest *init_req = apdu->u.initRequest;
-    //TODO: add user name in apdu
-    //TODO: add user passwd in apdu
-    //init_req->idAuthentication = org_init->idAuthentication;
-    //init_req->implementationId = "IDxyz";
-    //init_req->implementationName = "NAMExyz";
-    //init_req->implementationVersion = "VERSIONxyz";
 
+    Z_IdAuthentication *auth = NULL;
+    if (sru_pdu_req->username && !sru_pdu_req->password)
+    {
+        auth = (Z_IdAuthentication *) odr_malloc(odr_en, sizeof(Z_IdAuthentication));
+        auth->which = Z_IdAuthentication_open;
+        auth->u.open = odr_strdup(odr_en, sru_pdu_req->username);
+    }
+    else if (sru_pdu_req->username && sru_pdu_req->password)
+    {
+        auth = (Z_IdAuthentication *) odr_malloc(odr_en, sizeof(Z_IdAuthentication));
+        auth->which = Z_IdAuthentication_idPass;
+        auth->u.idPass = (Z_IdPass *) odr_malloc(odr_en, sizeof(Z_IdPass));
+        auth->u.idPass->groupId = NULL;
+        auth->u.idPass->password = odr_strdup(odr_en, sru_pdu_req->password);
+        auth->u.idPass->userId = odr_strdup(odr_en, sru_pdu_req->username);
+    }
+
+    init_req->idAuthentication = auth;
+    
     ODR_MASK_SET(init_req->options, Z_Options_search);
     ODR_MASK_SET(init_req->options, Z_Options_present);
     ODR_MASK_SET(init_req->options, Z_Options_namedResultSets);
@@ -339,7 +402,8 @@ yf::SRUtoZ3950::Impl::z3950_init_request(mp::Package &package,
     z3950_package.move();
 
     // dead Z3950 backend detection
-    if (z3950_package.session().is_closed()){
+    if (z3950_package.session().is_closed())
+    {
         yaz_add_srw_diagnostic(odr_en,
                                &(sru_pdu_res->u.response->diagnostics),
                                &(sru_pdu_res->u.response->num_diagnostics),
@@ -383,7 +447,8 @@ yf::SRUtoZ3950::Impl::z3950_close_request(mp::Package &package) const
     //    && z3950_gdu->u.z3950->which == Z_APDU_close)
     //    return true;
 
-    if (z3950_package.session().is_closed()){
+    if (z3950_package.session().is_closed())
+    {
         return true;
     }
     return false;
@@ -427,9 +492,7 @@ bool yf::SRUtoZ3950::Impl::z3950_search_request(mp::Package &package,
     Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
     z_searchRequest->query = z_query;
  
-    if (!z3950_build_query(odr_en, z_query, 
-                           (const SRW_query&)sr_req->query, 
-                           sr_req->query_type))
+    if (!z3950_build_query(odr_en, z_query, sr_req))
     {    
         yaz_add_srw_diagnostic(odr_en,
                                &(sru_pdu_res->u.response->diagnostics), 
@@ -475,8 +538,7 @@ bool yf::SRUtoZ3950::Impl::z3950_search_request(mp::Package &package,
 
     // Finally, roll on and srw'fy number of records
     sru_pdu_res->u.response->numberOfRecords 
-        = (int *) odr_malloc(odr_en, sizeof(int *));
-    *(sru_pdu_res->u.response->numberOfRecords) = *(sr->resultCount);
+        = odr_intdup(odr_en, *sr->resultCount);
     
     // srw'fy nextRecordPosition
     //sru_pdu_res->u.response->nextRecordPosition 
@@ -580,10 +642,12 @@ yf::SRUtoZ3950::Impl::z3950_present_request(mp::Package &package,
     
     // z3950'fy number of records requested 
     // protect against requesting records out of range
-    *apdu->u.presentRequest->numberOfRecordsRequested
-        = std::min(max_recs, 
-                   *sru_pdu_res->u.response->numberOfRecords - start + 1);
-    
+    if (max_recs < *sru_pdu_res->u.response->numberOfRecords - start + 1)
+        *apdu->u.presentRequest->numberOfRecordsRequested = max_recs;
+    else
+        *apdu->u.presentRequest->numberOfRecordsRequested =
+            *sru_pdu_res->u.response->numberOfRecords - start + 1;
+        
     // z3950'fy recordPacking
     int record_packing = Z_SRW_recordPacking_XML;
     if (sr_req->recordPacking && 's' == *(sr_req->recordPacking))
@@ -705,75 +769,11 @@ yf::SRUtoZ3950::Impl::z3950_present_request(mp::Package &package,
     return true;
 }
 
-bool 
-yf::SRUtoZ3950::Impl::z3950_scan_request(mp::Package &package,
-                                        mp::odr &odr_en,
-                                        Z_SRW_PDU *sru_pdu_res,
-                                        Z_SRW_scanRequest const *sr_req) const 
-{
-    assert(sru_pdu_res->u.scan_response);
-
-    Package z3950_package(package.session(), package.origin());
-    z3950_package.copy_filter(package); 
-    //mp::odr odr_en(ODR_ENCODE);
-    Z_APDU *apdu = zget_APDU(odr_en, Z_APDU_scanRequest);
-
-    //TODO: add stuff in apdu
-    Z_ScanRequest *z_scanRequest = apdu->u.scanRequest;
-
-    // database repackaging
-    z_scanRequest->num_databaseNames = 1;
-    z_scanRequest->databaseNames = (char**)
-        odr_malloc(odr_en, sizeof(char *));
-    if (sr_req->database)
-        z_scanRequest->databaseNames[0] 
-            = odr_strdup(odr_en, const_cast<char *>(sr_req->database));
-    else
-        z_scanRequest->databaseNames[0] 
-            = odr_strdup(odr_en, "Default");
-
-
-    // query repackaging
-    // CQL or XCQL scan is not possible in Z3950, flagging a diagnostic
-    if (sr_req->query_type != Z_SRW_query_type_pqf)
-    {        
-        //send_to_srw_client_error(7, "query");
-        return false;
-    }
-
-    // PQF query repackaging
-    // need to use Z_AttributesPlusTerm structure, not Z_Query
-    // this can be digget out of a 
-    // Z_query->type1(Z_RPNQuery)->RPNStructure(Z_RPNStructure)
-    //   ->u.simple(Z_Operand)->u.attributesPlusTerm(Z_AttributesPlusTerm )
-
-    //Z_Query *z_query = (Z_Query *) odr_malloc(odr_en, sizeof(Z_Query));
-    //z_searchRequest->query = z_query;
-
-    //if (!z3950_build_query(odr_en, z_query, 
-    //                       (const SRW_query&)sr_req->query, 
-    //                       sr_req->query_type))
-    //{    
-        //send_to_srw_client_error(7, "query");
-    //    return false;
-    //}
-
-    // TODO: 
-
-    z3950_package.request() = apdu;
-    std::cout << "z3950_scan_request " << *apdu << "\n";   
-
-    z3950_package.move();
-    //TODO: check success condition
-    return true;
-    return false;
-}
-
 bool yf::SRUtoZ3950::Impl::z3950_build_query(mp::odr &odr_en, Z_Query *z_query, 
-                                            const SRW_query &query, 
-                                            SRW_query_type query_type) const
+                                             const Z_SRW_searchRetrieveRequest *req
+    ) const
 {        
-    if (query_type == Z_SRW_query_type_cql)
+    if (req->query_type == Z_SRW_query_type_cql)
     {
         Z_External *ext = (Z_External *) 
             odr_malloc(odr_en, sizeof(*ext));
@@ -782,21 +782,21 @@ bool yf::SRUtoZ3950::Impl::z3950_build_query(mp::odr &odr_en, Z_Query *z_query,
         ext->indirect_reference = 0;
         ext->descriptor = 0;
         ext->which = Z_External_CQL;
-        ext->u.cql = const_cast<char *>(query.cql);
+        ext->u.cql = odr_strdup(odr_en, req->query.cql);
         
         z_query->which = Z_Query_type_104;
         z_query->u.type_104 =  ext;
         return true;
     }
 
-    if (query_type == Z_SRW_query_type_pqf)
+    if (req->query_type == Z_SRW_query_type_pqf)
     {
         Z_RPNQuery *RPNquery;
         YAZ_PQF_Parser pqf_parser;
         
         pqf_parser = yaz_pqf_create ();
         
-        RPNquery = yaz_pqf_parse (pqf_parser, odr_en, query.pqf);
+        RPNquery = yaz_pqf_parse (pqf_parser, odr_en, req->query.pqf);
         if (!RPNquery)
         {
             std::cout << "TODO: Handeling of bad PQF\n";
@@ -865,8 +865,9 @@ extern "C" {
 /*
  * Local variables:
  * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
  * indent-tabs-mode: nil
- * c-file-style: "stroustrup"
  * End:
  * vim: shiftwidth=4 tabstop=8 expandtab
  */
+