Use size_t for NMEM's get_block. Change space-left check to
[yaz-moved-to-github.git] / src / zoom-c.c
index b5241f3..e5987f5 100644 (file)
@@ -1,11 +1,14 @@
 /*
- * Copyright (c) 2000-2004, Index Data
+ * Copyright (C) 1995-2005, Index Data ApS
  * See the file LICENSE for details.
  *
- * $Id: zoom-c.c,v 1.26 2004-04-06 17:47:24 adam Exp $
- *
- * ZOOM layer for C, connections, result sets, queries.
+ * $Id: zoom-c.c,v 1.43 2005-06-06 12:23:47 adam Exp $
+ */
+/**
+ * \file zoom-c.c
+ * \brief Implements ZOOM C interface.
  */
+
 #include <assert.h>
 #include <string.h>
 #include "zoom-p.h"
 #include <yaz/ill.h>
 #include <yaz/srw.h>
 
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
 #if HAVE_SYS_POLL_H
 #include <sys/poll.h>
 #endif
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef WIN32
+#include <winsock.h>
+#endif
+
+static int log_api = 0;
+static int log_details = 0;
 
 typedef enum {
     zoom_pending,
@@ -33,6 +51,17 @@ typedef enum {
 static zoom_ret ZOOM_connection_send_init (ZOOM_connection c);
 static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out);
 
+static void initlog()
+{
+    static int log_level_initialized = 0;
+    if (!log_level_initialized)
+    {
+        log_api = yaz_log_module_level("zoom");
+       log_details = yaz_log_module_level("zoomdetails");
+       log_level_initialized = 1;
+    }
+}
+
 static ZOOM_Event ZOOM_Event_create (int kind)
 {
     ZOOM_Event event = (ZOOM_Event) xmalloc (sizeof(*event));
@@ -91,6 +120,7 @@ static void set_dset_error (ZOOM_connection c, int error,
                             const char *addinfo, const char *addinfo2)
 {
     char *cp;
+
     xfree (c->addinfo);
     c->addinfo = 0;
     c->error = error;
@@ -104,15 +134,15 @@ static void set_dset_error (ZOOM_connection c, int error,
     }
     if (addinfo && addinfo2)
     {
-        c->addinfo = xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
+        c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
         strcpy(c->addinfo, addinfo);
         strcat(c->addinfo, addinfo2);
     }
     else if (addinfo)
         c->addinfo = xstrdup(addinfo);
     if (error)
-        yaz_log(LOG_DEBUG, "Error %s %s:%d %s %s",
-                c->host_port ? c->host_port : "<>", dset, error,
+        yaz_log(log_api, "%p set_dset_error %s %s:%d %s %s",
+                c, c->host_port ? c->host_port : "<>", dset, error,
                 addinfo ? addinfo : "",
                 addinfo2 ? addinfo2 : "");
 }
@@ -185,7 +215,6 @@ void ZOOM_connection_remove_task (ZOOM_connection c)
        switch (task->which)
        {
        case ZOOM_TASK_SEARCH:
-
            ZOOM_resultset_destroy (task->u.search.resultset);
            break;
        case ZOOM_TASK_RETRIEVE:
@@ -225,6 +254,10 @@ ZOOM_connection_create (ZOOM_options options)
 {
     ZOOM_connection c = (ZOOM_connection) xmalloc (sizeof(*c));
 
+    initlog();
+
+    yaz_log(log_api, "%p ZOOM_connection_create", c);
+
     c->proto = PROTO_Z3950;
     c->cs = 0;
     c->mask = 0;
@@ -270,54 +303,20 @@ static char **set_DatabaseNames (ZOOM_connection con, ZOOM_options options,
                                  int *num)
 {
     char **databaseNames;
-    const char *c;
-    int no = 2;
     const char *cp = ZOOM_options_get (options, "databaseName");
     
     if (!cp || !*cp)
     {
         if (strncmp (con->host_port, "unix:", 5) == 0)
-           cp = strchr (con->host_port+5, ':');
+           cp = strchr(con->host_port+5, ':');
        else
-           cp = strchr (con->host_port, '/');
+           cp = strchr(con->host_port, '/');
        if (cp)
            cp++;
     }
-    if (cp)
-    {
-       c = cp;
-       while ((c = strchr(c, '+')))
-       {
-           c++;
-           no++;
-       }
-    }
-    else
+    if (!cp)
        cp = "Default";
-    databaseNames = (char**)
-        odr_malloc (con->odr_out, no * sizeof(*databaseNames));
-    no = 0;
-    while (*cp)
-    {
-       c = strchr (cp, '+');
-       if (!c)
-           c = cp + strlen(cp);
-       else if (c == cp)
-       {
-           cp++;
-           continue;
-       }
-       /* cp ptr to first char of db name, c is char
-          following db name */
-       databaseNames[no] = (char*) odr_malloc (con->odr_out, 1+c-cp);
-       memcpy (databaseNames[no], cp, c-cp);
-       databaseNames[no++][c-cp] = '\0';
-       cp = c;
-       if (*cp)
-           cp++;
-    }
-    databaseNames[no] = NULL;
-    *num = no;
+    nmem_strsplit(con->odr_out->mem, "+", cp,  &databaseNames, num);
     return databaseNames;
 }
 
@@ -337,31 +336,45 @@ ZOOM_connection_connect(ZOOM_connection c,
     const char *val;
     ZOOM_task task;
 
+    initlog();
+
+    yaz_log(log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
+           c, host, portnum);
+
     if (c->cs)
     {
-        yaz_log (LOG_DEBUG, "reconnect");
+        yaz_log(log_details, "%p ZOOM_connection_connect reconnect ok", c);
         c->reconnect_ok = 1;
         return;
     }
-    yaz_log(LOG_DEBUG, "connect");
+    yaz_log(log_details, "%p ZOOM_connection_connect connect", c);
     xfree (c->proxy);
     val = ZOOM_options_get (c->options, "proxy");
     if (val && *val)
+    {
+       yaz_log(log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
        c->proxy = xstrdup (val);
+    }
     else
        c->proxy = 0;
 
     xfree (c->charset);
     val = ZOOM_options_get (c->options, "charset");
     if (val && *val)
+    {
+       yaz_log(log_details, "%p ZOOM_connection_connect charset=%s", c, val);
        c->charset = xstrdup (val);
+    }
     else
        c->charset = 0;
 
     xfree (c->lang);
     val = ZOOM_options_get (c->options, "lang");
     if (val && *val)
+    {
+       yaz_log(log_details, "%p ZOOM_connection_connect lang=%s", c, val);
        c->lang = xstrdup (val);
+    }
     else
        c->lang = 0;
 
@@ -379,13 +392,21 @@ ZOOM_connection_connect(ZOOM_connection c,
 
     val = ZOOM_options_get (c->options, "cookie");
     if (val && *val)
-        c->cookie_out = xstrdup (val);
+    { 
+       yaz_log(log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
+       c->cookie_out = xstrdup (val);
+    }
 
     val = ZOOM_options_get (c->options, "clientIP");
     if (val && *val)
+    {
+       yaz_log(log_details, "%p ZOOM_connection_connect clientIP=%s",
+               c, val);
         c->client_IP = xstrdup (val);
+    }
 
     c->async = ZOOM_options_get_bool (c->options, "async", 0);
+    yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
  
     set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
 
@@ -403,6 +424,7 @@ ZOOM_query_create(void)
 {
     ZOOM_query s = (ZOOM_query) xmalloc (sizeof(*s));
 
+    yaz_log(log_details, "%p ZOOM_query_create", s);
     s->refcount = 1;
     s->z_query = 0;
     s->sort_spec = 0;
@@ -419,7 +441,7 @@ ZOOM_query_destroy(ZOOM_query s)
        return;
 
     (s->refcount)--;
-    yaz_log (LOG_DEBUG, "ZOOM_query_destroy count=%d", s->refcount);
+    yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
     if (s->refcount == 0)
     {
        odr_destroy (s->odr);
@@ -436,9 +458,11 @@ ZOOM_query_prefix(ZOOM_query s, const char *str)
     s->z_query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
     if (!s->z_query->u.type_1)
     {
+       yaz_log(log_details, "%p ZOOM_query_prefix str=%s failed", s, str);
        s->z_query = 0;
        return -1;
     }
+    yaz_log(log_details, "%p ZOOM_query_prefix str=%s", s, str);
     return 0;
 }
 
@@ -460,6 +484,8 @@ ZOOM_query_cql(ZOOM_query s, const char *str)
     s->z_query->which = Z_Query_type_104;
     s->z_query->u.type_104 =  ext;
 
+    yaz_log(log_details, "%p ZOOM_query_cql str=%s", s, str);
+
     return 0;
 }
 
@@ -468,7 +494,12 @@ ZOOM_query_sortby(ZOOM_query s, const char *criteria)
 {
     s->sort_spec = yaz_sort_spec (s->odr, criteria);
     if (!s->sort_spec)
+    {
+       yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s failed",
+               s, criteria);
        return -1;
+    }
+    yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s", s, criteria);
     return 0;
 }
 
@@ -480,6 +511,7 @@ ZOOM_connection_destroy(ZOOM_connection c)
     ZOOM_resultset r;
     if (!c)
        return;
+    yaz_log(log_api, "%p ZOOM_connection_destroy", c);
     if (c->cs)
        cs_close (c->cs);
     for (r = c->resultsets; r; r = r->next)
@@ -508,8 +540,8 @@ void ZOOM_resultset_addref (ZOOM_resultset r)
     if (r)
     {
        (r->refcount)++;
-        yaz_log (LOG_DEBUG, "ZOOM_resultset_addref r=%p count=%d",
-                 r, r->refcount);
+        yaz_log(log_details, "%p ZOOM_resultset_addref count=%d",
+               r, r->refcount);
     }
 }
 
@@ -517,7 +549,9 @@ ZOOM_resultset ZOOM_resultset_create ()
 {
     ZOOM_resultset r = (ZOOM_resultset) xmalloc (sizeof(*r));
 
-    yaz_log (LOG_DEBUG, "ZOOM_resultset_create r = %p", r);
+    initlog();
+
+    yaz_log(log_details, "%p ZOOM_resultset_create", r);
     r->refcount = 1;
     r->size = 0;
     r->odr = odr_createmem (ODR_ENCODE);
@@ -555,6 +589,7 @@ ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
     ZOOM_task task;
     const char *cp;
 
+    yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
     r->r_sort_spec = q->sort_spec;
     r->query = q;
 
@@ -585,12 +620,12 @@ ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
     {
         if (!c->cs)
         {
-            yaz_log(LOG_DEBUG, "NO COMSTACK");
+            yaz_log(log_details, "ZOOM_connection_search: no comstack");
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(LOG_DEBUG, "PREPARE FOR RECONNECT");
+            yaz_log(log_details, "ZOOM_connection_search: reconnect");
             c->reconnect_ok = 1;
         }
     }
@@ -616,6 +651,8 @@ ZOOM_API(void)
     ZOOM_connection c = r->connection;
     ZOOM_task task;
 
+    yaz_log(log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
+           r, r, sort_type, sort_spec);
     if (!c)
        return;
 
@@ -623,12 +660,13 @@ ZOOM_API(void)
     {
         if (!c->cs)
         {
-            yaz_log(LOG_DEBUG, "NO COMSTACK");
+            yaz_log(log_details, "%p ZOOM_resultset_sort: no comstack", r);
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(LOG_DEBUG, "PREPARE FOR RECONNECT");
+            yaz_log(log_details, "%p ZOOM_resultset_sort: prepare reconnect",
+                   r);
             c->reconnect_ok = 1;
         }
     }
@@ -671,8 +709,8 @@ ZOOM_resultset_destroy(ZOOM_resultset r)
     if (!r)
         return;
     (r->refcount)--;
-    yaz_log (LOG_DEBUG, "ZOOM_resultset_destroy r = %p count=%d",
-             r, r->refcount);
+    yaz_log(log_details, "%p ZOOM_resultset_destroy r=%p count=%d",
+           r, r, r->refcount);
     if (r->refcount == 0)
     {
        ZOOM_resultset_cache_reset(r);
@@ -725,6 +763,8 @@ static void ZOOM_resultset_retrieve (ZOOM_resultset r,
 
     if (!r)
        return;
+    yaz_log(log_details, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
+           " count=%d", r, force_sync, start, count);
     c = r->connection;
     if (!c)
        return;
@@ -733,12 +773,13 @@ static void ZOOM_resultset_retrieve (ZOOM_resultset r,
     {
         if (!c->cs)
         {
-            yaz_log(LOG_DEBUG, "NO COMSTACK");
+            yaz_log(log_details, "%p ZOOM_resultset_retrieve: no comstack", r);
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(LOG_DEBUG, "PREPARE FOR RECONNECT");
+            yaz_log(log_details, "%p ZOOM_resultset_retrieve: prepare "
+                   "reconnect", r);
             c->reconnect_ok = 1;
         }
     }
@@ -772,6 +813,8 @@ ZOOM_resultset_records (ZOOM_resultset r, ZOOM_record *recs,
 
     if (!r)
        return ;
+    yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%d count=%d",
+           r, r, start, count);
     if (count && recs)
         force_present = 1;
     ZOOM_resultset_retrieve (r, force_present, start, count);
@@ -783,6 +826,19 @@ ZOOM_resultset_records (ZOOM_resultset r, ZOOM_record *recs,
     }
 }
 
+static void get_cert(ZOOM_connection c)
+{
+    char *cert_buf;
+    int cert_len;
+    
+    if (cs_get_peer_certificate_x509(c->cs, &cert_buf, &cert_len))
+    {
+        ZOOM_connection_option_setl(c, "sslPeerCert",
+                                   cert_buf, cert_len);
+       xfree(cert_buf);
+    }
+}
+
 static zoom_ret do_connect (ZOOM_connection c)
 {
     void *add;
@@ -793,9 +849,10 @@ static zoom_ret do_connect (ZOOM_connection c)
     else
        effective_host = c->host_port;
 
-    yaz_log (LOG_DEBUG, "do_connect host=%s", effective_host);
+    yaz_log(log_details, "%p do_connect effective_host=%s", c, effective_host);
 
-    assert (!c->cs);
+    if (c->cs)
+       cs_close(c->cs);
     c->cs = cs_create_host (effective_host, 0, &add);
 
     if (c->cs && c->cs->protocol == PROTO_HTTP)
@@ -806,7 +863,7 @@ static zoom_ret do_connect (ZOOM_connection c)
         c->proto = PROTO_HTTP;
         cs_get_host_args(c->host_port, &path);
         xfree(c->path);
-        c->path = xmalloc(strlen(path)+2);
+        c->path = (char*) xmalloc(strlen(path)+2);
         c->path[0] = '/';
         strcpy (c->path+1, path);
 #else
@@ -822,6 +879,7 @@ static zoom_ret do_connect (ZOOM_connection c)
         {
             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
             ZOOM_connection_put_event(c, event);
+           get_cert(c);
             if (c->proto == PROTO_Z3950)
                 ZOOM_connection_send_init(c);
             else
@@ -881,7 +939,7 @@ static void otherInfo_attach (ZOOM_connection c, Z_APDU *a, ODR out)
         val = ZOOM_options_get (c->options, buf);
         if (!val)
             break;
-        cp = strchr (val, ':');
+        cp = strchr(val, ':');
         if (!cp)
             continue;
         len = cp - val;
@@ -925,12 +983,12 @@ static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
            z_APDU(odr_pr, &a, 0, 0);
            odr_destroy(odr_pr);
        }
-        yaz_log (LOG_DEBUG, "encoding failed");
+        yaz_log(log_api, "%p encoding_APDU: encoding failed", c);
         set_ZOOM_error(c, ZOOM_ERROR_ENCODE, 0);
        odr_reset(out);
        return -1;
     }
-    
+    yaz_log(log_details, "%p encoding_APDU encoding OK", c);
     return 0;
 }
 
@@ -940,7 +998,7 @@ static zoom_ret send_APDU (ZOOM_connection c, Z_APDU *a)
     assert (a);
     if (encode_APDU(c, a, c->odr_out))
        return zoom_complete;
-    yaz_log(LOG_DEBUG, "send APDU type=%d", a->which);
+    yaz_log(log_details, "%p send APDU type=%d", c, a->which);
     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
     ZOOM_connection_put_event (c, event);
@@ -987,7 +1045,7 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
        ZOOM_options_get(c->options, "implementationName"),
        odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName));
 
-    version = odr_strdup(c->odr_out, "$Revision: 1.26 $");
+    version = odr_strdup(c->odr_out, "$Revision: 1.43 $");
     if (strlen(version) > 10)  /* check for unexpanded CVS strings */
        version[strlen(version)-2] = '\0';
     ireq->implementationVersion = odr_prepend(c->odr_out,
@@ -1045,7 +1103,7 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
     if (c->proxy)
        yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
                                 VAL_PROXY, 1, c->host_port);
-    if (c->charset||c->lang)
+    if (c->charset || c->lang)
     {
        Z_OtherInformation **oi;
        Z_OtherInformationUnit *oi_unit;
@@ -1054,14 +1112,26 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
        
        if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
        {
+            char **charsets_addresses = 0;
+            char **langs_addresses = 0;
+            int charsets_count = 0;
+            int langs_count = 0;
+          
+            if (c->charset)
+               nmem_strsplit_blank(c->odr_out->mem, c->charset,
+                                   &charsets_addresses, &charsets_count);
+            if (c->lang)
+               nmem_strsplit_blank(c->odr_out->mem, c->lang,
+                                   &langs_addresses, &langs_count);
             ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
-            
             oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
             oi_unit->information.externallyDefinedInfo =
-                yaz_set_proposal_charneg
-                (c->odr_out,
-                 (const char **)&c->charset, (c->charset) ? 1:0,
-                 (const char **)&c->lang, (c->lang) ? 1:0, 1);
+                yaz_set_proposal_charneg(c->odr_out,
+                                        (const char **) charsets_addresses,
+                                        charsets_count,
+                                        (const char **) langs_addresses,
+                                        langs_count, 
+                                        1);
        }
     }
     assert (apdu);
@@ -1078,7 +1148,7 @@ static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr)
     };
     ODR o = odr_createmem(ODR_ENCODE);
     int ret;
-    Z_SOAP *p = odr_malloc(o, sizeof(*p));
+    Z_SOAP *p = (Z_SOAP*) odr_malloc(o, sizeof(*p));
     Z_GDU *gdu;
     ZOOM_Event event;
 
@@ -1100,7 +1170,7 @@ static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr)
 
         if (cp0 && cp1)
         {
-            char *h = odr_malloc(c->odr_out, cp1 - cp0 + 1);
+            char *h = (char*) odr_malloc(c->odr_out, cp1 - cp0 + 1);
             memcpy (h, cp0, cp1 - cp0);
             h[cp1-cp0] = '\0';
             z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
@@ -1119,7 +1189,7 @@ static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr)
     z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
                       "SOAPAction", "\"\"");
     p->which = Z_SOAP_generic;
-    p->u.generic = odr_malloc(o, sizeof(*p->u.generic));
+    p->u.generic = (Z_SOAP_Generic *) odr_malloc(o, sizeof(*p->u.generic));
     p->u.generic->no = 0;
     p->u.generic->ns = 0;
     p->u.generic->p = sr;
@@ -1239,6 +1309,8 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
 
     r = c->tasks->u.search.resultset;
 
+    yaz_log(log_details, "%p ZOOM_connection_send_search set=%p", c, r);
+
     elementSetName =
        ZOOM_options_get (r->options, "elementSetName");
     smallSetElementSetName  =
@@ -1335,31 +1407,26 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
                     break;
             }
             r->setname = xstrdup (setname);
-            yaz_log (LOG_DEBUG, "allocating set %s", r->setname);
+            yaz_log(log_details, "%p ZOOM_connection_send_search: allocating "
+                   "set %s", c, r->setname);
         }
         else
+       {
+            yaz_log(log_details, "%p ZOOM_connection_send_search: using "
+                   "default set", c);
             r->setname = xstrdup ("default");
+       }
         ZOOM_options_set (r->options, "setname", r->setname);
     }
     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
-    /* send search request */
     return send_APDU (c, apdu);
 }
 
-static void response_diag (ZOOM_connection c, Z_DiagRec *p)
+static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
 {
     int oclass;
-    Z_DefaultDiagFormat *r;
     char *addinfo = 0;
-    
-    xfree (c->addinfo);
-    c->addinfo = 0;
-    if (p->which != Z_DiagRec_defaultFormat)
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-       return;
-    }
-    r = p->u.defaultFormat;
+
     switch (r->which)
     {
     case Z_DefaultDiagFormat_v2Addinfo:
@@ -1369,11 +1436,21 @@ static void response_diag (ZOOM_connection c, Z_DiagRec *p)
        addinfo = r->u.v3Addinfo;
        break;
     }
+    xfree (c->addinfo);
+    c->addinfo = 0;
     set_dset_error(c, *r->condition,
                    yaz_z3950oid_to_str(r->diagnosticSetId, &oclass),
                    addinfo, 0);
 }
 
+static void response_diag(ZOOM_connection c, Z_DiagRec *p)
+{
+    if (p->which != Z_DiagRec_defaultFormat)
+        set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
+    else
+       response_default_diag(c, p->u.defaultFormat);
+}
+
 ZOOM_API(ZOOM_record)
 ZOOM_record_clone (ZOOM_record srec)
 {
@@ -1448,7 +1525,7 @@ static const char *marc_iconv_return(ZOOM_record rec, int marc_type,
     if (record_charset && *record_charset)
     {
        /* Use "from,to" or just "from" */
-       const char *cp =strchr(record_charset, ',');
+       const char *cp = strchr(record_charset, ',');
        int clen = strlen(record_charset);
        if (cp && cp[1])
        {
@@ -1502,7 +1579,7 @@ static const char *record_iconv_return(ZOOM_record rec, int *len,
     if (record_charset && *record_charset)
     {
        /* Use "from,to" or just "from" */
-       const char *cp =strchr(record_charset, ',');
+       const char *cp = strchr(record_charset, ',');
        int clen = strlen(record_charset);
        if (cp && cp[1])
        {
@@ -1651,7 +1728,8 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
        }
         if (r->which == Z_External_sutrs)
            return record_iconv_return(rec, len,
-                                      r->u.sutrs->buf, r->u.sutrs->len,
+                                      (char*) r->u.sutrs->buf,
+                                      r->u.sutrs->len,
                                       charset);
         else if (r->which == Z_External_octet)
         {
@@ -1899,13 +1977,7 @@ static void handle_records (ZOOM_connection c, Z_Records *sr,
         return;
     }
     if (sr && sr->which == Z_Records_NSD)
-    {
-       Z_DiagRec dr, *dr_p = &dr;
-       dr.which = Z_DiagRec_defaultFormat;
-       dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
-       
-       response_diag (c, dr_p);
-    }
+       response_default_diag(c, sr->u.nonSurrogateDiagnostic);
     else if (sr && sr->which == Z_Records_multipleNSD)
     {
        if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
@@ -1936,13 +2008,17 @@ static void handle_records (ZOOM_connection c, Z_Records *sr,
            if (present_phase && p->num_records == 0)
            {
                /* present response and we didn't get any records! */
-                set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
+               Z_NamePlusRecord *myrec = 
+                   zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
+               record_cache_add(resultset, myrec, resultset->start);
            }
        }
        else if (present_phase)
        {
            /* present response and we didn't get any records! */
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
+           Z_NamePlusRecord *myrec = 
+               zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
+           record_cache_add(resultset, myrec, resultset->start);
        }
     }
 }
@@ -1956,9 +2032,7 @@ static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
 {
     ZOOM_resultset resultset;
     ZOOM_Event event;
-    
-    yaz_log (LOG_DEBUG, "got search response");
-    
+
     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
        return ;
     
@@ -2026,7 +2100,7 @@ static zoom_ret send_sort (ZOOM_connection c,
     return zoom_complete;
 }
 
-static zoom_ret send_present (ZOOM_connection c)
+static zoom_ret send_present(ZOOM_connection c)
 {
     Z_APDU *apdu = 0;
     Z_PresentRequest *req = 0;
@@ -2036,8 +2110,10 @@ static zoom_ret send_present (ZOOM_connection c)
     ZOOM_resultset  resultset;
 
     if (!c->tasks)
+    {
+       yaz_log(log_details, "%p send_present no tasks", c);
        return zoom_complete;
-
+    }
     switch (c->tasks->which)
     {
     case ZOOM_TASK_SEARCH:
@@ -2049,13 +2125,19 @@ static zoom_ret send_present (ZOOM_connection c)
         resultset->count = c->tasks->u.retrieve.count;
 
         if (resultset->start >= resultset->size)
+       {
+           yaz_log(log_details, "%p send_present start=%d >= size=%d",
+                   c, resultset->start, resultset->size);
             return zoom_complete;
+       }
         if (resultset->start + resultset->count > resultset->size)
             resultset->count = resultset->size - resultset->start;
        break;
     default:
         return zoom_complete;
     }
+    yaz_log(log_details, "%p send_present start=%d count=%d",
+           c, resultset->start, resultset->count);
 
     syntax = ZOOM_resultset_option_get (resultset, "preferredRecordSyntax");
     elementSetName = ZOOM_resultset_option_get (resultset, "elementSetName");
@@ -2072,18 +2154,26 @@ static zoom_ret send_present (ZOOM_connection c)
            break;
     }
     if (i == resultset->count)
+    {
+       yaz_log(log_details, "%p send_present skip=%d no more to fetch", c, i);
        return zoom_complete;
+    }
 
     apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
     req = apdu->u.presentRequest;
 
     resultset->start += i;
     resultset->count -= i;
+
+    if (i)
+       yaz_log(log_details, "%p send_present skip=%d", c, i);
+
     *req->resultSetStartPoint = resultset->start + 1;
     *req->numberOfRecordsRequested = resultset->step>0 ?
         resultset->step : resultset->count;
     assert (*req->numberOfRecordsRequested > 0);
 
+
     if (syntax && *syntax)
        req->preferredRecordSyntax =
            yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
@@ -2194,6 +2284,8 @@ ZOOM_scanset_destroy (ZOOM_scanset scan)
 static zoom_ret send_package (ZOOM_connection c)
 {
     ZOOM_Event event;
+
+    yaz_log(log_details, "%p send_package", c);
     if (!c->tasks)
         return zoom_complete;
     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
@@ -2210,6 +2302,8 @@ static zoom_ret send_scan (ZOOM_connection c)
     ZOOM_scanset scan;
     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
     Z_ScanRequest *req = apdu->u.scanRequest;
+
+    yaz_log(log_details, "%p send_scan", c);
     if (!c->tasks)
         return zoom_complete;
     assert (c->tasks->which == ZOOM_TASK_SCAN);
@@ -2404,7 +2498,7 @@ static Z_ItemOrder *encode_item_order(ZOOM_package p)
     Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc (p->odr_out, sizeof(*req));
     const char *str;
     
-    req->which=Z_IOItemOrder_esRequest;
+    req->which = Z_IOItemOrder_esRequest;
     req->u.esRequest = (Z_IORequest *) 
         odr_malloc(p->odr_out,sizeof(Z_IORequest));
 
@@ -2490,17 +2584,17 @@ Z_APDU *create_admin_package(ZOOM_package p, int type,
        toKeep = r->u.adminService->u.esRequest->toKeep =
            (Z_ESAdminOriginPartToKeep *) 
            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
-       toKeep->which=type;
+       toKeep->which = type;
        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
-       toKeep->u.create=odr_nullval();
+       toKeep->u.create = odr_nullval();
        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
        
        r->u.adminService->u.esRequest->notToKeep = notToKeep =
            (Z_ESAdminOriginPartNotToKeep *)
            odr_malloc(p->odr_out,
                       sizeof(*r->u.adminService->u.esRequest->notToKeep));
-       notToKeep->which=Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
-       notToKeep->u.recordsWillFollow=odr_nullval();
+       notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
+       notToKeep->u.recordsWillFollow = odr_nullval();
        if (toKeepP)
            *toKeepP = toKeep;
        if (notToKeepP)
@@ -2509,6 +2603,30 @@ Z_APDU *create_admin_package(ZOOM_package p, int type,
     return apdu;
 }
 
+static Z_APDU *create_xmlupdate_package(ZOOM_package p)
+{
+    Z_APDU *apdu = create_es_package(p, VAL_XMLUPDATE);
+    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
+    Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
+    const char *doc = ZOOM_options_get(p->options, "doc");
+
+    req->taskSpecificParameters = ext;
+    ext->direct_reference = req->packageType;
+    ext->descriptor = 0;
+    ext->indirect_reference = 0;
+    
+    ext->which = Z_External_octet;
+    ext->u.single_ASN1_type = (Odr_oct *)
+       odr_malloc (p->odr_out, sizeof(Odr_oct));
+
+    if (!doc)
+       doc = "";
+    ext->u.single_ASN1_type->buf = (unsigned char*)
+       odr_strdup(p->odr_out, doc);
+    ext->u.single_ASN1_type->size = ext->u.single_ASN1_type->len = strlen(doc);
+    return apdu;
+}
+
 static Z_APDU *create_update_package(ZOOM_package p)
 {
     Z_APDU *apdu = 0;
@@ -2599,7 +2717,7 @@ static Z_APDU *create_update_package(ZOOM_package p)
                odr_malloc (p->odr_out, sizeof(Odr_oct));
            notToKeep->elements[0]->u.opaque->size =
                notToKeep->elements[0]->u.opaque->len = strlen(recordIdOpaque);
-           notToKeep->elements[0]->u.opaque->buf =
+           notToKeep->elements[0]->u.opaque->buf = (unsigned char*)
                odr_strdup(p->odr_out, recordIdOpaque);
        }
        else if (recordIdNumber)
@@ -2656,20 +2774,29 @@ ZOOM_API(void)
             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
         }
     }
-    if (!strcmp(type, "create"))  /* create database */
+    else if (!strcmp(type, "create"))  /* create database */
     {
        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
                                    0, 0);
     }  
-    if (!strcmp(type, "drop"))  /* drop database */
+    else if (!strcmp(type, "drop"))  /* drop database */
     {
        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
                                    0, 0);
     }
-    if (!strcmp(type, "update")) /* update record(s) */
+    else if (!strcmp(type, "commit"))  /* commit changes */
+    {
+       apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
+                                   0, 0);
+    }
+    else if (!strcmp(type, "update")) /* update record(s) */
     {
        apdu = create_update_package(p);
     }
+    else if (!strcmp(type, "xmlupdate"))
+    {
+       apdu = create_xmlupdate_package(p);
+    }
     if (apdu)
     {
         if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
@@ -2742,21 +2869,20 @@ static int ZOOM_connection_exec_task (ZOOM_connection c)
     zoom_ret ret = zoom_complete;
 
     if (!task)
-    {
-        yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task task=<null>");
        return 0;
-    }
-    yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task type=%d run=%d",
-             task->which, task->running);
+    yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
+             c, task->which, task->running);
     if (c->error != ZOOM_ERROR_NONE)
     {
-        yaz_log (LOG_DEBUG, "remove tasks because of error = %d", c->error);
+        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+               "removing tasks because of error = %d", c, c->error);
         ZOOM_connection_remove_tasks (c);
         return 0;
     }
     if (task->running)
     {
-        yaz_log (LOG_DEBUG, "task already running");
+        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+                "task already running", c);
        return 0;
     }
     task->running = 1;
@@ -2795,16 +2921,19 @@ static int ZOOM_connection_exec_task (ZOOM_connection c)
     }
     else
     {
-        yaz_log (LOG_DEBUG, "remove tasks because no connection exist");
+        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+               "remove tasks because no connection exist", c);
         ZOOM_connection_remove_tasks (c);
     }
     if (ret == zoom_complete)
     {
-        yaz_log (LOG_DEBUG, "task removed (complete)");
+        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+                "task removed (complete)", c);
         ZOOM_connection_remove_task (c);
         return 0;
     }
-    yaz_log (LOG_DEBUG, "task pending");
+    yaz_log(log_details, "%p ZOOM_connection_exec_task "
+           "task pending", c);
     return 1;
 }
 
@@ -2836,19 +2965,55 @@ static int es_response (ZOOM_connection c,
             ZOOM_options_setl (c->tasks->u.package->options,
                                "targetReference", (char*) id->buf, id->len);
     }
+    if (res->taskPackage && 
+       res->taskPackage->which == Z_External_octet)
+    {
+       Odr_oct *doc = res->taskPackage->u.octet_aligned;
+       ZOOM_options_setl (c->tasks->u.package->options,
+                          "xmlUpdateDoc", (char*) doc->buf, doc->len);
+    }
     return 1;
 }
 
+static void interpret_init_diag(ZOOM_connection c,
+                               Z_DiagnosticFormat *diag)
+{
+    if (diag->num > 0)
+    {
+       Z_DiagnosticFormat_s *ds = diag->elements[0];
+       if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
+           response_default_diag(c, ds->u.defaultDiagRec);
+    }
+}
+
+
+static void interpret_otherinformation_field(ZOOM_connection c,
+                                            Z_OtherInformation *ui)
+{
+    int i;
+    for (i = 0; i < ui->num_elements; i++)
+    {
+       Z_OtherInformationUnit *unit = ui->list[i];
+       if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
+           unit->information.externallyDefinedInfo &&
+           unit->information.externallyDefinedInfo->which ==
+           Z_External_diag1) 
+       {
+           interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
+       } 
+    }
+}
 
-static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
+static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
 {
     Z_InitResponse *initrs;
     
     c->mask = 0;
-    yaz_log (LOG_DEBUG, "recv APDU type=%d", apdu->which);
+    yaz_log(log_details, "%p recv_apdu apdu->which=%d", c, apdu->which);
     switch(apdu->which)
     {
     case Z_APDU_initResponse:
+       yaz_log(log_api, "%p recv_apd: Received Init response", c);
        initrs = apdu->u.initResponse;
         ZOOM_connection_option_set(c, "serverImplementationId",
                                    initrs->implementationId ?
@@ -2871,7 +3036,12 @@ static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
                                    initrs->implementationVersion : "");
        if (!*initrs->result)
        {
-            set_ZOOM_error(c, ZOOM_ERROR_INIT, 0);
+           Z_External *uif = initrs->userInformationField;
+
+           set_ZOOM_error(c, ZOOM_ERROR_INIT, 0); /* default error */
+
+           if (uif && uif->which == Z_External_userInfo1)
+               interpret_otherinformation_field(c, uif->u.userInfo1);
        }
        else
        {
@@ -2900,12 +3070,13 @@ static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
             
             if (p)
             {
-                char *charset=NULL, *lang=NULL;
+                char *charset = NULL, *lang = NULL;
                 int sel;
                 
                 yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
-                yaz_log(LOG_DEBUG, "Target accepted: charset %s, "
-                        "language %s, select %d",
+                yaz_log(log_details, "%p recv_apdu target accepted: "
+                       "charset %s, language %s, select %d",
+                       c,
                         charset ? charset : "none", lang ? lang : "none", sel);
                 if (charset)
                     ZOOM_connection_option_set (c, "negotiation-charset",
@@ -2913,34 +3084,44 @@ static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
                 if (lang)
                     ZOOM_connection_option_set (c, "negotiation-lang",
                                                 lang);
+
+                ZOOM_connection_option_set (
+                   c,  "negotiation-charset-in-effect-for-records",
+                   (sel != 0) ? "1" : "0");
                 nmem_destroy(tmpmem);
             }
        }       
        break;
     case Z_APDU_searchResponse:
+       yaz_log(log_api, "%p recv_apdu Search response", c);
        handle_search_response (c, apdu->u.searchResponse);
        if (send_sort_present (c) == zoom_complete)
            ZOOM_connection_remove_task (c);
        break;
     case Z_APDU_presentResponse:
+       yaz_log(log_api, "%p recv_apdu Present response", c);
        handle_present_response (c, apdu->u.presentResponse);
        if (send_present (c) == zoom_complete)
            ZOOM_connection_remove_task (c);
        break;
     case Z_APDU_sortResponse:
+       yaz_log(log_api, "%p recv_apdu Sort response", c);
        sort_response (c, apdu->u.sortResponse);
        if (send_present (c) == zoom_complete)
            ZOOM_connection_remove_task (c);
         break;
     case Z_APDU_scanResponse:
+       yaz_log(log_api, "%p recv_apdu Scan response", c);
         scan_response (c, apdu->u.scanResponse);
         ZOOM_connection_remove_task (c);
         break;
     case Z_APDU_extendedServicesResponse:
+       yaz_log(log_api, "%p recv_apdu Extended Services response", c);
         es_response (c, apdu->u.extendedServicesResponse);
         ZOOM_connection_remove_task (c);
         break;
     case Z_APDU_close:
+       yaz_log(log_api, "%p recv_apdu Close PDU", c);
         if (c->reconnect_ok)
         {
             do_close(c);
@@ -2954,6 +3135,7 @@ static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
         }
         break;
     default:
+       yaz_log(log_api, "%p Received unknown PDU", c);
         set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
         do_close(c);
     }
@@ -2983,7 +3165,7 @@ static void handle_srw_response(ZOOM_connection c,
 
     resultset->size = 0;
 
-    yaz_log(LOG_DEBUG, "got SRW response OK");
+    yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
     
     if (res->numberOfRecords)
         resultset->size = *res->numberOfRecords;
@@ -3011,7 +3193,7 @@ static void handle_srw_response(ZOOM_connection c,
         npr->u.databaseRecord->which = Z_External_octet;
         npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
             odr_malloc(c->odr_in, sizeof(Odr_oct));
-        npr->u.databaseRecord->u.octet_aligned->buf = 
+        npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
             res->records[i].recordData_buf;
         npr->u.databaseRecord->u.octet_aligned->len = 
             npr->u.databaseRecord->u.octet_aligned->size = 
@@ -3046,7 +3228,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
     const char *connection_head = z_HTTP_header_lookup(hres->headers,
                                                        "Connection");
     c->mask = 0;
-    yaz_log (LOG_DEBUG, "handle_http");
+    yaz_log(log_details, "%p handle_http", c);
 
     if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
     {
@@ -3063,7 +3245,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         if (!ret && soap_package->which == Z_SOAP_generic &&
             soap_package->u.generic->no == 0)
         {
-            Z_SRW_PDU *sr = soap_package->u.generic->p;
+            Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
             if (sr->which == Z_SRW_searchRetrieve_response)
                 handle_srw_response(c, sr->u.response);
             else
@@ -3113,7 +3295,7 @@ static int do_read (ZOOM_connection c)
     
     r = cs_get (c->cs, &c->buf_in, &c->len_in);
     more = cs_more(c->cs);
-    yaz_log (LOG_DEBUG, "do_read len=%d more=%d", r, more);
+    yaz_log(log_details, "%p do_read len=%d more=%d", c, r, more);
     if (r == 1)
        return 0;
     if (r <= 0)
@@ -3122,7 +3304,7 @@ static int do_read (ZOOM_connection c)
         {
             do_close (c);
             c->reconnect_ok = 0;
-            yaz_log (LOG_DEBUG, "reconnect read");
+            yaz_log(log_details, "%p do_read reconnect read", c);
             c->tasks->running = 0;
             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
         }
@@ -3154,7 +3336,7 @@ static int do_read (ZOOM_connection c)
            do_close (c);
        }
        else if (gdu->which == Z_GDU_Z3950)
-           handle_apdu (c, gdu->u.z3950);
+           recv_apdu (c, gdu->u.z3950);
         else if (gdu->which == Z_GDU_HTTP_Response)
         {
 #if HAVE_XML2
@@ -3177,14 +3359,15 @@ static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
     ZOOM_connection_put_event (c, event);
 
-    yaz_log (LOG_DEBUG, "do_write_ex len=%d", len_out);
-    if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
+    yaz_log(log_details, "%p do_write_ex len=%d", c, len_out);
+    if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
     {
+        yaz_log(log_details, "%p do_write_ex write failed", c);
         if (c->reconnect_ok)
         {
             do_close (c);
             c->reconnect_ok = 0;
-            yaz_log (LOG_DEBUG, "reconnect write");
+            yaz_log(log_details, "%p do_write_ex reconnect write", c);
             c->tasks->running = 0;
             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
             return zoom_pending;
@@ -3203,12 +3386,14 @@ static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
             c->mask += ZOOM_SELECT_WRITE;
         if (c->cs->io_pending & CS_WANT_READ)
             c->mask += ZOOM_SELECT_READ;
-        yaz_log (LOG_DEBUG, "do_write_ex 1 mask=%d", c->mask);
+        yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
+               c, c->mask);
     }
     else
     {
         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
-        yaz_log (LOG_DEBUG, "do_write_ex 2 mask=%d", c->mask);
+        yaz_log(log_details, "%p do_write_ex write complete mask=%d",
+               c, c->mask);
     }
     return zoom_pending;
 }
@@ -3227,11 +3412,18 @@ ZOOM_connection_option_get (ZOOM_connection c, const char *key)
 
 ZOOM_API(void)
 ZOOM_connection_option_set (ZOOM_connection c, const char *key,
-                                  const char *val)
+                           const char *val)
 {
     ZOOM_options_set (c->options, key, val);
 }
 
+ZOOM_API(void)
+ZOOM_connection_option_setl (ZOOM_connection c, const char *key,
+                            const char *val, int len)
+{
+    ZOOM_options_setl (c->options, key, val, len);
+}
+
 ZOOM_API(const char *)
 ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
 {
@@ -3338,7 +3530,7 @@ static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
 {
     ZOOM_Event event = 0;
     int r = cs_look(c->cs);
-    yaz_log (LOG_DEBUG, "ZOOM_connection_do_io c=%p mask=%d cs_look=%d",
+    yaz_log(log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
             c, mask, r);
     
     if (r == CS_NONE)
@@ -3354,7 +3546,8 @@ static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
 
         ret = cs_rcvconnect (c->cs);
-        yaz_log (LOG_DEBUG, "cs_rcvconnect returned %d", ret);
+        yaz_log(log_details, "%p ZOOM_connection_do_io "
+               "cs_rcvconnect returned %d", c, ret);
         if (ret == 1)
         {
             c->mask = ZOOM_SELECT_EXCEPT;
@@ -3367,6 +3560,7 @@ static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
         else if (ret == 0)
         {
             ZOOM_connection_put_event (c, event);
+           get_cert(c);
             if (c->proto == PROTO_Z3950)
                 ZOOM_connection_send_init(c);
             else
@@ -3407,7 +3601,8 @@ ZOOM_connection_last_event(ZOOM_connection cs)
 ZOOM_API(int)
 ZOOM_event (int no, ZOOM_connection *cs)
 {
-    int timeout = 5000;
+    int timeout = 30;      /* default timeout in seconds */
+    int timeout_set = 0;   /* whether it was overriden at all */
 #if HAVE_SYS_POLL_H
     struct pollfd pollfds[1024];
     ZOOM_connection poll_cs[1024];
@@ -3464,10 +3659,18 @@ ZOOM_event (int no, ZOOM_connection *cs)
            continue;
        if (max_fd < fd)
            max_fd = fd;
-
-        this_timeout = ZOOM_options_get_int (c->options, "timeout", -1);
-        if (this_timeout != -1 && this_timeout < timeout)
-            timeout = this_timeout;
+       
+       /* -1 is used for indefinite timeout (no timeout), so -2 here. */
+        this_timeout = ZOOM_options_get_int (c->options, "timeout", -2);
+       if (this_timeout != -2)
+       {
+           /* ensure the minimum timeout is used */
+           if (!timeout_set)
+               timeout = this_timeout;
+           else if (this_timeout != -1 && this_timeout < timeout)
+               timeout = this_timeout;
+           timeout_set = 1;
+       }               
 #if HAVE_SYS_POLL_H
         if (mask)
         {
@@ -3503,14 +3706,11 @@ ZOOM_event (int no, ZOOM_connection *cs)
        }
 #endif
     }
-    if (timeout >= 5000)
-        timeout = 30;
-
     if (!nfds)
         return 0;
 
 #if HAVE_SYS_POLL_H
-    r = poll (pollfds, nfds, timeout * 1000);
+    r = poll (pollfds, nfds, (timeout == -1 ? -1 : timeout * 1000));
     for (i = 0; i<nfds; i++)
     {
         ZOOM_connection c = poll_cs[i];
@@ -3538,9 +3738,7 @@ ZOOM_event (int no, ZOOM_connection *cs)
 #else
     tv.tv_sec = timeout;
     tv.tv_usec = 0;
-    yaz_log (LOG_DEBUG, "select start");
-    r = select (max_fd+1, &input, &output, &except, &tv);
-    yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
+    r = select (max_fd+1, &input, &output, &except, (timeout == -1 ? 0 : &tv));
     for (i = 0; i<no; i++)
     {
        ZOOM_connection c = cs[i];
@@ -3568,7 +3766,6 @@ ZOOM_event (int no, ZOOM_connection *cs)
            /* timeout and this connection was waiting */
            set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
             do_close (c);
-            yaz_log (LOG_DEBUG, "timeout");
             ZOOM_connection_put_event(c, event);
        }
     }