Fixed timeout bug for SRW/SRU operation : Could occur if XSLT
[yazproxy-moved-to-github.git] / src / yaz-proxy.cpp
index 1edd048..2c35be0 100644 (file)
@@ -1,7 +1,7 @@
-/* $Id: yaz-proxy.cpp,v 1.41 2005-11-30 11:38:46 adam Exp $
-   Copyright (c) 1998-2005, Index Data.
+/* $Id: yaz-proxy.cpp,v 1.63 2006-04-27 00:04:42 adam Exp $
+   Copyright (c) 1998-2006, Index Data.
 
-This file is part of the yaz-proxy.
+This file is part of the yazproxy.
 
 YAZ proxy 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
@@ -55,10 +55,14 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
 using namespace yazpp_1;
 
+#ifdef WIN32
+#define strncasecmp _strnicmp
+#endif
+
 #define USE_AUTH_MSG 1
 
 #if USE_AUTH_MSG
-class Auth_Msg : public IMsg_Thread {
+class YAZ_EXPORT Auth_Msg : public IMsg_Thread {
 public:
     int m_ret;
     IMsg_Thread *handle();
@@ -80,10 +84,9 @@ Auth_Msg::~Auth_Msg()
 {
     nmem_destroy(m_nmem);
 }
-    
+
 IMsg_Thread *Auth_Msg::handle()
 {
-    yaz_log(YLOG_LOG, "Auth_Msg:handle begin");
     ODR decode = odr_createmem(ODR_DECODE);
     Z_APDU *apdu;
 
@@ -97,26 +100,22 @@ IMsg_Thread *Auth_Msg::handle()
     {
         m_ret = m_proxy->handle_authentication(apdu);
     }
-    yaz_log(YLOG_LOG, "Auth_Msg:handle end");
     odr_destroy(decode);
     return this;
 }
 
 void Auth_Msg::result()
 {
-    yaz_log(YLOG_LOG, "Auth_Msg:result proxy ok buf=%p len=%d",
-            m_apdu_buf, m_apdu_len);
-    if (m_proxy->dec_ref(false))
+    if (m_proxy->dec_ref())
+    {
         yaz_log(YLOG_LOG, "Auth_Msg::proxy deleted meanwhile");
+    }
     else
     {
-        yaz_log(YLOG_LOG, "Auth_Msg::proxy still alive");
         odr_setbuf(m_proxy->odr_decode(), m_apdu_buf, m_apdu_len, 0);
         Z_APDU *apdu = 0;
         int r = z_APDU(m_proxy->odr_decode(), &apdu, 0, 0);
-        if (r)
-            yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU OK");
-        else
+        if (!r)
             yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU failed");
         m_proxy->result_authentication(apdu, m_ret);
     }
@@ -132,10 +131,19 @@ void Yaz_Proxy::result_authentication(Z_APDU *apdu, int ret)
         Z_APDU *apdu_reject = zget_APDU(odr_encode(), Z_APDU_initResponse);
         *apdu_reject->u.initResponse->result = 0;
         send_to_client(apdu_reject);
-        dec_ref(false);
+        dec_ref();
     }
     else
+    {
+        if (apdu->which == Z_APDU_initRequest)
+        {
+            Yaz_ProxyConfig *cfg = check_reconfigure();
+            if (cfg)
+                cfg->target_authentication(m_default_target, odr_encode(), 
+                                           apdu->u.initRequest);
+        }
         handle_incoming_Z_PDU_2(apdu);
+    }
 }
 
 static const char *apdu_name(Z_APDU *apdu)
@@ -192,9 +200,10 @@ static const char *gdu_name(Z_GDU *gdu)
 
 Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
                      ISocketObservable *the_socket_observable,
-                     Yaz_Proxy *parent) 
+                     Yaz_Proxy *parent)
     :
-    Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
+    Z_Assoc(the_PDU_Observable),
+    m_bw_stat(60), m_pdu_stat(60), m_search_stat(60)
 {
     m_PDU_Observable = the_PDU_Observable;
     m_socket_observable = the_socket_observable;
@@ -208,6 +217,7 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
     m_default_target = 0;
     m_proxy_negotiation_charset = 0;
     m_proxy_negotiation_lang = 0;
+    m_proxy_negotiation_default_charset = 0;
     m_charset_converter = new Yaz_CharsetConverter;
     m_max_clients = 150;
     m_log_mask = 0;
@@ -222,7 +232,10 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
     m_bw_max = 0;
     m_pdu_max = 0;
     m_search_max = 0;
-    m_connect_max = 0;
+    m_max_connect = 0;
+    m_max_connect_period = 0;
+    m_limit_connect = 0;
+    m_limit_connect_period = 0;
     m_timeout_mode = timeout_normal;
     m_timeout_gdu = 0;
     m_max_record_retrieve = 0;
@@ -301,6 +314,7 @@ Yaz_Proxy::~Yaz_Proxy()
     xfree(m_default_target);
     xfree(m_proxy_negotiation_charset);
     xfree(m_proxy_negotiation_lang);
+    xfree(m_proxy_negotiation_default_charset);
     delete m_charset_converter;
     xfree(m_optimize);
 
@@ -341,7 +355,12 @@ int Yaz_Proxy::set_config(const char *config)
     m_config_fname = xstrdup(config);
     int r = m_config->read_xml(config);
     if (!r)
-        m_config->get_generic_info(&m_log_mask, &m_max_clients);
+    {
+        int period = 60;
+        m_config->get_generic_info(&m_log_mask, &m_max_clients,
+                                   &m_max_connect, &m_limit_connect, &period);
+        m_connect.set_period(period);
+    }
     return r;
 }
 
@@ -353,10 +372,13 @@ void Yaz_Proxy::set_default_target(const char *target)
         m_default_target = (char *) xstrdup (target);
 }
 
-void Yaz_Proxy::set_proxy_negotiation (const char *charset, const char *lang)
+void Yaz_Proxy::set_proxy_negotiation (const char *charset, const char *lang,
+                                       const char *default_charset)
 {
-    yaz_log(YLOG_LOG, "%sSet the proxy negotiation: charset to '%s', "
-        "language to '%s'", m_session_str, charset?charset:"none",
+    yaz_log(YLOG_DEBUG, "%sSet the proxy negotiation: charset to '%s', "
+        "default charset to '%s', language to '%s'", m_session_str, 
+        charset?charset:"none",
+        default_charset?default_charset:"none",
         lang?lang:"none");
     xfree (m_proxy_negotiation_charset);
     xfree (m_proxy_negotiation_lang);
@@ -365,6 +387,9 @@ void Yaz_Proxy::set_proxy_negotiation (const char *charset, const char *lang)
         m_proxy_negotiation_charset = (char *) xstrdup (charset);
     if (lang)
         m_proxy_negotiation_lang = (char *) xstrdup (lang);
+    if (default_charset)
+        m_proxy_negotiation_default_charset =
+            (char *) xstrdup (default_charset);
 }
 
 Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
@@ -386,7 +411,11 @@ Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
             else
             {
                 m_log_mask = 0;
-                cfg->get_generic_info(&m_log_mask, &m_max_clients);
+                int period = 60;
+                cfg->get_generic_info(&m_log_mask, &m_max_clients,
+                                      &m_max_connect, &m_limit_connect,
+                                      &period);
+                m_connect.set_period(period);
             }
         }
         else
@@ -400,8 +429,22 @@ IPDU_Observer *Yaz_Proxy::sessionNotify(IPDU_Observable
                                         *the_PDU_Observable, int fd)
 {
     check_reconfigure();
+
+    char session_str[200];
+    const char *peername = the_PDU_Observable->getpeername();
+    if (m_log_mask & PROXY_LOG_IP_CLIENT)
+        sprintf(session_str, "%ld:%d %.80s %d ",
+                (long) time(0), m_session_no, peername, 0);
+    else
+        sprintf(session_str, "%ld:%d %d ",
+                (long) time(0), m_session_no, 0);
+    m_session_no++;
+
+    yaz_log (YLOG_LOG, "%sNew session %s", session_str, peername);
+
     Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable,
                                          m_socket_observable, this);
+
     new_proxy->m_config = 0;
     new_proxy->m_config_fname = 0;
     new_proxy->timeout(m_client_idletime);
@@ -409,18 +452,19 @@ IPDU_Observer *Yaz_Proxy::sessionNotify(IPDU_Observable
     new_proxy->set_default_target(m_default_target);
     new_proxy->m_max_clients = m_max_clients;
     new_proxy->m_log_mask = m_log_mask;
+
+    if (!strcmp(peername, "tcp:163.121.19.82")) // NIS GROUP
+        new_proxy->m_log_mask = 255;
+
     new_proxy->set_APDU_log(get_APDU_log());
-    if (m_log_mask & PROXY_LOG_APDU_CLIENT)
+    if (new_proxy->m_log_mask & PROXY_LOG_APDU_CLIENT)
         new_proxy->set_APDU_yazlog(1);
     else
         new_proxy->set_APDU_yazlog(0);
-    sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
-    m_session_no++;
-    new_proxy->m_peername = xstrdup(the_PDU_Observable->getpeername());
-    yaz_log (YLOG_LOG, "%sNew session %s", new_proxy->m_session_str,
-             new_proxy->m_peername);
+    strcpy(new_proxy->m_session_str, session_str);
+    new_proxy->m_peername = xstrdup(peername);
     new_proxy->set_proxy_negotiation(m_proxy_negotiation_charset,
-        m_proxy_negotiation_lang);
+        m_proxy_negotiation_lang, m_proxy_negotiation_default_charset);
     // create thread object the first time we get an incoming connection
     if (!m_my_thread)
         m_my_thread = new Msg_Thread(m_socket_observable, 1);
@@ -438,7 +482,7 @@ char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
     ent.value = (oid_value) VAL_COOKIE;
     assert (oid_ent_to_oid (&ent, oid));
 
-    if (oid_ent_to_oid (&ent, oid) && 
+    if (oid_ent_to_oid (&ent, oid) &&
         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
         oi->which == Z_OtherInfo_characterInfo)
         return oi->information.characterInfo;
@@ -495,7 +539,7 @@ const char *Yaz_Proxy::load_balance(const char **url)
             min_use = zurl_in_use[i];
             spare_for_min = zurl_in_spare[i];
         }
-        if (max_spare < zurl_in_spare[i]) 
+        if (max_spare < zurl_in_spare[i])
         {
             ret_spare = url[i];
             max_spare = zurl_in_spare[i];
@@ -510,7 +554,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
     assert (m_parent);
     Yaz_Proxy *parent = m_parent;
     Yaz_ProxyClient *c = m_client;
-    
+
     if (!m_proxyTarget)
     {
         const char *url[MAX_ZURL_PLEX];
@@ -532,6 +576,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
         const char *negotiation_charset = 0;
         const char *negotiation_lang = 0;
         const char *query_charset = 0;
+        const char *default_client_query_charset = 0;
         url[0] = m_default_target;
         url[1] = 0;
         if (cfg)
@@ -539,7 +584,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
             int pre_init = 0;
             cfg->get_target_info(proxy_host, url, &m_bw_max,
                                  &m_pdu_max, &m_max_record_retrieve,
-                                 &m_search_max, &m_connect_max,
+                                 &m_search_max,
                                  &m_target_idletime, &client_idletime,
                                  &parent->m_max_clients,
                                  &m_keepalive_limit_bw,
@@ -548,19 +593,26 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
                                  &cql2rpn_fname,
                                  &negotiation_charset,
                                  &negotiation_lang,
-                                 &query_charset);
+                                 &query_charset,
+                                 &default_client_query_charset);
         }
         if (client_idletime != -1)
         {
             m_client_idletime = client_idletime;
             timeout(m_client_idletime);
         }
+
+        // get those FILE descriptors available 
+        m_parent->low_socket_close();
         if (cql2rpn_fname)
             m_cql2rpn.set_pqf_file(cql2rpn_fname);
-        if (negotiation_charset || negotiation_lang)
+        // reserve them again
+        m_parent->low_socket_open();
+        
+        if (negotiation_charset || negotiation_lang || default_client_query_charset)
         {
             set_proxy_negotiation(negotiation_charset,
-                negotiation_lang);
+                negotiation_lang, default_client_query_charset);
         }
         m_charset_converter->set_target_query_charset(query_charset);
         if (!url[0])
@@ -595,7 +647,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
                              c->get_hostname());
                     c->close();
                     c->m_init_flag = 0;
-                    
+
                     c->m_last_ok = 0;
                     c->m_cache.clear();
                     c->m_last_resultCount = 0;
@@ -608,7 +660,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
                         delete c;
                         return 0;
                     }
-                    c->timeout(30); 
+                    c->timeout(30);
                 }
                 c->m_seqno = parent->m_seqno;
                 if (c->m_server && c->m_server != this)
@@ -620,10 +672,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
             }
         }
     }
-    else if (!c &&
-            apdu->which == Z_APDU_initRequest &&
-            apdu->u.initRequest->idAuthentication == 0 &&
-            !ODR_MASK_GET(apdu->u.initRequest->options, Z_Options_negotiationModel))
+    else if (!c && apdu->which == Z_APDU_initRequest )
     {
         // anonymous sessions without cookie.
         // if authentication is set it is NOT anonymous se we can't share them.
@@ -632,9 +681,10 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
         {
             assert(c->m_prev);
             assert(*c->m_prev == c);
-            if (c->m_server == 0 && c->m_cookie == 0 && 
-                c->m_waiting == 0 && 
-                !strcmp(m_proxyTarget, c->get_hostname()))
+            if (c->m_server == 0 && c->m_cookie == 0 &&  c->m_waiting == 0 
+                && c->compare_idAuthentication(apdu)
+                && c->compare_charset(apdu)
+                && !strcmp(m_proxyTarget, c->get_hostname()))
             {
                 // found it in cache
                 yaz_log (YLOG_LOG, "%sREUSE %d %s",
@@ -650,9 +700,9 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
                     c->set_APDU_yazlog(0);
 
                 (parent->m_seqno)++;
-                
+
                 parent->pre_init();
-                
+
                 return c;
             }
         }
@@ -664,14 +714,6 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
             yaz_log (YLOG_LOG, "%sno init request as first PDU", m_session_str);
             return 0;
         }
-        Z_InitRequest *initRequest = apdu->u.initRequest;
-
-        if (initRequest->idAuthentication)
-        {
-            // the client uses authentication. We set the keepalive PDU
-            // to 0 so we don't cache it in releaseClient
-            m_keepalive_limit_pdu = 0;
-        }
         // go through list of clients - and find the lowest/oldest one.
         Yaz_ProxyClient *c_min = 0;
         int min_seq = -1;
@@ -698,8 +740,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
                 yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Destroy %d",
                          m_session_str, parent->m_max_clients, c->m_seqno);
                 if (c->m_server && c->m_server != this)
-                    delete c->m_server;   // PROBLEM: m_ref_count!
-                c->m_server = 0;
+                    c->m_server->dec_ref();
             }
             else
             {
@@ -714,12 +755,12 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
                 if (c->m_server && c->m_server != this)
                 {
                     c->m_server->m_client = 0;
-                    delete c->m_server;   // PROBLEM: m_ref_count!
+                    c->m_server->dec_ref();
                 }
                 (parent->m_seqno)++;
                 c->m_target_idletime = m_target_idletime;
                 c->timeout(m_target_idletime);
-                
+
                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
                     c->set_APDU_yazlog(1);
                 else
@@ -766,6 +807,8 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
             c->set_APDU_yazlog(1);
         else
             c->set_APDU_yazlog(0);
+
+        c->set_idAuthentication(apdu);
     }
     yaz_log (YLOG_DEBUG, "get_client 3 %p %p", this, c);
     return c;
@@ -839,7 +882,7 @@ void Yaz_Proxy::convert_xsl_delay()
                 (char*) r->u.octet_aligned->buf,
                 r->u.octet_aligned->len);
 
-            
+
             yaz_log(YLOG_LOG, "%sXSLT convert %d",
                     m_session_str, m_stylesheet_offset);
             res = xsltApplyStylesheet((xsltStylesheetPtr) m_stylesheet_xsp,
@@ -850,9 +893,9 @@ void Yaz_Proxy::convert_xsl_delay()
                 xmlChar *out_buf;
                 int out_len;
                 xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
-                
+
                 m_stylesheet_nprl->records[m_stylesheet_offset]->
-                    u.databaseRecord = 
+                    u.databaseRecord =
                     z_ext_record(odr_encode(), VAL_TEXT_XML,
                                  (char*) out_buf, out_len);
                 xmlFree(out_buf);
@@ -956,7 +999,6 @@ void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p)
 void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p,
                                         const char *backend_charset)
 {
-    yaz_log(YLOG_LOG, "%sconvert_to_marc", m_session_str);
     int sel =   m_charset_converter->get_client_charset_selected();
     const char *client_record_charset =
         m_charset_converter->get_client_query_charset();
@@ -1015,10 +1057,6 @@ void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p,
             yaz_iconv_close(cd);
         yaz_marc_destroy(mt);
     }
-    else
-    {
-        yaz_log(YLOG_LOG, "%sSkipping marc convert", m_session_str);
-    }
 }
 
 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p,
@@ -1041,7 +1079,7 @@ void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p,
             {
                 WRBUF w = wrbuf_alloc();
 
-                yaz_display_OPAC(w, r->u.opac, 0);
+                yaz_opac_decode_wrbuf(mt, r->u.opac, w);
                 npr->u.databaseRecord = z_ext_record(
                     odr_encode(), VAL_TEXT_XML,
                     wrbuf_buf(w), wrbuf_len(w)
@@ -1097,7 +1135,7 @@ int Yaz_Proxy::send_http_response(int code)
         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
     else
         timeout(0);
-    
+
     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
     {
         yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
@@ -1114,11 +1152,11 @@ int Yaz_Proxy::send_http_response(int code)
     return r;
 }
 
-int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
+int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu, int http_code /* = 200 */)
 {
     ODR o = odr_encode();
     const char *ctype = "text/xml";
-    Z_GDU *gdu = z_get_HTTP_Response(o, 200);
+    Z_GDU *gdu = z_get_HTTP_Response(o, http_code);
     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
     if (m_http_version)
         hres->version = odr_strdup(o, m_http_version);
@@ -1127,6 +1165,8 @@ int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
     else
         timeout(0);
+    if (http_code == 401)
+        z_HTTP_header_add(o, &hres->headers, "WWW-Authenticate", "Basic realm=\"YAZ Proxy\"");
 
     static Z_SOAP_Handler soap_handlers[2] = {
 #if HAVE_XSLT
@@ -1135,10 +1175,10 @@ int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
 #endif
         {0, 0, 0}
     };
-    
+
     Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
     soap_package->which = Z_SOAP_generic;
-    soap_package->u.generic = 
+    soap_package->u.generic =
         (Z_SOAP_Generic *) odr_malloc(o,  sizeof(*soap_package->u.generic));
     soap_package->u.generic->no = 0;
     soap_package->u.generic->ns = soap_handlers[0].ns;
@@ -1170,7 +1210,7 @@ int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
         odr_malloc(o, sizeof(*diagnostic));
     int num_diagnostic = 1;
     yaz_mk_std_diagnostic(o, diagnostic, srw_error, add);
-    return send_srw_search_response(diagnostic, num_diagnostic);
+    return send_srw_search_response(diagnostic, num_diagnostic, srw_error == 3 ? 401 : 200);
 }
 
 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
@@ -1183,7 +1223,7 @@ int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
     srw_res->diagnostics = (Z_SRW_diagnostic *)
         odr_malloc(o, sizeof(*srw_res->diagnostics));
     yaz_mk_std_diagnostic(o, srw_res->diagnostics,
-                          yaz_diag_bib1_to_srw(*ddf->condition), 
+                          yaz_diag_bib1_to_srw(*ddf->condition),
                           ddf->u.v2Addinfo);
     return 0;
 }
@@ -1220,7 +1260,7 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
             {
                 srw_res->records[i].recordSchema = m_schema;
                 srw_res->records[i].recordPacking = m_s2z_packing;
-                srw_res->records[i].recordData_buf = (char*) 
+                srw_res->records[i].recordData_buf = (char*)
                     r->u.octet_aligned->buf;
                 srw_res->records[i].recordData_len = r->u.octet_aligned->len;
                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
@@ -1244,11 +1284,11 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
             return send_http_response(http_code);
     }
     return send_srw_response(srw_pdu);
-    
+
 }
 
 int Yaz_Proxy::send_srw_search_response(Z_SRW_diagnostic *diagnostics,
-                                        int num_diagnostics)
+                                        int num_diagnostics, int http_code /* = 200 */)
 {
     ODR o = odr_encode();
     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
@@ -1256,7 +1296,7 @@ int Yaz_Proxy::send_srw_search_response(Z_SRW_diagnostic *diagnostics,
 
     srw_res->num_diagnostics = num_diagnostics;
     srw_res->diagnostics = diagnostics;
-    return send_srw_response(srw_pdu);
+    return send_srw_response(srw_pdu, http_code);
 }
 
 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
@@ -1317,9 +1357,9 @@ int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
             }
             else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
             {
-                // adjust 
+                // adjust
                 Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
-                
+
                 if (*pr->resultSetStartPoint <= m_s2z_hit_count)
                 {
                     if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
@@ -1337,7 +1377,7 @@ int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
         }
         else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
         {
-            int start = 
+            int start =
                 *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
 
             m_s2z_present_apdu = 0;
@@ -1367,7 +1407,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
 
     if (new_id)
         *new_id = m_referenceId;
-    
+
     if (apdu->which == Z_APDU_searchResponse)
     {
         Z_SearchResponse *sr = apdu->u.searchResponse;
@@ -1399,7 +1439,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
                                             m_backend_charset);
                 if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
                     return 0;
-                    
+
             }
             if (sr->resultCount)
             {
@@ -1433,7 +1473,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
         }
         if (p && p->which == Z_Records_DBOSD)
         {
-            if (m_backend_type 
+            if (m_backend_type
 #if HAVE_USEMARCON
                 || m_usemarcon_ini_stage1 || m_usemarcon_ini_stage2
 #endif
@@ -1454,10 +1494,10 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
         //Get and check negotiation record
         //from init response.
         handle_charset_lang_negotiation(apdu);
-        
+
         if (m_initRequest_options)
         {
-            Z_Options *nopt = 
+            Z_Options *nopt =
                 (Odr_bitmask *)odr_malloc(odr_encode(),
                                           sizeof(Odr_bitmask));
             ODR_MASK_ZERO(nopt);
@@ -1467,11 +1507,11 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
                 if (ODR_MASK_GET(m_initRequest_options, i) &&
                     ODR_MASK_GET(apdu->u.initResponse->options, i))
                     ODR_MASK_SET(nopt, i);
-            apdu->u.initResponse->options = nopt;           
+            apdu->u.initResponse->options = nopt;
         }
         if (m_initRequest_version)
         {
-            Z_ProtocolVersion *nopt = 
+            Z_ProtocolVersion *nopt =
                 (Odr_bitmask *)odr_malloc(odr_encode(),
                                           sizeof(Odr_bitmask));
             ODR_MASK_ZERO(nopt);
@@ -1481,7 +1521,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
                 if (ODR_MASK_GET(m_initRequest_version, i) &&
                     ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
                     ODR_MASK_SET(nopt, i);
-            apdu->u.initResponse->protocolVersion = nopt;           
+            apdu->u.initResponse->protocolVersion = nopt;
         }
         apdu->u.initResponse->preferredMessageSize =
             odr_intdup(odr_encode(),
@@ -1496,7 +1536,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
                        m_initRequest_maximumRecordSize :
                        m_client->m_initResponse_maximumRecordSize);
     }
-    
+
     int r = send_PDU_convert(apdu);
     if (r)
         return r;
@@ -1509,6 +1549,43 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
     return r;
 }
 
+void Yaz_ProxyClient::set_idAuthentication(Z_APDU *apdu)
+{
+    Z_IdAuthentication *t = apdu->u.initRequest->idAuthentication;
+    
+    odr_reset(m_idAuthentication_odr);
+    z_IdAuthentication(m_idAuthentication_odr, &t, 1, 0);
+    m_idAuthentication_ber_buf =
+        odr_getbuf(m_idAuthentication_odr, 
+                   &m_idAuthentication_ber_size, 0);
+}
+
+bool Yaz_ProxyClient::compare_charset(Z_APDU *apdu)
+{
+    return true;
+}
+
+bool Yaz_ProxyClient::compare_idAuthentication(Z_APDU *apdu)
+{
+    Z_IdAuthentication *t = apdu->u.initRequest->idAuthentication;
+    ODR odr = odr_createmem(ODR_ENCODE);
+
+    z_IdAuthentication(odr, &t, 1, 0);
+    int sz;
+    char *buf = odr_getbuf(odr, &sz, 0);
+    if (buf && m_idAuthentication_ber_buf
+        && sz == m_idAuthentication_ber_size
+        && !memcmp(m_idAuthentication_ber_buf, buf, sz))
+    {
+        odr_destroy(odr);
+        return true;
+    }
+    odr_destroy(odr);
+    if (!buf && !m_idAuthentication_ber_buf)
+        return true;
+    return false;
+}
+
 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
 {
     int len = 0;
@@ -1540,8 +1617,21 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
         {
             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
             new_apdu->u.presentResponse->records =
-                create_nonSurrogateDiagnostics(odr_encode(), 30,
-                                               pr->resultSetId);
+                create_nonSurrogateDiagnostics(
+                    odr_encode(), 
+                    YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
+                    pr->resultSetId);
+            send_to_client(new_apdu);
+            return 0;
+        }
+        if (start < 1 || toget < 0)
+        {
+            Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
+            new_apdu->u.presentResponse->records =
+                create_nonSurrogateDiagnostics(
+                    odr_encode(), 
+                    YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, 
+                    0);
             send_to_client(new_apdu);
             return 0;
         }
@@ -1551,7 +1641,10 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
             {
                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
                 new_apdu->u.presentResponse->records =
-                    create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
+                    create_nonSurrogateDiagnostics(
+                        odr_encode(), 
+                        YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE,
+                        0);
                 send_to_client(new_apdu);
                 return 0;
             }
@@ -1565,14 +1658,14 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
                                           pr->preferredRecordSyntax,
                                           pr->recordComposition))
             {
-                yaz_log (YLOG_LOG, "%sReturned cached records for present request", 
+                yaz_log (YLOG_LOG, "%sReturned cached records for present request",
                          m_session_str);
                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
                 new_apdu->u.presentResponse->referenceId = pr->referenceId;
-                
+
                 new_apdu->u.presentResponse->numberOfRecordsReturned
                     = odr_intdup(odr_encode(), toget);
-                                                                 
+
                 new_apdu->u.presentResponse->records = (Z_Records*)
                     odr_malloc(odr_encode(), sizeof(Z_Records));
                 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
@@ -1594,9 +1687,24 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
 
     this_databases.set(sr->num_databaseNames, (const char **)
                        sr->databaseNames);
-    
+
     this_query->set_Z_Query(sr->query);
 
+    // Check for non-negative piggyback params.
+    if (*sr->smallSetUpperBound < 0
+        || *sr->largeSetLowerBound < 0
+        || *sr->mediumSetPresentNumber < 0)
+    {
+        Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+        // Not a present request.. But can't find better diagnostic
+        new_apdu->u.searchResponse->records =
+            create_nonSurrogateDiagnostics(
+                odr_encode(), 
+                YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, 0);
+        send_to_client(new_apdu);
+        return 0;
+    }
+
     char query_str[120];
     this_query->print(query_str, sizeof(query_str)-1);
     yaz_log(YLOG_LOG, "%sSearch %s", m_session_str, query_str);
@@ -1625,7 +1733,7 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
                 comp->which = Z_RecordComp_simple;
                 comp->u.simple = sr->mediumSetElementSetNames;
             }
+
             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
                                           sr->preferredRecordSyntax, comp))
             {
@@ -1635,10 +1743,10 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
                 new_apdu->u.searchResponse->resultCount =
                     &m_client->m_last_resultCount;
-                
+
                 new_apdu->u.searchResponse->numberOfRecordsReturned
                     = odr_intdup(odr_encode(), toget);
-                                                        
+
                 new_apdu->u.searchResponse->presentStatus =
                     odr_intdup(odr_encode(), Z_PresentStatus_success);
                 new_apdu->u.searchResponse->records = (Z_Records*)
@@ -1688,7 +1796,7 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
             Z_RecordComposition *comp = 0;
             // small set
             // send a present request (small set)
-            
+
             if (sr->smallSetElementSetNames)
             {
                 comp = (Z_RecordComposition *)
@@ -1706,10 +1814,10 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
                 new_apdu->u.searchResponse->resultCount =
                     &m_client->m_last_resultCount;
-                
+
                 new_apdu->u.searchResponse->numberOfRecordsReturned
                     = odr_intdup(odr_encode(), toget);
-                                                                 
+
                 new_apdu->u.searchResponse->presentStatus =
                     odr_intdup(odr_encode(), Z_PresentStatus_success);
                 new_apdu->u.searchResponse->records = (Z_Records*)
@@ -1757,9 +1865,13 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
 
 void Yaz_Proxy::inc_request_no()
 {
-    char *cp = strchr(m_session_str, ' ');
     m_request_no++;
-    if (cp)
+    char *cp = m_session_str + strlen(m_session_str)-1;
+    if (*cp == ' ')
+        cp--;
+    while (*cp && *cp != ' ')
+        cp--;
+    if (*cp)
         sprintf(cp+1, "%d ", m_request_no);
 }
 
@@ -1768,39 +1880,133 @@ void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
     inc_request_no();
 
     m_bytes_recv += len;
-    
+
     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
         yaz_log (YLOG_LOG, "%sReceiving %s from client %d bytes",
                  m_session_str, gdu_name(apdu), len);
-    
+
+#if 0
+    // try to make a _bad_ attribute set ID .. Don't enable this in prod.
+    if (apdu->which == Z_GDU_Z3950 
+        && apdu->u.z3950->which == Z_APDU_searchRequest)
+    {
+        Z_SearchRequest *req = apdu->u.z3950->u.searchRequest;
+        if (req->query && req->query->which == Z_Query_type_1)
+        {
+            Z_RPNQuery *rpnquery = req->query->u.type_1;
+            if (rpnquery->attributeSetId)
+            {
+                rpnquery->attributeSetId[0] = -2;
+                rpnquery->attributeSetId[1] = -1;
+                yaz_log(YLOG_WARN, "%sBAD FIXUP TEST", m_session_str);
+            }
+        }
+    }
+#endif
+
 #if HAVE_GETTIMEOFDAY
     gettimeofday((struct timeval *) m_time_tv, 0);
 #endif
     m_bw_stat.add_bytes(len);
     m_pdu_stat.add_bytes(1);
-    
+
     GDU *gdu = new GDU(apdu);
-    m_in_queue.enqueue(gdu);
 
+    if (gdu->get() == 0)
+    {
+        delete gdu;
+        yaz_log(YLOG_LOG, "%sUnable to encode package", m_session_str);
+        m_in_queue.clear();
+        dec_ref();
+        return;
+    }
+    m_in_queue.enqueue(gdu);
     recv_GDU_more(false);
 }
 
+void Yaz_Proxy::HTTP_Forwarded(Z_GDU *z_gdu)
+{
+    if (z_gdu->which == Z_GDU_HTTP_Request)
+    {
+        Z_HTTP_Request *hreq = z_gdu->u.HTTP_Request;
+        const char *x_forwarded_for =
+            z_HTTP_header_lookup(hreq->headers, "X-Forwarded-For");
+        if (x_forwarded_for)
+        {
+            xfree(m_peername);
+            m_peername = (char*) xmalloc(strlen(x_forwarded_for)+5);
+            sprintf(m_peername, "tcp:%s", x_forwarded_for);
+            
+            yaz_log(YLOG_LOG, "%sHTTP Forwarded from %s", m_session_str,
+                    m_peername);
+            if (m_log_mask & PROXY_LOG_IP_CLIENT)
+                sprintf(m_session_str, "%ld:%d %.80s %d ",
+                        (long) time(0), m_session_no, m_peername, m_request_no);
+            else
+                sprintf(m_session_str, "%ld:%d %d ",
+                        (long) time(0), m_session_no, m_request_no);
+        }
+    }
+}
+
+void Yaz_Proxy::connect_stat(bool &block, int &reduce)
+{
+
+    m_parent->m_connect.cleanup(false);
+    m_parent->m_connect.add_connect(m_peername);
+
+    int connect_total = m_parent->m_connect.get_total(m_peername);
+    int max_connect = m_parent->m_max_connect;
+
+    if (max_connect && connect_total > max_connect)
+    {
+        yaz_log(YLOG_LOG, "%sconnect not accepted total=%d max=%d",
+                m_session_str, connect_total, max_connect);
+        block = true;
+    }
+    else 
+        block = false;
+    yaz_log(YLOG_LOG, "%sconnect accepted total=%d", m_session_str,
+            connect_total);
+    
+    int limit_connect = m_parent->m_limit_connect;
+    if (limit_connect)
+        reduce = connect_total / limit_connect;
+    else
+        reduce = 0;
+}
+
 void Yaz_Proxy::recv_GDU_reduce(GDU *gdu)
 {
-    int bw_total = m_bw_stat.get_total();
-    int pdu_total = m_pdu_stat.get_total();
-    int reduce = 0;
+    HTTP_Forwarded(gdu->get());
 
-    assert(m_timeout_mode == timeout_busy);
-    assert(m_timeout_gdu == 0);
+    int reduce = 0;
     
-    if (m_bw_max)
+    if (m_request_no == 1)
     {
-        if (bw_total > m_bw_max)
+        bool block = false;
+        
+        connect_stat(block, reduce);
+
+        if (block)
         {
-            reduce = (bw_total/m_bw_max);
+            m_timeout_mode = timeout_busy;
+            timeout(0);
+            return;
         }
     }
+
+    int bw_total = m_bw_stat.get_total();
+    int pdu_total = m_pdu_stat.get_total();
+    int search_total = m_search_stat.get_total();
+
+    assert(m_timeout_mode == timeout_busy);
+    assert(m_timeout_gdu == 0);
+
+    if (m_search_max)
+        reduce += search_total / m_search_max;
+    if (m_bw_max)
+        reduce += (bw_total/m_bw_max);
     if (m_pdu_max)
     {
         if (pdu_total > m_pdu_max)
@@ -1817,13 +2023,13 @@ void Yaz_Proxy::recv_GDU_reduce(GDU *gdu)
     m_timeout_gdu = gdu;
     timeout(3);       // call us reduce seconds later
     return;
-#endif    
-    if (reduce)  
+#endif
+    if (reduce)
     {
-        yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
-                m_session_str, reduce, bw_total, pdu_total,
-                m_bw_max, m_pdu_max);
-        
+        yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d search=%d limit-bw=%d limit-pdu=%d limit-search=%d",
+                m_session_str, reduce, bw_total, pdu_total, search_total,
+                m_bw_max, m_pdu_max, m_search_max);
+
         m_timeout_mode = timeout_reduce;
         m_timeout_gdu = gdu;
         timeout(reduce);       // call us reduce seconds later
@@ -1840,7 +2046,10 @@ void Yaz_Proxy::recv_GDU_more(bool normal)
     while (m_timeout_mode == timeout_normal && (g = m_in_queue.dequeue()))
     {
         m_timeout_mode = timeout_busy;
+        inc_ref();
         recv_GDU_reduce(g);
+        if (dec_ref())
+            break;
     }
 }
 
@@ -1863,7 +2072,7 @@ void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
         if (apdu->which == Z_APDU_presentRequest)
         {
             Z_PresentRequest *pr = apdu->u.presentRequest;
-            if (pr->numberOfRecordsRequested && 
+            if (pr->numberOfRecordsRequested &&
                 *pr->numberOfRecordsRequested > m_max_record_retrieve)
                 *pr->numberOfRecordsRequested = m_max_record_retrieve;
         }
@@ -1874,8 +2083,6 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
 {
     if (apdu->which == Z_APDU_initRequest)
     {
-        yaz_log(YLOG_LOG, "%shandle_charset_lang_negotiation", 
-                m_session_str);
         if (m_initRequest_options &&
             !ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel) &&
             (m_proxy_negotiation_charset || m_proxy_negotiation_lang))
@@ -1884,7 +2091,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
             // client's side. OK. The proxy negotiation
             // in use, only.
             Z_InitRequest *initRequest = apdu->u.initRequest;
-            Z_OtherInformation **otherInfo;  
+            Z_OtherInformation **otherInfo;
             Z_OtherInformationUnit *oi;
             get_otherInfoAPDU(apdu, &otherInfo);
             oi = update_otherInformation(otherInfo, 1, NULL, 0, 0);
@@ -1892,7 +2099,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
             {
                 ODR_MASK_SET(initRequest->options,
                     Z_Options_negotiationModel);
-                oi->which = Z_OtherInfo_externallyDefinedInfo;    
+                oi->which = Z_OtherInfo_externallyDefinedInfo;
                 oi->information.externallyDefinedInfo =
                 yaz_set_proposal_charneg(odr_encode(),
                     (const char**)&m_proxy_negotiation_charset,
@@ -1912,7 +2119,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
                     m_charset_converter->get_target_query_charset());
             Z_InitRequest *initRequest = apdu->u.initRequest;
             Z_CharSetandLanguageNegotiation *negotiation =
-                yaz_get_charneg_record (initRequest->otherInfo);        
+                yaz_get_charneg_record (initRequest->otherInfo);
             if (negotiation &&
                 negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
             {
@@ -1952,7 +2159,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
                     m_charset_converter->set_client_charset_selected(selected);
                 }
                 nmem_destroy(nmem);
-                ODR_MASK_CLEAR(m_initRequest_options, 
+                ODR_MASK_CLEAR(m_initRequest_options,
                                Z_Options_negotiationModel);
                 yaz_del_charneg_record(&initRequest->otherInfo);
             }
@@ -1962,40 +2169,40 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
                         m_session_str);
             }
         }
+        else if (m_charset_converter->get_target_query_charset() &&
+            m_proxy_negotiation_default_charset)
+        {
+            m_charset_converter->
+                set_client_query_charset(m_proxy_negotiation_default_charset);
+        }
     }
     else if (apdu->which == Z_APDU_initResponse)
     {
-        Z_InitResponse *initResponse = apdu->u.initResponse;    
-        Z_OtherInformation **otherInfo;  
+        Z_InitResponse *initResponse = apdu->u.initResponse;
+        Z_OtherInformation **otherInfo;
+        get_otherInfoAPDU(apdu, &otherInfo);
         
-        if (ODR_MASK_GET(initResponse->options, Z_Options_negotiationModel))
+        Z_CharSetandLanguageNegotiation *charneg = 0;
+
+        if (otherInfo && *otherInfo && 
+            ODR_MASK_GET(initResponse->options, Z_Options_negotiationModel)
+            && (charneg = yaz_get_charneg_record(*otherInfo)))
         {
             char *charset = 0;
             char *lang = 0;
             int selected = 0;
-            
-            get_otherInfoAPDU(apdu, &otherInfo);
-            
-            if (!otherInfo && !(*otherInfo))
-                return;
-            
-            Z_CharSetandLanguageNegotiation *charneg =
-                yaz_get_charneg_record(*otherInfo);
-                
-            if (!charneg)
-                return;
-                                
+
             yaz_get_response_charneg(m_referenceId_mem, charneg,
                 &charset, &lang, &selected);
-                                
+
             yaz_log(YLOG_LOG, "%sAccepted charset - '%s' and lang - '%s'",
                 m_session_str, (charset)?charset:"none", (lang)?lang:"none");
-                
+
             if (m_initRequest_options &&
                 ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel))
             {
                 yaz_log(YLOG_LOG, "%sClient's negotiation record in use",
-                    m_session_str);          
+                    m_session_str);
             }
             else if (m_proxy_negotiation_charset || m_proxy_negotiation_lang)
             {
@@ -2007,7 +2214,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
 
                 // clear negotiation option.
                 ODR_MASK_CLEAR(initResponse->options, Z_Options_negotiationModel);
-                
+
                 // Delete negotiation (charneg-3) entry.
                 yaz_del_charneg_record(otherInfo);
             }
@@ -2021,7 +2228,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
             }
             else if (m_charset_converter->get_client_query_charset())
             {
-                Z_OtherInformation **otherInfo;  
+                Z_OtherInformation **otherInfo;
                 Z_OtherInformationUnit *oi;
                 get_otherInfoAPDU(apdu, &otherInfo);
                 oi = update_otherInformation(otherInfo, 1, NULL, 0, 0);
@@ -2079,7 +2286,7 @@ Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
         Z_RPNQuery *rpnquery = 0;
         Z_SearchRequest *sr = apdu->u.searchRequest;
         char *addinfo = 0;
-        
+
         yaz_log(YLOG_LOG, "%sCQL: %s", m_session_str,
                 sr->query->u.type_104->u.cql);
 
@@ -2198,9 +2405,6 @@ int Yaz_Proxy::handle_authentication(Z_APDU *apdu)
     else
         ret = cfg->client_authentication(m_default_target, 0, 0, 0,
                                          m_peername);
-
-    cfg->target_authentication(m_default_target, odr_encode(), req);
-
     return ret;
 }
 
@@ -2262,7 +2466,7 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
             m_marcxml_mode = marcxml;
             if (m_backend_type)
             {
-                
+
                 sr->preferredRecordSyntax =
                     yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
                                         m_backend_type);
@@ -2275,14 +2479,14 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
         else if (err)
         {
             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
-            
+
             new_apdu->u.searchResponse->referenceId = sr->referenceId;
             new_apdu->u.searchResponse->records =
                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
             *new_apdu->u.searchResponse->searchStatus = 0;
-            
+
             send_to_client(new_apdu);
-            
+
             return 0;
         }
         else if (m_backend_type)
@@ -2338,7 +2542,7 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
             m_marcxml_mode = marcxml;
             if (m_backend_type)
             {
-                
+
                 pr->preferredRecordSyntax =
                     yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
                                         m_backend_type);
@@ -2351,15 +2555,15 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
         else if (err)
         {
             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
-            
+
             new_apdu->u.presentResponse->referenceId = pr->referenceId;
             new_apdu->u.presentResponse->records =
                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
             *new_apdu->u.presentResponse->presentStatus =
                 Z_PresentStatus_failure;
-            
+
             send_to_client(new_apdu);
-            
+
             return 0;
         }
         else if (m_backend_type)
@@ -2393,7 +2597,7 @@ void Yaz_Proxy::srw_get_client(const char *db, const char **backend_db)
     {
         releaseClient();
     }
-    
+
     if (t)
     {
         xfree(m_default_target);
@@ -2407,39 +2611,41 @@ int Yaz_Proxy::file_access(Z_HTTP_Request *hreq)
     if (strcmp(hreq->method, "GET"))
         return 0;
     if (hreq->path[0] != '/')
-    {
-        yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
         return 0;
-    }
     const char *cp = hreq->path;
     while (*cp)
     {
         if (*cp == '/' && strchr("/.", cp[1]))
-        {
-            yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
             return 0;
-        }
         cp++;
     }
+
+    Yaz_ProxyConfig *cfg = check_reconfigure();
+
+    if (!cfg->get_file_access_info(hreq->path+1))
+        return 0;
+
     const char *fname = hreq->path+1;
     if (stat(fname, &sbuf))
     {
-        yaz_log(YLOG_WARN|YLOG_ERRNO, "%s: stat failed", fname);
+        yaz_log(YLOG_LOG|YLOG_ERRNO, "%sstat failed for %s", m_session_str,
+                fname);
         return 0;
     }
     if ((sbuf.st_mode & S_IFMT) != S_IFREG)
     {
-        yaz_log(YLOG_WARN, "%s: not a regular file", fname);
+        yaz_log(YLOG_LOG, "%sNot a regular file %s", m_session_str, fname);
         return 0;
     }
     if (sbuf.st_size > (off_t) 1000000)
     {
-        yaz_log(YLOG_WARN, "%s: too large for transfer", fname);
+        yaz_log(YLOG_WARN, "%sFile %s too large for transfer", m_session_str,
+                fname);
         return 0;
     }
-    
+
     ODR o = odr_encode();
-    Yaz_ProxyConfig *cfg = check_reconfigure();
+
     const char *ctype = cfg->check_mime_type(fname);
     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
@@ -2473,7 +2679,7 @@ int Yaz_Proxy::file_access(Z_HTTP_Request *hreq)
     recv_GDU_more(true);
     return 1;
 }
-        
+
 void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
 {
     if (m_s2z_odr_init)
@@ -2489,7 +2695,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
 
     m_http_keepalive = 0;
     m_http_version = 0;
-    if (!strcmp(hreq->version, "1.0")) 
+    if (!strcmp(hreq->version, "1.0"))
     {
         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
         if (v && !strcmp(v, "Keep-Alive"))
@@ -2508,12 +2714,20 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
         m_http_version = "1.1";
     }
 
+    const char *a = z_HTTP_header_lookup(hreq->headers, "Authorization");
+    char authorization_str[255];
+    *authorization_str = '\0';
+    if (a && strncasecmp(a, "Basic ", 6) == 0)
+        base64_decode(a + 6, authorization_str, 254);
+
     Z_SRW_PDU *srw_pdu = 0;
     Z_SOAP *soap_package = 0;
     char *charset = 0;
     Z_SRW_diagnostic *diagnostic = 0;
     int num_diagnostic = 0;
 
+    yaz_log(YLOG_LOG, "%s%s %s", m_session_str, hreq->method, hreq->path);
+
     if (file_access(hreq))
     {
         return;
@@ -2531,9 +2745,27 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
         m_s2z_present_apdu = 0;
 
         m_s2z_stylesheet = 0;
-        
+
+        Z_IdAuthentication *auth = NULL;
+        if (*authorization_str)
+        {
+            auth = (Z_IdAuthentication *) odr_malloc(m_s2z_odr_init, sizeof(Z_IdAuthentication));
+            auth->which = Z_IdAuthentication_idPass;
+            auth->u.idPass = (Z_IdPass *) odr_malloc(m_s2z_odr_init, sizeof(Z_IdPass));
+            auth->u.idPass->groupId = NULL;
+            char *p = strchr(authorization_str, ':');
+            if (p)
+            {
+                *p = '\0';
+                p++;
+                auth->u.idPass->password = odr_strdup(m_s2z_odr_init, p);
+            }
+            auth->u.idPass->userId = odr_strdup(m_s2z_odr_init, authorization_str);
+        }
+
         if (srw_pdu->which == Z_SRW_searchRetrieve_request)
         {
+
             Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
 
             const char *backend_db = srw_req->database;
@@ -2558,7 +2790,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
             if (srw_req->stylesheet)
                 m_s2z_stylesheet =
                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
-                                              
+
             // set packing for response records ..
             if (srw_req->recordPacking &&
                 !strcmp(srw_req->recordPacking, "xml"))
@@ -2572,7 +2804,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
                     yaz_srw_get(odr_encode(),
                                 Z_SRW_searchRetrieve_response);
                 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
-                
+
                 srw_res->diagnostics = diagnostic;
                 srw_res->num_diagnostics = num_diagnostic;
                 send_srw_response(srw_pdu);
@@ -2590,23 +2822,23 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
                 odr_malloc(m_s2z_odr_search, sizeof(char *));
             z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
                                                            backend_db);
-            
+
             // query transformation
             Z_Query *query = (Z_Query *)
                 odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
             z_searchRequest->query = query;
-            
+
             if (srw_req->query_type == Z_SRW_query_type_cql)
             {
-                Z_External *ext = (Z_External *) 
+                Z_External *ext = (Z_External *)
                     odr_malloc(m_s2z_odr_search, sizeof(*ext));
-                ext->direct_reference = 
+                ext->direct_reference =
                     odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
                 ext->indirect_reference = 0;
                 ext->descriptor = 0;
                 ext->which = Z_External_CQL;
                 ext->u.cql = srw_req->query.cql;
-                
+
                 query->which = Z_Query_type_104;
                 query->u.type_104 =  ext;
             }
@@ -2614,9 +2846,9 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
             {
                 Z_RPNQuery *RPNquery;
                 YAZ_PQF_Parser pqf_parser;
-                
+
                 pqf_parser = yaz_pqf_create ();
-                
+
                 RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
                                           srw_req->query.pqf);
                 if (!RPNquery)
@@ -2627,13 +2859,13 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
                     int ioff = off;
                     yaz_log(YLOG_LOG, "%*s^\n", ioff+4, "");
                     yaz_log(YLOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
-                    
+
                     send_to_srw_client_error(10, 0);
                     return;
                 }
                 query->which = Z_Query_type_1;
                 query->u.type_1 =  RPNquery;
-                
+
                 yaz_pqf_destroy (pqf_parser);
             }
             else
@@ -2673,9 +2905,9 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
                 }
                 else   // Z39.50 present
                 {
-                    m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, 
+                    m_s2z_present_apdu = zget_APDU(m_s2z_odr_search,
                                                    Z_APDU_presentRequest);
-                    Z_PresentRequest *z_presentRequest = 
+                    Z_PresentRequest *z_presentRequest =
                         m_s2z_present_apdu->u.presentRequest;
                     *z_presentRequest->resultSetStartPoint = start;
                     *z_presentRequest->numberOfRecordsRequested = max;
@@ -2688,8 +2920,8 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
                             (Z_RecordComposition *)
                             odr_malloc(m_s2z_odr_search,
                                        sizeof(Z_RecordComposition));
-                        z_presentRequest->recordComposition->which = 
-                            Z_RecordComp_simple;                    
+                        z_presentRequest->recordComposition->which =
+                            Z_RecordComp_simple;
                         z_presentRequest->recordComposition->u.simple =
                             mk_esn_from_schema(m_s2z_odr_search,
                                                srw_req->recordSchema);
@@ -2700,7 +2932,9 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
             {
                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
                                             Z_APDU_initRequest);
-                
+
+                m_s2z_init_apdu->u.initRequest->idAuthentication = auth;
+
                 // prevent m_initRequest_apdu memory from being grabbed
                 // in Yaz_Proxy::handle_incoming_Z_PDU
                 m_initRequest_apdu = m_s2z_init_apdu;
@@ -2743,6 +2977,8 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
             {
                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
                                             Z_APDU_initRequest);
+
+                m_s2z_init_apdu->u.initRequest->idAuthentication = auth;
                 
                 // prevent m_initRequest_apdu memory from being grabbed
                 // in Yaz_Proxy::handle_incoming_Z_PDU
@@ -2765,7 +3001,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
                 yaz_srw_get(odr_encode(),
                             Z_SRW_scan_response);
             Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
-            
+
             srw_res->diagnostics = diagnostic;
             srw_res->num_diagnostics = num_diagnostic;
             send_srw_response(srw_pdu);
@@ -2783,6 +3019,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
 
 void Yaz_Proxy::handle_init(Z_APDU *apdu)
 {
+
     Z_OtherInformation **oi;
     get_otherInfoAPDU(apdu, &oi);
 
@@ -2799,10 +3036,10 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu)
     {
         if (m_initRequest_mem)
             nmem_destroy(m_initRequest_mem);
-        
+
         m_initRequest_apdu = apdu;
         m_initRequest_mem = odr_extract_mem(odr_decode());
-        
+
         m_initRequest_preferredMessageSize = *apdu->u.initRequest->
             preferredMessageSize;
         *apdu->u.initRequest->preferredMessageSize = 1024*1024;
@@ -2812,13 +3049,13 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu)
 
         Z_CharSetandLanguageNegotiation *charSetandLangRecord =
             yaz_get_charneg_record(*oi);
-        
+
         // Save proposal charsets and langs.
         if (ODR_MASK_GET(apdu->u.initRequest->options,
-                         Z_Options_negotiationModel) 
+                         Z_Options_negotiationModel)
             && charSetandLangRecord)
         {
-            
+
             yaz_get_proposal_charneg(m_referenceId_mem,
                                      charSetandLangRecord,
                                      &m_initRequest_oi_negotiation_charsets,
@@ -2826,7 +3063,7 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu)
                                      &m_initRequest_oi_negotiation_langs,
                                      &m_initRequest_oi_negotiation_num_langs,
                                      &m_initRequest_oi_negotiation_selected);
-            
+
             for (int i = 0; i<m_initRequest_oi_negotiation_num_charsets; i++)
             {
                 yaz_log(YLOG_LOG, "%scharacters set proposal: %s",
@@ -2841,11 +3078,11 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu)
             }
             yaz_log(YLOG_LOG, "%sselected proposal: %d (boolean)",
                     m_session_str, m_initRequest_oi_negotiation_selected);
-        }       
+        }
         // save init options for the response..
         m_initRequest_options = apdu->u.initRequest->options;
-        
-        apdu->u.initRequest->options = 
+
+        apdu->u.initRequest->options =
             (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
                                        sizeof(Odr_bitmask));
         ODR_MASK_ZERO(apdu->u.initRequest->options);
@@ -2863,14 +3100,15 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu)
                        Z_Options_concurrentOperations);
         // make new version
         m_initRequest_version = apdu->u.initRequest->protocolVersion;
-        apdu->u.initRequest->protocolVersion = 
+        apdu->u.initRequest->protocolVersion =
             (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
                                        sizeof(Odr_bitmask));
         ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
-        
+
         for (i = 0; i<= 8; i++)
             ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
     }
+    handle_charset_lang_negotiation(apdu);
     if (m_client->m_init_flag)
     {
         if (handle_init_response_for_invalid_session(apdu))
@@ -2885,18 +3123,20 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu)
             apdu2->u.initResponse->referenceId =
                 apdu->u.initRequest->referenceId;
             apdu2->u.initResponse->options = m_client->m_initResponse_options;
-            apdu2->u.initResponse->protocolVersion = 
+            apdu2->u.initResponse->protocolVersion =
                 m_client->m_initResponse_version;
-            
+
+            handle_charset_lang_negotiation(apdu2);
+
+           if (m_timeout_mode == timeout_busy)
+               m_timeout_mode = timeout_normal;
             send_to_client(apdu2);
-            m_timeout_mode = timeout_normal;
             return;
         }
     }
     m_client->m_init_flag = 1;
-    
+
 #if USE_AUTH_MSG
-    yaz_log(YLOG_LOG, "%suse_auth_msg", m_session_str);
     Auth_Msg *m = new Auth_Msg;
     m->m_proxy = this;
     z_APDU(odr_encode(), &apdu, 0, "encode");
@@ -2936,7 +3176,10 @@ void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
         m_mem_invalid_session = odr_extract_mem(odr_decode());
         apdu = m_initRequest_apdu;     // but throw an init to the target
     }
-    
+
+    if (apdu->which == Z_APDU_searchRequest)
+        m_search_stat.add_bytes(1);
+
     // Determine our client.
     Z_OtherInformation **oi;
     get_otherInfoAPDU(apdu, &oi);
@@ -2951,11 +3194,11 @@ void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
         else
         {
             // Z39.50 just shutdown
-            delete this;
+            timeout(0);
             return;
         }
     }
-    
+
     m_client->m_server = this;
 
     if (apdu->which == Z_APDU_initRequest)
@@ -2967,7 +3210,7 @@ void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
 void Yaz_Proxy::handle_incoming_Z_PDU_2(Z_APDU *apdu)
 {
     handle_max_record_retrieve(apdu);
-    
+
     if (apdu)
         apdu = handle_syntax_validation(apdu);
 
@@ -2985,15 +3228,11 @@ void Yaz_Proxy::handle_incoming_Z_PDU_2(Z_APDU *apdu)
 
     if (!apdu)
     {
-        m_client->timeout(m_target_idletime);  // mark it active even 
+        m_client->timeout(m_target_idletime);  // mark it active even
         recv_GDU_more(true);
         // though we didn't use it
         return;
     }
-    // Add otherInformation entry in APDU if
-    // negotiation is in use.
-    if (apdu)
-        handle_charset_lang_negotiation(apdu);
 
     // delete other info construct completely if 0 elements
     Z_OtherInformation **oi;
@@ -3028,7 +3267,7 @@ void Yaz_Proxy::releaseClient()
     m_proxyTarget = 0;
     m_flag_invalid_session = 0;
     // only keep if keep_alive flag is set...
-    if (m_client && 
+    if (m_client &&
         m_client->m_pdu_recv < m_keepalive_limit_pdu &&
         m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
         m_client->m_waiting == 0)
@@ -3060,7 +3299,7 @@ void Yaz_Proxy::releaseClient()
                  m_session_str);
         assert (m_parent);
     }
-    else 
+    else
     {
         yaz_log (YLOG_LOG, "%sShutdown (client to proxy)",
                  m_session_str);
@@ -3069,18 +3308,8 @@ void Yaz_Proxy::releaseClient()
         m_parent->pre_init();
 }
 
-bool Yaz_Proxy::dec_ref(bool main_ptr)
+bool Yaz_Proxy::dec_ref()
 {
-    yaz_log(YLOG_LOG, "%sdec_ref count=%d", m_session_str, m_ref_count);
-
-    assert(m_ref_count > 0);
-    if (main_ptr)
-    {
-        if (m_main_ptr_dec)
-            return false;
-        m_main_ptr_dec = true;
-    }
-
     m_http_keepalive = 0;
 
     --m_ref_count;
@@ -3093,7 +3322,7 @@ bool Yaz_Proxy::dec_ref(bool main_ptr)
     return true;
 }
 
-const char *Yaz_ProxyClient::get_session_str() 
+const char *Yaz_ProxyClient::get_session_str()
 {
     if (!m_server)
         return "0 ";
@@ -3107,8 +3336,8 @@ void Yaz_ProxyClient::shutdown()
 
     if (m_server)
     {
-        m_waiting = 1;      // ensure it's released from Proxy in releaseClient
-        m_server->dec_ref(true);
+        m_waiting = 1;   // ensure it's released from Yaz_Proxy::releaseClient
+        m_server->dec_ref();
     }
     else
         delete this;
@@ -3117,14 +3346,12 @@ void Yaz_ProxyClient::shutdown()
 void Yaz_Proxy::failNotify()
 {
     inc_request_no();
-    yaz_log (YLOG_LOG, "%sConnection closed by client",
-             get_session_str());
-    dec_ref(true);
+    yaz_log (YLOG_LOG, "%sConnection closed by client", get_session_str());
+    dec_ref();
 }
 
 void Yaz_Proxy::send_response_fail_client(const char *addr)
 {
-    yaz_log(YLOG_LOG, "%ssend_close_response", get_session_str());
     if (m_http_version)
     {
         Z_SRW_diagnostic *diagnostic = 0;
@@ -3143,7 +3370,7 @@ void Yaz_ProxyClient::failNotify()
 {
     if (m_server)
         m_server->inc_request_no();
-    yaz_log (YLOG_LOG, "%sConnection closed by target %s", 
+    yaz_log (YLOG_LOG, "%sConnection closed by target %s",
              get_session_str(), get_hostname());
 
     if (m_server)
@@ -3176,6 +3403,7 @@ Yaz_ProxyClient::~Yaz_ProxyClient()
         m_next->m_prev = m_prev;
     m_waiting = 2;     // for debugging purposes only.
     odr_destroy(m_init_odr);
+    odr_destroy(m_idAuthentication_odr);
     delete m_last_query;
     xfree (m_last_resultSetId);
     xfree (m_cookie);
@@ -3185,7 +3413,7 @@ void Yaz_ProxyClient::pre_init_client()
 {
     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
     Z_InitRequest *req = apdu->u.initRequest;
-    
+
     int i;
     for (i = 0; i<= 24; i++)
         ODR_MASK_SET(req->options, i);
@@ -3212,7 +3440,7 @@ void Yaz_Proxy::pre_init()
     int i;
     const char *name = 0;
     const char *zurl_in_use[MAX_ZURL_PLEX];
-    int limit_bw, limit_pdu, limit_req, limit_search, limit_connect;
+    int limit_bw, limit_pdu, limit_req, limit_search;
     int target_idletime, client_idletime;
     int max_clients;
     int keepalive_limit_bw, keepalive_limit_pdu;
@@ -3233,9 +3461,9 @@ void Yaz_Proxy::pre_init()
 
     for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
                                           &limit_bw, &limit_pdu, &limit_req,
-                                          &limit_search, &limit_connect,
+                                          &limit_search,
                                           &target_idletime, &client_idletime,
-                                          &max_clients, 
+                                          &max_clients,
                                           &keepalive_limit_bw,
                                           &keepalive_limit_pdu,
                                           &pre_init,
@@ -3243,6 +3471,7 @@ void Yaz_Proxy::pre_init()
                                           &authentication,
                                           &negotiation_charset,
                                           &negotiation_lang,
+                                          0,
                                           0) ; i++)
     {
         if (pre_init)
@@ -3285,7 +3514,7 @@ void Yaz_Proxy::pre_init()
                         c->m_next->m_prev = &c->m_next;
                     m_clientPool = c;
                     c->m_prev = &m_clientPool;
-                    
+
                     if (m_log_mask & PROXY_LOG_APDU_SERVER)
                         c->set_APDU_yazlog(1);
                     else
@@ -3319,7 +3548,7 @@ void Yaz_Proxy::timeoutNotify()
             inc_request_no();
             m_in_queue.clear();
             yaz_log (YLOG_LOG, "%sTimeout (client to proxy)", m_session_str);
-            dec_ref(true);
+            dec_ref();
             break;
         case timeout_reduce:
             timeout(m_client_idletime);
@@ -3357,9 +3586,12 @@ void Yaz_ProxyClient::timeoutNotify()
 
     if (m_server)
         m_server->send_response_fail_client(get_hostname());
+
+    Yaz_Proxy *proxy_root = m_root;
+
     shutdown();
 
-    m_root->pre_init();
+    proxy_root->pre_init();
 }
 
 Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable,
@@ -3389,13 +3621,16 @@ Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable,
     m_seqno = 0;
     m_target_idletime = 600;
     m_root = parent;
+    m_idAuthentication_odr = odr_createmem(ODR_ENCODE);
+    m_idAuthentication_ber_buf = 0;
+    m_idAuthentication_ber_size = 0;
 }
 
 const char *Yaz_Proxy::option(const char *name, const char *value)
 {
     if (!strcmp (name, "optimize")) {
         if (value) {
-            xfree (m_optimize); 
+            xfree (m_optimize);
             m_optimize = xstrdup (value);
         }
         return m_optimize;
@@ -3449,15 +3684,26 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
         m_initResponse = apdu;
         m_initResponse_options = apdu->u.initResponse->options;
         m_initResponse_version = apdu->u.initResponse->protocolVersion;
-        m_initResponse_preferredMessageSize = 
+        m_initResponse_preferredMessageSize =
             *apdu->u.initResponse->preferredMessageSize;
-        m_initResponse_maximumRecordSize = 
+        m_initResponse_maximumRecordSize =
             *apdu->u.initResponse->maximumRecordSize;
 
         Z_InitResponse *ir = apdu->u.initResponse;
-        char *im0 = ir->implementationName;
+       
+        // apply YAZ Proxy version
+        char *imv0 = ir->implementationVersion;
+        char *imv1 = (char*)
+            odr_malloc(m_init_odr, 20 + (imv0 ? strlen(imv0) : 0));
+        *imv1 = '\0';
+        if (imv0)
+            strcat(imv1, imv0);
+        strcat(imv1, "/" VERSION);
+        ir->implementationVersion = imv1;
         
-        char *im1 = (char*) 
+        // apply YAZ Proxy implementation name
+        char *im0 = ir->implementationName;
+        char *im1 = (char*)
             odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
         *im1 = '\0';
         if (im0)
@@ -3481,7 +3727,7 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
         if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
         {
             m_last_ok = 1;
-            
+
             if (sr->records && sr->records->which == Z_Records_DBOSD)
             {
                 m_cache.add(odr_decode(),
@@ -3505,7 +3751,7 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
             sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
             apdu = new_apdu;
         }
-        if (pr->records && 
+        if (pr->records &&
             pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
         {
             m_cache.add(odr_decode(),
@@ -3553,9 +3799,9 @@ int Yaz_Proxy::server(const char *addr)
     int r = Z_Assoc::server(addr);
     if (!r)
     {
-        yaz_log(YLOG_LOG, "%sStarted proxy " 
+        yaz_log(YLOG_LOG, "%sStarted proxy "
 #ifdef VERSION
-            VERSION 
+            VERSION
 #endif
             " on %s", m_session_str, addr);
         timeout(1);
@@ -3563,6 +3809,47 @@ int Yaz_Proxy::server(const char *addr)
     return r;
 }
 
+void Yaz_Proxy::base64_decode(const char *base64, char *buf, int buf_len)
+{
+    const char *base64_chars =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    int len = strlen(base64);
+    int buf_pos = 0;
+    int index = 1;
+
+    for (int pos = 0; pos <= len; pos++)
+    {
+        if (base64[pos] == '=' || buf_pos + 1 >= buf_len)
+            break;
+
+        const char *ch_ptr = strchr(base64_chars, base64[pos]);
+        if (!ch_ptr)
+            break;
+        char ch = (char) (ch_ptr - base64_chars);
+        switch (index)
+        {
+            case 1:
+                buf[buf_pos] = ch << 2;
+                break;
+            case 2:
+                buf[buf_pos++] += (ch & 0x30) >> 4;
+                buf[buf_pos] = (ch & 0x0f) << 4;
+                break;
+            case 3:
+                buf[buf_pos++] += (ch & 0x3c) >> 2;
+                buf[buf_pos] = (ch & 0x03) << 6;
+                break;
+            case 4:
+                buf[buf_pos++] += ch;
+        }
+        if (index < 4)
+            index++;
+        else
+            index = 1;
+    }
+    buf[buf_pos] = '\0';
+}
+
 /*
  * Local variables:
  * c-basic-offset: 4