Happy new year
[metaproxy-moved-to-github.git] / src / filter_zoom.cpp
index 39c02e6..17d7c10 100644 (file)
@@ -1,5 +1,5 @@
 /* This file is part of Metaproxy.
-   Copyright (C) 2005-2011 Index Data
+   Copyright (C) 2005-2012 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
@@ -92,6 +92,7 @@ namespace metaproxy_1 {
             SearchablePtr sptr;
             xsltStylesheetPtr xsp;
             std::string content_session_id;
+            bool enable_cproxy;
         public:
             Backend(SearchablePtr sptr);
             ~Backend();
@@ -120,7 +121,8 @@ namespace metaproxy_1 {
                                                   std::string &database,
                                                   int *error,
                                                   char **addinfo,
-                                                  ODR odr);
+                                                  ODR odr,
+                                                  int *proxy_step);
 
             bool create_content_session(mp::Package &package,
                                         BackendPtr b,
@@ -128,7 +130,8 @@ namespace metaproxy_1 {
                                         char **addinfo,
                                         ODR odr,
                                         std::string authentication,
-                                        std::string proxy);
+                                        std::string proxy,
+                                        std::string realm);
             
             void prepare_elements(BackendPtr b,
                                   Odr_oid *preferredRecordSyntax,
@@ -170,7 +173,8 @@ namespace metaproxy_1 {
             std::map<mp::Session, FrontendPtr> m_clients;            
             boost::mutex m_mutex;
             boost::condition m_cond_session_ready;
-            std::string torus_url;
+            std::string torus_searchable_url;
+            std::string torus_content_url;
             std::string default_realm;
             std::map<std::string,std::string> fieldmap;
             std::string xsldir;
@@ -218,6 +222,7 @@ yf::Zoom::Backend::Backend(SearchablePtr ptr) : sptr(ptr)
     ZOOM_connection_save_apdu_wrbuf(m_connection, m_apdu_wrbuf);
     m_resultset = 0;
     xsp = 0;
+    enable_cproxy = true;
 }
 
 yf::Zoom::Backend::~Backend()
@@ -572,7 +577,9 @@ void yf::Zoom::Impl::configure(const xmlNode *ptr, bool test_only,
             for (attr = ptr->properties; attr; attr = attr->next)
             {
                 if (!strcmp((const char *) attr->name, "url"))
-                    torus_url = mp::xml::get_text(attr->children);
+                    torus_searchable_url = mp::xml::get_text(attr->children);
+                else if (!strcmp((const char *) attr->name, "content_url"))
+                    torus_content_url = mp::xml::get_text(attr->children);
                 else if (!strcmp((const char *) attr->name, "realm"))
                     default_realm = mp::xml::get_text(attr->children);
                 else if (!strcmp((const char *) attr->name, "xsldir"))
@@ -588,6 +595,10 @@ void yf::Zoom::Impl::configure(const xmlNode *ptr, bool test_only,
                         "Bad attribute " + std::string((const char *)
                                                        attr->name));
             }
+            // If content_url is not given, use value of searchable, to
+            // ensure backwards compatibility
+            if (!torus_content_url.length())
+                torus_content_url = torus_searchable_url;
             configure_local_records(ptr->children, test_only);
         }
         else if (!strcmp((const char *) ptr->name, "cclmap"))
@@ -657,7 +668,8 @@ bool yf::Zoom::Frontend::create_content_session(mp::Package &package,
                                                 int *error, char **addinfo,
                                                 ODR odr,
                                                 std::string authentication,
-                                                std::string proxy)
+                                                std::string proxy,
+                                                std::string realm)
 {
     if (b->sptr->contentConnector.length())
     {
@@ -691,6 +703,8 @@ bool yf::Zoom::Frontend::create_content_session(mp::Package &package,
             wrbuf_printf(w, "auth: %s\n", authentication.c_str());
         if (proxy.length())
             wrbuf_printf(w, "proxy: %s\n", proxy.c_str());
+        if (realm.length())
+            wrbuf_printf(w, "realm: %s\n", realm.c_str());
 
         fwrite(wrbuf_buf(w), 1, wrbuf_len(w), file);
         fclose(file);
@@ -702,7 +716,8 @@ bool yf::Zoom::Frontend::create_content_session(mp::Package &package,
 
 yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
     mp::Package &package,
-    std::string &database, int *error, char **addinfo, ODR odr)
+    std::string &database, int *error, char **addinfo, ODR odr,
+    int *proxy_step)
 {
     std::list<BackendPtr>::const_iterator map_it;
     if (m_backend && m_backend->m_frontend_database == database)
@@ -722,12 +737,14 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
     std::string authentication;
     std::string content_authentication;
     std::string proxy;
+    std::string content_proxy;
     std::string realm = m_p->default_realm;
 
     const char *param_user = 0;
     const char *param_password = 0;
     const char *param_content_user = 0;
     const char *param_content_password = 0;
+    const char *param_nocproxy = 0;
     int no_parms = 0;
 
     char **names;
@@ -742,6 +759,8 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
     const char **out_values = (const char **)
         odr_malloc(odr, (10 + no_parms) * sizeof(*out_values));
     
+    // may be changed if it's a content connection
+    std::string torus_url = m_p->torus_searchable_url;
     int i;
     for (i = 0; i < no_parms; i++)
     {
@@ -757,12 +776,31 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
             param_content_user = value;
         else if (!strcmp(name, "content-password"))
             param_content_password = value;
+        else if (!strcmp(name, "content-proxy"))
+            content_proxy = value;
+        else if (!strcmp(name, "nocproxy"))
+            param_nocproxy = value;
         else if (!strcmp(name, "proxy"))
-            proxy = value;
+        {
+            char **dstr;
+            int dnum = 0;
+            nmem_strsplit(odr->mem, ",", value, &dstr, &dnum);
+            if (*proxy_step >= dnum)
+                *proxy_step = 0;
+            else
+            {
+                proxy = dstr[*proxy_step];
+                
+                (*proxy_step)++;
+                if (*proxy_step == dnum)
+                    *proxy_step = 0;
+            }
+        }
         else if (!strcmp(name, "cproxysession"))
         {
             out_names[no_out_args] = name;
             out_values[no_out_args++] = value;
+            torus_url = m_p->torus_content_url;
         }
         else if (!strcmp(name, "realm"))
             realm = value;
@@ -799,11 +837,10 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
     it = m_p->s_map.find(torus_db);
     if (it != m_p->s_map.end())
         sptr = it->second;
-    else if (m_p->torus_url.length() > 0)
+    else if (torus_url.length() > 0)
     {
-        xmlDoc *doc = mp::get_searchable(package,
-                                         m_p->torus_url, torus_db, realm,
-                                         m_p->proxy);
+        xmlDoc *doc = mp::get_searchable(package,torus_url, torus_db,
+                                         realm, m_p->proxy);
         if (!doc)
         {
             *error = YAZ_BIB1_DATABASE_DOES_NOT_EXIST;
@@ -919,6 +956,7 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
 
     b->xsp = xsp;
     b->m_frontend_database = database;
+    b->enable_cproxy = param_nocproxy ? false : true;
 
     if (sptr->query_encoding.length())
         b->set_option("rpnCharset", sptr->query_encoding);
@@ -973,6 +1011,11 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
             out_names[no_out_args] = "subdatabase";
             out_values[no_out_args++] = odr_strdup(odr, sptr->cfSubDB.c_str());
         }
+        if (param_nocproxy)
+        {
+            out_names[no_out_args] = "nocproxy";
+            out_values[no_out_args++] = odr_strdup(odr, param_nocproxy);
+        }
     }
     else
     {
@@ -989,6 +1032,9 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
         if (proxy.length())
             b->set_option("proxy", proxy);
     }
+    if (proxy.length())
+        package.log("zoom", YLOG_LOG, "proxy: %s", proxy.c_str());
+                
     std::string url;
     if (sptr->sru.length())
     {
@@ -1013,10 +1059,12 @@ yf::Zoom::BackendPtr yf::Zoom::Frontend::get_backend_from_databases(
     }
     package.log("zoom", YLOG_LOG, "url: %s", url.c_str());
     b->connect(url, error, addinfo, odr);
-    if (*error == 0)
+    if (*error == 0 && b->enable_cproxy)
         create_content_session(package, b, error, addinfo, odr,
                                content_authentication.length() ?
-                               content_authentication : authentication, proxy);
+                               content_authentication : authentication,
+                               content_proxy.length() ? content_proxy : proxy,
+                               realm);
     if (*error == 0)
         m_backend = b;
     return b;
@@ -1234,7 +1282,7 @@ Z_Records *yf::Zoom::Frontend::get_records(Package &package,
                     }
                 }
 
-                if (rec_buf)
+                if (rec_buf && b->enable_cproxy)
                 {
                     xmlDoc *doc = xmlParseMemory(rec_buf, rec_len);
                     std::string res = 
@@ -1380,11 +1428,22 @@ void yf::Zoom::Frontend::handle_search(mp::Package &package)
         return;
     }
 
+    int proxy_step = 0;
+
+next_proxy:
+
     int error = 0;
     char *addinfo = 0;
     std::string db(sr->databaseNames[0]);
+
     BackendPtr b = get_backend_from_databases(package, db, &error,
-                                              &addinfo, odr);
+                                              &addinfo, odr, &proxy_step);
+    if (error && proxy_step)
+    {
+        package.log("zoom", YLOG_WARN,
+                    "create backend failed: trying next proxy");
+        goto next_proxy;
+    }
     if (error)
     {
         log_diagnostic(package, error, addinfo);
@@ -1454,59 +1513,11 @@ void yf::Zoom::Frontend::handle_search(mp::Package &package)
             log_diagnostic(package, error, addinfo);
             apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
             package.response() = apdu_res;
+            cql_parser_destroy(cp);
             return;
         }
         char ccl_buf[1024];
-
         r = cql_to_ccl_buf(cn, ccl_buf, sizeof(ccl_buf));
-        if (r == 0)
-        {
-            ccl_wrbuf = wrbuf_alloc();
-            wrbuf_puts(ccl_wrbuf, ccl_buf);
-            
-            WRBUF sru_sortkeys_wrbuf = wrbuf_alloc();
-
-            cql_sortby_to_sortkeys(cn, wrbuf_vp_puts, sru_sortkeys_wrbuf);
-            WRBUF sort_spec_wrbuf = wrbuf_alloc();
-            yaz_srw_sortkeys_to_sort_spec(wrbuf_cstr(sru_sortkeys_wrbuf),
-                                          sort_spec_wrbuf);
-            wrbuf_destroy(sru_sortkeys_wrbuf);
-
-            yaz_tok_cfg_t tc = yaz_tok_cfg_create();
-            yaz_tok_parse_t tp =
-                yaz_tok_parse_buf(tc, wrbuf_cstr(sort_spec_wrbuf));
-            yaz_tok_cfg_destroy(tc);
-
-            /* go through sortspec and map fields */
-            int token = yaz_tok_move(tp);
-            while (token != YAZ_TOK_EOF)
-            {
-                if (token == YAZ_TOK_STRING)
-                {
-                    const char *field = yaz_tok_parse_string(tp);
-                    std::map<std::string,std::string>::iterator it;
-                    it = b->sptr->sortmap.find(field);
-                    if (it != b->sptr->sortmap.end())
-                        sortkeys += it->second;
-                    else
-                        sortkeys += field;
-                }
-                sortkeys += " ";
-                token = yaz_tok_move(tp);
-                if (token == YAZ_TOK_STRING)
-                {
-                    sortkeys += yaz_tok_parse_string(tp);
-                }
-                if (token != YAZ_TOK_EOF)
-                {
-                    sortkeys += " ";
-                    token = yaz_tok_move(tp);
-                }
-            }
-            yaz_tok_parse_destroy(tp);
-            wrbuf_destroy(sort_spec_wrbuf);
-        }
-        cql_parser_destroy(cp);
         if (r)
         {
             error = YAZ_BIB1_MALFORMED_QUERY;
@@ -1515,8 +1526,66 @@ void yf::Zoom::Frontend::handle_search(mp::Package &package)
             log_diagnostic(package, error, addinfo);
             apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
             package.response() = apdu_res;
+            cql_parser_destroy(cp);
             return;
         }
+
+        WRBUF sru_sortkeys_wrbuf = wrbuf_alloc();
+        if (cql_sortby_to_sortkeys(cn, wrbuf_vp_puts, sru_sortkeys_wrbuf))
+        {
+            error = YAZ_BIB1_ILLEGAL_SORT_RELATION;
+            const char *addinfo = "CQL to CCL sortby conversion";
+
+            log_diagnostic(package, error, addinfo);
+            apdu_res = odr.create_searchResponse(apdu_req, error, addinfo);
+            package.response() = apdu_res;
+            wrbuf_destroy(sru_sortkeys_wrbuf);
+            cql_parser_destroy(cp);
+            return;
+        }
+        WRBUF sort_spec_wrbuf = wrbuf_alloc();
+        yaz_srw_sortkeys_to_sort_spec(wrbuf_cstr(sru_sortkeys_wrbuf),
+                                      sort_spec_wrbuf);
+        wrbuf_destroy(sru_sortkeys_wrbuf);
+
+        ccl_wrbuf = wrbuf_alloc();
+        wrbuf_puts(ccl_wrbuf, ccl_buf);
+        
+        yaz_tok_cfg_t tc = yaz_tok_cfg_create();
+        yaz_tok_parse_t tp =
+            yaz_tok_parse_buf(tc, wrbuf_cstr(sort_spec_wrbuf));
+        yaz_tok_cfg_destroy(tc);
+        
+        /* go through sortspec and map fields */
+        int token = yaz_tok_move(tp);
+        while (token != YAZ_TOK_EOF)
+        {
+            if (token == YAZ_TOK_STRING)
+            {
+                const char *field = yaz_tok_parse_string(tp);
+                std::map<std::string,std::string>::iterator it;
+                it = b->sptr->sortmap.find(field);
+                if (it != b->sptr->sortmap.end())
+                    sortkeys += it->second;
+                else
+                    sortkeys += field;
+            }
+            sortkeys += " ";
+            token = yaz_tok_move(tp);
+            if (token == YAZ_TOK_STRING)
+            {
+                sortkeys += yaz_tok_parse_string(tp);
+            }
+            if (token != YAZ_TOK_EOF)
+            {
+                sortkeys += " ";
+                token = yaz_tok_move(tp);
+            }
+        }
+        yaz_tok_parse_destroy(tp);
+        wrbuf_destroy(sort_spec_wrbuf);
+
+        cql_parser_destroy(cp);
     }
     else
     {
@@ -1622,6 +1691,14 @@ void yf::Zoom::Frontend::handle_search(mp::Package &package)
         wrbuf_destroy(pqf_wrbuf);
     }
 
+    if (error && proxy_step)
+    {
+        // reset below prevent reuse in get_backend_from_databases
+        m_backend.reset();
+        package.log("zoom", YLOG_WARN, "search failed: trying next proxy");
+        goto next_proxy;
+    }
+
     const char *element_set_name = 0;
     Odr_int number_to_present = 0;
     if (!error)