Using yaz_daemon rather than own code. Fixed bug #2060.
[yazproxy-moved-to-github.git] / src / yaz-proxy.cpp
index 9915853..258c5b4 100644 (file)
@@ -1,7 +1,7 @@
-/* $Id: yaz-proxy.cpp,v 1.36 2005-09-12 20:09:14 adam Exp $
-   Copyright (c) 1998-2005, Index Data.
+/* $Id: yaz-proxy.cpp,v 1.78 2008-02-21 09:33:23 adam Exp $
+   Copyright (c) 1998-2007, 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
@@ -51,14 +51,16 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include <yaz/pquery.h>
 #include <yaz/otherinfo.h>
 #include <yaz/charneg.h>
+#include <yaz/oid_db.h>
 #include "msg-thread.h"
 
 using namespace yazpp_1;
 
-#define USE_AUTH_MSG 1
+#ifdef WIN32
+#define strncasecmp _strnicmp
+#endif
 
-#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 +82,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,34 +98,28 @@ 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);
-    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
-        yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU failed");
     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 failed");
         m_proxy->result_authentication(apdu, m_ret);
     }
     delete this;
 }
 
-#endif
-
 void Yaz_Proxy::result_authentication(Z_APDU *apdu, int ret)
 {
     if (apdu == 0 || ret == 0)
@@ -135,7 +130,16 @@ void Yaz_Proxy::result_authentication(Z_APDU *apdu, int ret)
         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 +196,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 +213,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;
@@ -219,9 +225,13 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
     m_session_no = 0;
     m_bytes_sent = 0;
     m_bytes_recv = 0;
-    m_bw_hold_PDU = 0;
     m_bw_max = 0;
     m_pdu_max = 0;
+    m_search_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;
@@ -241,7 +251,7 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
     m_schema = 0;
     m_backend_type = 0;
     m_backend_charset = 0;
-    m_frontend_type = 0;
+    m_frontend_type[0] = -1;
     m_initRequest_apdu = 0;
     m_initRequest_mem = 0;
     m_initRequest_preferredMessageSize = 0;
@@ -279,7 +289,9 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
         low_socket_open();
     m_my_thread = 0;
     m_ref_count = 1;
+    m_main_ptr_dec = false;
     m_peername = 0;
+    m_num_msg_threads = 0;
 }
 
 void Yaz_Proxy::inc_ref()
@@ -299,10 +311,11 @@ 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);
 
-#if HAVE_XSLT
+#if YAZ_HAVE_XSLT
     if (m_stylesheet_xsp)
         xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
 #endif
@@ -339,7 +352,13 @@ 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_num_msg_threads);
+        m_connect.set_period(period);
+    }
     return r;
 }
 
@@ -351,10 +370,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);
@@ -363,6 +385,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()
@@ -374,7 +399,6 @@ Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
     if (m_reconfig_flag)
     {
         yaz_log(YLOG_LOG, "reconfigure");
-        yaz_log_reopen();
         if (m_config_fname && cfg)
         {
             yaz_log(YLOG_LOG, "reconfigure config %s", m_config_fname);
@@ -384,7 +408,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_num_msg_threads);
+                m_connect.set_period(period);
             }
         }
         else
@@ -398,8 +426,25 @@ 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 (!peername)
+        peername = "nullpeer";
+
+    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);
@@ -407,52 +452,51 @@ 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;
+    new_proxy->m_session_no = m_session_no;
+    new_proxy->m_num_msg_threads = m_num_msg_threads;
+
+#if 0
+    // in case we want to watch a particular client..
+    if (!strcmp(peername, "tcp:163.121.19.82")) // NIS GROUP
+        new_proxy->m_log_mask = 255;
+#endif
+
     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);
+    if (!m_my_thread && m_num_msg_threads > 0)
+    {
+        yaz_log (YLOG_LOG, "%sStarting message thread management. number=%d",
+                 session_str, m_num_msg_threads);
+        m_my_thread = new Msg_Thread(m_socket_observable, m_num_msg_threads);
+    }
     new_proxy->m_my_thread = m_my_thread;
     return new_proxy;
 }
 
 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
 {
-    int oid[OID_SIZE];
-    Z_OtherInformationUnit *oi;
-    struct oident ent;
-    ent.proto = PROTO_Z3950;
-    ent.oclass = CLASS_USERINFO;
-    ent.value = (oid_value) VAL_COOKIE;
-    assert (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)
+    Z_OtherInformationUnit *oi =
+        update_otherInformation(otherInfo, 0, yaz_oid_userinfo_cookie, 1, 1);
+    
+    if (oi && oi->which == Z_OtherInfo_characterInfo)
         return oi->information.characterInfo;
     return 0;
 }
+
 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
 {
-    int oid[OID_SIZE];
-    Z_OtherInformationUnit *oi;
-    struct oident ent;
-    ent.proto = PROTO_Z3950;
-    ent.oclass = CLASS_USERINFO;
-    ent.value = (oid_value) VAL_PROXY;
-    if (oid_ent_to_oid (&ent, oid) &&
-        (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
-        oi->which == Z_OtherInfo_characterInfo)
+    Z_OtherInformationUnit *oi =
+        update_otherInformation(otherInfo, 0, yaz_oid_userinfo_proxy, 1, 1);
+    
+    if (oi && oi->which == Z_OtherInfo_characterInfo)
         return oi->information.characterInfo;
     return 0;
 }
@@ -493,7 +537,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];
@@ -508,7 +552,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];
@@ -530,6 +574,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)
@@ -537,6 +582,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_target_idletime, &client_idletime,
                                  &parent->m_max_clients,
                                  &m_keepalive_limit_bw,
@@ -545,19 +591,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])
@@ -592,7 +645,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;
@@ -605,7 +658,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)
@@ -617,10 +670,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.
@@ -629,9 +679,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",
@@ -647,9 +698,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;
             }
         }
@@ -661,14 +712,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;
@@ -695,8 +738,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
             {
@@ -711,12 +753,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
@@ -763,6 +805,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;
@@ -773,7 +817,6 @@ void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
     int i;
     for (i = 0; i<num; i++)
     {
-        oident *ent;
         Z_DefaultDiagFormat *r;
         Z_DiagRec *p = pp[i];
         if (p->which != Z_DiagRec_defaultFormat)
@@ -783,9 +826,6 @@ void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
         }
         else
             r = p->u.defaultFormat;
-        if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
-            ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
-            yaz_log(YLOG_LOG, "%sError unknown diagnostic set", m_session_str);
         switch (r->which)
         {
         case Z_DefaultDiagFormat_v2Addinfo:
@@ -822,7 +862,7 @@ int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu)
 
 void Yaz_Proxy::convert_xsl_delay()
 {
-#if HAVE_XSLT
+#if YAZ_HAVE_XSLT
     Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset];
     if (npr->which == Z_NamePlusRecord_databaseRecord)
     {
@@ -836,7 +876,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,
@@ -847,11 +887,10 @@ 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 = 
-                    z_ext_record(odr_encode(), VAL_TEXT_XML,
-                                 (char*) out_buf, out_len);
+                    u.databaseRecord =
+                    z_ext_record_oid(odr_encode(), yaz_oid_recsyn_xml,
+                                     (char*) out_buf, out_len);
                 xmlFree(out_buf);
                 xmlFreeDoc(res);
             }
@@ -865,7 +904,7 @@ void Yaz_Proxy::convert_xsl_delay()
     {
         m_timeout_mode = timeout_normal;
         m_stylesheet_nprl = 0;
-#if HAVE_XSLT
+#if YAZ_HAVE_XSLT
         if (m_stylesheet_xsp)
             xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
 #endif
@@ -879,7 +918,7 @@ void Yaz_Proxy::convert_xsl_delay()
 
 void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p)
 {
-    if (m_frontend_type != VAL_NONE)
+    if (m_frontend_type[0] != -1)
     {
         int i;
         for (i = 0; i < p->num_records; i++)
@@ -925,10 +964,10 @@ void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p)
                                 }
                             }
                             npr->u.databaseRecord =
-                                z_ext_record(odr_encode(),
-                                             m_frontend_type,
-                                             converted,
-                                             strlen(converted));
+                                z_ext_record_oid(odr_encode(),
+                                                 m_frontend_type,
+                                                 converted,
+                                                 strlen(converted));
                             free(converted);
                         }
                         else
@@ -940,10 +979,10 @@ void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p)
 #endif
 /* HAVE_USEMARCON */
                     npr->u.databaseRecord =
-                        z_ext_record(odr_encode(),
-                                     m_frontend_type,
-                                     (char*) r->u.octet_aligned->buf,
-                                     r->u.octet_aligned->len);
+                        z_ext_record_oid(odr_encode(),
+                                         m_frontend_type,
+                                         (char*) r->u.octet_aligned->buf,
+                                         r->u.octet_aligned->len);
                 }
             }
         }
@@ -953,7 +992,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();
@@ -972,36 +1010,36 @@ void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p,
             if (npr->which == Z_NamePlusRecord_databaseRecord)
             {
                 Z_External *r = npr->u.databaseRecord;
-                oident *ent = oid_getentbyoid(r->direct_reference);
-                if (!ent || ent->value == VAL_NONE)
+                const Odr_oid *oid = r->direct_reference;
+                if (!oid)
                     continue;
 
-                if (ent->value == VAL_SUTRS)
+                if (!oid_oidcmp(oid, yaz_oid_recsyn_sutrs))
                 {
                     WRBUF w = wrbuf_alloc();
 
                     wrbuf_iconv_write(w, cd,  (char*) r->u.octet_aligned->buf,
                                       r->u.octet_aligned->len);
                     npr->u.databaseRecord =
-                        z_ext_record(odr_encode(), ent->value, wrbuf_buf(w),
-                                     wrbuf_len(w));
-                    wrbuf_free(w, 1);
+                        z_ext_record_oid(odr_encode(), oid, wrbuf_buf(w),
+                                         wrbuf_len(w));
+                    wrbuf_destroy(w);
                 }
-                else if (ent->value == VAL_TEXT_XML)
+                else if (!oid_oidcmp(oid, yaz_oid_recsyn_xml))
                 {
                     ;
                 }
                 else if (r->which == Z_External_octet)
                 {
-                    int rlen;
-                    char *result;
+                    size_t rlen;
+                    const char *result;
                     if (yaz_marc_decode_buf(mt,
                                             (char*) r->u.octet_aligned->buf,
                                             r->u.octet_aligned->len,
                                             &result, &rlen))
                     {
                         npr->u.databaseRecord =
-                            z_ext_record(odr_encode(), ent->value, result, rlen);
+                            z_ext_record_oid(odr_encode(), oid, result, rlen);
                         yaz_log(YLOG_LOG, "%sRecoding MARC record",
                                 m_session_str);
                     }
@@ -1012,10 +1050,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,
@@ -1038,23 +1072,23 @@ void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p,
             {
                 WRBUF w = wrbuf_alloc();
 
-                yaz_display_OPAC(w, r->u.opac, 0);
-                npr->u.databaseRecord = z_ext_record(
-                    odr_encode(), VAL_TEXT_XML,
-                    wrbuf_buf(w), wrbuf_len(w)
-                    );
-                wrbuf_free(w, 1);
+                yaz_opac_decode_wrbuf(mt, r->u.opac, w);
+                npr->u.databaseRecord = z_ext_record_oid(
+                    odr_encode(), yaz_oid_recsyn_xml,
+                    wrbuf_buf(w), wrbuf_len(w));
+                wrbuf_destroy(w);
             }
             else if (r->which == Z_External_octet)
             {
-                int rlen;
-                char *result;
+                size_t rlen;
+                const char *result;
                 if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
                                         r->u.octet_aligned->len,
                                         &result, &rlen))
                 {
                     npr->u.databaseRecord =
-                        z_ext_record(odr_encode(), VAL_TEXT_XML, result, rlen);
+                        z_ext_record_oid(odr_encode(), yaz_oid_recsyn_xml,
+                                         result, rlen);
                 }
             }
         }
@@ -1094,7 +1128,11 @@ int Yaz_Proxy::send_http_response(int code)
         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
     else
         timeout(0);
-    
+    if (code == 401)
+        z_HTTP_header_add(o, &hres->headers, "WWW-Authenticate", 
+                          "Basic realm=\"YAZ Proxy\"");
+
+
     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
     {
         yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
@@ -1105,14 +1143,17 @@ int Yaz_Proxy::send_http_response(int code)
     m_bytes_sent += len;
     m_bw_stat.add_bytes(len);
     logtime();
+
+    recv_GDU_more(true);
+
     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);
@@ -1121,18 +1162,20 @@ 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
+#if YAZ_HAVE_XSLT
         {"http://www.loc.gov/zing/srw/", 0,
          (Z_SOAP_fun) yaz_srw_codec},
 #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;
@@ -1151,20 +1194,20 @@ int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
     m_bytes_sent += len;
     m_bw_stat.add_bytes(len);
     logtime();
+
+    recv_GDU_more(true);
+
     return r;
 }
 
 int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
 {
     ODR o = odr_encode();
-    Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
-    Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
-
-    srw_res->num_diagnostics = 1;
-    srw_res->diagnostics = (Z_SRW_diagnostic *)
-        odr_malloc(o, sizeof(*srw_res->diagnostics));
-    yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
-    return send_srw_response(srw_pdu);
+    Z_SRW_diagnostic *diagnostic = (Z_SRW_diagnostic *)
+        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, srw_error == 3 ? 401 : 200);
 }
 
 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
@@ -1177,7 +1220,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;
 }
@@ -1209,12 +1252,13 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
                 continue;
             }
             Z_External *r = npr->u.databaseRecord;
-            oident *ent = oid_getentbyoid(r->direct_reference);
-            if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
+
+            if (r->which == Z_External_octet 
+                && !oid_oidcmp(r->direct_reference, yaz_oid_recsyn_xml))
             {
                 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);
@@ -1238,7 +1282,19 @@ 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 http_code /* = 200 */)
+{
+    ODR o = odr_encode();
+    Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
+    Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
+
+    srw_res->num_diagnostics = num_diagnostics;
+    srw_res->diagnostics = diagnostics;
+    return send_srw_response(srw_pdu, http_code);
 }
 
 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
@@ -1299,9 +1355,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
@@ -1319,7 +1375,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;
@@ -1349,7 +1405,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;
@@ -1381,7 +1437,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)
             {
@@ -1415,7 +1471,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
@@ -1436,10 +1492,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);
@@ -1449,11 +1505,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);
@@ -1463,7 +1519,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(),
@@ -1478,7 +1534,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;
@@ -1491,6 +1547,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;
@@ -1522,8 +1615,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;
         }
@@ -1533,7 +1639,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;
             }
@@ -1547,14 +1656,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;
@@ -1576,10 +1685,25 @@ 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);
 
-    char query_str[120];
+    // 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[4096];
     this_query->print(query_str, sizeof(query_str)-1);
     yaz_log(YLOG_LOG, "%sSearch %s", m_session_str, query_str);
 
@@ -1607,7 +1731,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))
             {
@@ -1617,10 +1741,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*)
@@ -1670,7 +1794,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 *)
@@ -1688,10 +1812,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*)
@@ -1739,9 +1863,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);
 }
 
@@ -1750,39 +1878,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)
@@ -1797,19 +2019,17 @@ void Yaz_Proxy::recv_GDU_reduce(GDU *gdu)
     /* uncomment to force a big reduce */
     m_timeout_mode = timeout_reduce;
     m_timeout_gdu = gdu;
-    // m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
     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;
-        // m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
         timeout(reduce);       // call us reduce seconds later
     }
     else
@@ -1824,14 +2044,17 @@ 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;
     }
 }
 
 void Yaz_Proxy::recv_GDU_normal(GDU *gdu)
 {
-    Z_GDU *apdu = gdu->get();
-    gdu->extract_odr_to(odr_decode());
+    Z_GDU *apdu = 0;
+    gdu->move_away_gdu(odr_decode(), &apdu);
     delete gdu;
 
     if (apdu->which == Z_GDU_Z3950)
@@ -1847,7 +2070,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;
         }
@@ -1858,8 +2081,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))
@@ -1868,7 +2089,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);
@@ -1876,7 +2097,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,
@@ -1896,7 +2117,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)
             {
@@ -1936,7 +2157,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);
             }
@@ -1946,40 +2167,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)
             {
@@ -1991,7 +2212,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);
             }
@@ -2005,7 +2226,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);
@@ -2013,9 +2234,10 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
                 {
                     ODR_MASK_SET(initResponse->options,
                                  Z_Options_negotiationModel);
-                    ODR_MASK_SET(m_initRequest_options,
-                                 Z_Options_negotiationModel);
-
+                    if (m_initRequest_options)
+                        ODR_MASK_SET(m_initRequest_options,
+                                     Z_Options_negotiationModel);
+                    
                     oi->which = Z_OtherInfo_externallyDefinedInfo;    
                     oi->information.externallyDefinedInfo =
                         yaz_set_response_charneg(
@@ -2044,8 +2266,7 @@ Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
     *err = error;
     rec->which = Z_Records_NSD;
     rec->u.nonSurrogateDiagnostic = dr;
-    dr->diagnosticSetId =
-        yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
+    dr->diagnosticSetId = odr_oiddup(odr, yaz_oid_diagset_bib_1);
     dr->condition = err;
     dr->which = Z_DefaultDiagFormat_v2Addinfo;
     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
@@ -2062,7 +2283,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);
 
@@ -2181,9 +2402,44 @@ int Yaz_Proxy::handle_authentication(Z_APDU *apdu)
     else
         ret = cfg->client_authentication(m_default_target, 0, 0, 0,
                                          m_peername);
+    return ret;
+}
 
-    cfg->target_authentication(m_default_target, odr_encode(), req);
+int Yaz_Proxy::handle_global_authentication(Z_APDU *apdu)
+{
+    if (apdu->which != Z_APDU_initRequest)
+        return 1;  // pass if no init request
+    Z_InitRequest *req = apdu->u.initRequest;
 
+    Yaz_ProxyConfig *cfg = check_reconfigure();
+    if (!cfg)
+        return 1;  // pass if no config
+
+    int ret;
+    if (req->idAuthentication == 0)
+    {
+        ret = cfg->global_client_authentication(0, 0, 0,
+                                                m_peername);
+    }
+    else if (req->idAuthentication->which == Z_IdAuthentication_idPass)
+    {
+        ret = cfg->global_client_authentication(
+            req->idAuthentication->u.idPass->userId,
+            req->idAuthentication->u.idPass->groupId,
+            req->idAuthentication->u.idPass->password,
+            m_peername);
+    }
+    else if (req->idAuthentication->which == Z_IdAuthentication_open)
+    {
+        char user[64], pass[64];
+        *user = '\0';
+        *pass = '\0';
+        sscanf(req->idAuthentication->u.open, "%63[^/]/%63s", user, pass);
+        ret = cfg->global_client_authentication(user, 0, pass,
+                                                m_peername);
+    }
+    else
+        ret = cfg->global_client_authentication(0, 0, 0, m_peername);
     return ret;
 }
 
@@ -2206,13 +2462,9 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
         }
 
         if (sr->preferredRecordSyntax)
-        {
-            struct oident *ent;
-            ent = oid_getentbyoid(sr->preferredRecordSyntax);
-            m_frontend_type = ent->value;
-        }
+            oid_oidcpy(m_frontend_type, sr->preferredRecordSyntax);
         else
-            m_frontend_type = VAL_NONE;
+            m_frontend_type[0] = -1;
 
         char *stylesheet_name = 0;
         if (cfg)
@@ -2227,7 +2479,7 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
         {
             m_parent->low_socket_close();
 
-#if HAVE_XSLT
+#if YAZ_HAVE_XSLT
             if (m_stylesheet_xsp)
                 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
@@ -2243,35 +2495,30 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
             sr->smallSetElementSetNames = 0;
             sr->mediumSetElementSetNames = 0;
             m_marcxml_mode = marcxml;
-            if (m_backend_type)
-            {
-                
-                sr->preferredRecordSyntax =
-                    yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
-                                        m_backend_type);
-            }
-            else
-                sr->preferredRecordSyntax =
-                    yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN,
-                                           VAL_USMARC);
+            sr->preferredRecordSyntax =
+                yaz_string_to_oid_odr(
+                    yaz_oid_std(), CLASS_RECSYN,
+                    m_backend_type ? m_backend_type : "usmarc", 
+                    odr_encode());
         }
         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)
         {
             sr->preferredRecordSyntax =
-                yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type);
+                yaz_string_to_oid_odr(yaz_oid_std(), CLASS_RECSYN, 
+                                      m_backend_type, odr_encode());
         }
     }
     else if (apdu->which == Z_APDU_presentRequest)
@@ -2282,13 +2529,9 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
         Yaz_ProxyConfig *cfg = check_reconfigure();
 
         if (pr->preferredRecordSyntax)
-        {
-            struct oident *ent;
-            ent = oid_getentbyoid(pr->preferredRecordSyntax);
-            m_frontend_type = ent->value;
-        }
+            oid_oidcpy(m_frontend_type, pr->preferredRecordSyntax);
         else
-            m_frontend_type = VAL_NONE;
+            m_frontend_type[0] = -1;
 
         char *stylesheet_name = 0;
         if (cfg)
@@ -2304,7 +2547,7 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
         {
             m_parent->low_socket_close();
 
-#if HAVE_XSLT
+#if YAZ_HAVE_XSLT
             if (m_stylesheet_xsp)
                 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
@@ -2319,36 +2562,33 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
         {
             pr->recordComposition = 0;
             m_marcxml_mode = marcxml;
-            if (m_backend_type)
-            {
-                
-                pr->preferredRecordSyntax =
-                    yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
-                                        m_backend_type);
-            }
-            else
-                pr->preferredRecordSyntax =
-                    yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN,
-                                           VAL_USMARC);
+
+            pr->preferredRecordSyntax =
+                yaz_string_to_oid_odr(
+                    yaz_oid_std(), CLASS_RECSYN,
+                    m_backend_type ? m_backend_type : "usmarc", 
+                    odr_encode());
         }
         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)
         {
             pr->preferredRecordSyntax =
-                yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type);
+                yaz_string_to_oid_odr(yaz_oid_std(),
+                                      CLASS_RECSYN, m_backend_type, 
+                                      odr_encode());
         }
     }
     return apdu;
@@ -2376,7 +2616,7 @@ void Yaz_Proxy::srw_get_client(const char *db, const char **backend_db)
     {
         releaseClient();
     }
-    
+
     if (t)
     {
         xfree(m_default_target);
@@ -2390,39 +2630,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;
@@ -2453,9 +2695,10 @@ int Yaz_Proxy::file_access(Z_HTTP_Request *hreq)
     }
     int len;
     send_GDU(gdu, &len);
+    recv_GDU_more(true);
     return 1;
 }
-        
+
 void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
 {
     if (m_s2z_odr_init)
@@ -2471,7 +2714,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"))
@@ -2490,12 +2733,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;
@@ -2514,8 +2765,53 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
 
         m_s2z_stylesheet = 0;
         
+        Z_IdAuthentication *auth = NULL;
+        if (srw_pdu->username && srw_pdu->password)
+        {
+            yaz_log(YLOG_LOG, "username/password: %s/%s\n",
+                    srw_pdu->username, srw_pdu->password);
+            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;
+            auth->u.idPass->password = odr_strdup(m_s2z_odr_init, srw_pdu->password);
+            auth->u.idPass->userId = odr_strdup(m_s2z_odr_init, srw_pdu->username);
+        }
+        else
+        {
+            if (*authorization_str)
+            {
+                yaz_log(YLOG_LOG, "authorization_str present: %s\n", 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);
+            }
+            else
+            {
+                // Use _client_ IP as shown in the log entries...!
+                yaz_log(YLOG_LOG, "No authorization_str present: use client IP: %s\n", m_peername);
+                
+                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;
+                   auth->u.idPass->password = NULL;
+                   auth->u.idPass->userId   = odr_strdup(m_s2z_odr_init, m_peername);
+            }
+        }              
+        
         if (srw_pdu->which == Z_SRW_searchRetrieve_request)
         {
+
             Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
 
             const char *backend_db = srw_req->database;
@@ -2540,7 +2836,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"))
@@ -2554,7 +2850,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);
@@ -2572,23 +2868,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;
             }
@@ -2596,9 +2892,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)
@@ -2606,15 +2902,16 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
                     const char *pqf_msg;
                     size_t off;
                     int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
-                    yaz_log(YLOG_LOG, "%*s^\n", off+4, "");
+                    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
@@ -2642,8 +2939,8 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
                     *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
 
                     z_searchRequest->preferredRecordSyntax =
-                        yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
-                                               VAL_TEXT_XML);
+                        odr_oiddup(m_s2z_odr_search, yaz_oid_recsyn_xml);
+
                     if (srw_req->recordSchema)
                     {
                         z_searchRequest->smallSetElementSetNames =
@@ -2654,23 +2951,23 @@ 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;
+
                     z_presentRequest->preferredRecordSyntax =
-                        yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
-                                               VAL_TEXT_XML);
+                        odr_oiddup(m_s2z_odr_search, yaz_oid_recsyn_xml);
                     if (srw_req->recordSchema)
                     {
                         z_presentRequest->recordComposition =
                             (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);
@@ -2681,7 +2978,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;
@@ -2724,6 +3023,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
@@ -2746,7 +3047,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);
@@ -2764,6 +3065,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);
 
@@ -2780,10 +3082,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;
@@ -2793,13 +3095,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,
@@ -2807,7 +3109,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",
@@ -2822,11 +3124,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);
@@ -2844,14 +3146,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))
@@ -2861,37 +3164,42 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu)
             Z_APDU *apdu2 = m_client->m_initResponse;
             apdu2->u.initResponse->otherInfo = 0;
             if (m_client->m_cookie && *m_client->m_cookie)
-                set_otherInformationString(apdu2, VAL_COOKIE, 1,
-                                           m_client->m_cookie);
+                set_otherInformationString(apdu2, yaz_oid_userinfo_cookie, 
+                                           1, m_client->m_cookie);
             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");
-    char *apdu_buf = odr_getbuf(odr_encode(), &m->m_apdu_len, 0);
-    m->m_apdu_buf = (char*) nmem_malloc(m->m_nmem, m->m_apdu_len);
-    memcpy(m->m_apdu_buf, apdu_buf, m->m_apdu_len);
-    odr_reset(odr_encode());
-
-    inc_ref();
-    m_my_thread->put(m);
-#else
-    int ret = handle_authentication(apdu);
-    result_authentication(apdu, ret);
-#endif
+
+    if (m_num_msg_threads && m_my_thread)
+    {
+        Auth_Msg *m = new Auth_Msg;
+        m->m_proxy = this;
+        z_APDU(odr_encode(), &apdu, 0, "encode");
+        char *apdu_buf = odr_getbuf(odr_encode(), &m->m_apdu_len, 0);
+        m->m_apdu_buf = (char*) nmem_malloc(m->m_nmem, m->m_apdu_len);
+        memcpy(m->m_apdu_buf, apdu_buf, m->m_apdu_len);
+        odr_reset(odr_encode());
+        
+        inc_ref();
+        m_my_thread->put(m);
+    }
+    else
+    {
+        int ret = handle_authentication(apdu);
+        result_authentication(apdu, ret);
+    }
 }
 
 void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
@@ -2917,7 +3225,27 @@ 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);
+
+    // Handle global authentication
+    if (!handle_global_authentication(apdu))
+    {
+        if (m_http_version)
+        {   // HTTP. Send unauthorized
+            send_http_response(401);
+            return;
+        }
+        else
+        {
+            // Z39.50 just shutdown
+            timeout(0);
+            return;
+        }
+        return;
+    }
+
     // Determine our client.
     Z_OtherInformation **oi;
     get_otherInfoAPDU(apdu, &oi);
@@ -2932,11 +3260,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)
@@ -2948,7 +3276,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);
 
@@ -2966,15 +3294,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;
@@ -3009,7 +3333,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)
@@ -3041,7 +3365,7 @@ void Yaz_Proxy::releaseClient()
                  m_session_str);
         assert (m_parent);
     }
-    else 
+    else
     {
         yaz_log (YLOG_LOG, "%sShutdown (client to proxy)",
                  m_session_str);
@@ -3052,8 +3376,7 @@ void Yaz_Proxy::releaseClient()
 
 bool Yaz_Proxy::dec_ref()
 {
-    yaz_log(YLOG_LOG, "%sdec_ref count=%d", m_session_str, m_ref_count);
-    assert(m_ref_count > 0);
+    m_http_keepalive = 0;
 
     --m_ref_count;
     if (m_ref_count > 0)
@@ -3065,7 +3388,7 @@ bool Yaz_Proxy::dec_ref()
     return true;
 }
 
-const char *Yaz_ProxyClient::get_session_str() 
+const char *Yaz_ProxyClient::get_session_str()
 {
     if (!m_server)
         return "0 ";
@@ -3079,7 +3402,7 @@ void Yaz_ProxyClient::shutdown()
 
     if (m_server)
     {
-        m_waiting = 1;      // ensure it's released from Proxy in releaseClient
+        m_waiting = 1;   // ensure it's released from Yaz_Proxy::releaseClient
         m_server->dec_ref();
     }
     else
@@ -3089,17 +3412,35 @@ void Yaz_ProxyClient::shutdown()
 void Yaz_Proxy::failNotify()
 {
     inc_request_no();
-    yaz_log (YLOG_LOG, "%sConnection closed by client",
-             get_session_str());
+    yaz_log (YLOG_LOG, "%sConnection closed by client", get_session_str());
     dec_ref();
 }
 
+void Yaz_Proxy::send_response_fail_client(const char *addr)
+{
+    if (m_http_version)
+    {
+        Z_SRW_diagnostic *diagnostic = 0;
+        int num_diagnostic = 0;
+        
+        yaz_add_srw_diagnostic(odr_encode(),
+                               &diagnostic, &num_diagnostic,
+                               YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, addr);
+        if (m_s2z_search_apdu)
+            send_srw_search_response(diagnostic, num_diagnostic);
+        else
+            send_srw_explain_response(diagnostic, num_diagnostic);
+    }            
+}
 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)
+        m_server->send_response_fail_client(get_hostname());
     shutdown();
 }
 
@@ -3128,6 +3469,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);
@@ -3137,7 +3479,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);
@@ -3164,7 +3506,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;
+    int limit_bw, limit_pdu, limit_req, limit_search;
     int target_idletime, client_idletime;
     int max_clients;
     int keepalive_limit_bw, keepalive_limit_pdu;
@@ -3185,8 +3527,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,
                                           &target_idletime, &client_idletime,
-                                          &max_clients, 
+                                          &max_clients,
                                           &keepalive_limit_bw,
                                           &keepalive_limit_pdu,
                                           &pre_init,
@@ -3194,6 +3537,7 @@ void Yaz_Proxy::pre_init()
                                           &authentication,
                                           &negotiation_charset,
                                           &negotiation_lang,
+                                          0,
                                           0) ; i++)
     {
         if (pre_init)
@@ -3236,7 +3580,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
@@ -3305,17 +3649,15 @@ void Yaz_ProxyClient::timeoutNotify()
 
     yaz_log (YLOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
              get_hostname());
-    m_waiting = 1;
-    m_root->pre_init();
-    if (m_server && m_init_flag)
-    {
-        // target timed out in a session that was properly initialized
-        // server object stay alive but we mark it as invalid so it
-        // gets initialized again
-        m_server->markInvalid();
-        m_server = 0;
-    }
+
+    if (m_server)
+        m_server->send_response_fail_client(get_hostname());
+
+    Yaz_Proxy *proxy_root = m_root;
+
     shutdown();
+
+    proxy_root->pre_init();
 }
 
 Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable,
@@ -3345,13 +3687,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;
@@ -3405,15 +3750,28 @@ 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);
+#ifdef VERSION
+        strcat(imv1, "/" VERSION);
+#endif
+        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)
@@ -3437,7 +3795,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(),
@@ -3461,7 +3819,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(),
@@ -3471,7 +3829,7 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
         }
     }
     if (m_cookie)
-        set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
+        set_otherInformationString(apdu, yaz_oid_userinfo_cookie, 1, m_cookie);
 
     Yaz_Proxy *server = m_server; // save it. send_to_client may destroy us
 
@@ -3509,9 +3867,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);
@@ -3519,6 +3877,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