Start work on record caching - does not work!
[yaz-moved-to-github.git] / src / zoom-c.c
index b483fd9..41d6e1c 100644 (file)
@@ -1,11 +1,14 @@
 /* This file is part of the YAZ toolkit.
- * Copyright (C) 1995-2010 Index Data
+ * Copyright (C) Index Data
  * See the file LICENSE for details.
  */
 /**
  * \file zoom-c.c
  * \brief Implements ZOOM C interface.
  */
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 #include <assert.h>
 #include <string.h>
 #include <yaz/xmalloc.h>
 #include <yaz/otherinfo.h>
 #include <yaz/log.h>
-#include <yaz/pquery.h>
-#include <yaz/marcdisp.h>
 #include <yaz/diagbib1.h>
 #include <yaz/charneg.h>
-#include <yaz/ill.h>
-#include <yaz/srw.h>
-#include <yaz/cql.h>
-#include <yaz/ccl.h>
 #include <yaz/query-charset.h>
-#include <yaz/copy_types.h>
 #include <yaz/snprintf.h>
 #include <yaz/facet.h>
 
 YAZ_SHPTR_TYPE(WRBUF)
 #endif
 
-static int log_api = 0;
-static int log_details = 0;
-
-typedef enum {
-    zoom_pending,
-    zoom_complete
-} zoom_ret;
+static int log_api0 = 0;
+static int log_details0 = 0;
 
 static void resultset_destroy(ZOOM_resultset r);
-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 char *cql2pqf(ZOOM_connection c, const char *cql);
-
-ZOOM_API(const char *) ZOOM_get_event_str(int event)
-{
-    static const char *ar[] = {
-        "NONE",
-        "CONNECT",
-        "SEND_DATA",
-        "RECV_DATA",
-        "TIMEOUT",
-        "UNKNOWN",
-        "SEND_APDU",
-        "RECV_APDU",
-        "RECV_RECORD",
-        "RECV_SEARCH",
-        "END"
-    };
-    return ar[event];
-}
-
-/*
- * This wrapper is just for logging failed lookups.  It would be nicer
- * if it could cause failure when a lookup fails, but that's hard.
- */
-static Odr_oid *zoom_yaz_str_to_z3950oid(ZOOM_connection c,
-                                     oid_class oid_class, const char *str) {
-    Odr_oid *res = yaz_string_to_oid_odr(yaz_oid_std(), oid_class, str,
-                                     c->odr_out);
-    if (res == 0)
-        yaz_log(YLOG_WARN, "%p OID lookup (%d, '%s') failed",
-                c, (int) oid_class, str);
-    return res;
-}
-
 
 static void initlog(void)
 {
     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_api0 = yaz_log_module_level("zoom");
+        log_details0 = 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));
-    event->kind = kind;
-    event->next = 0;
-    event->prev = 0;
-    yaz_log(log_details, "ZOOM_Event_create(kind=%d)", kind);
-    return event;
-}
-
-static void ZOOM_Event_destroy(ZOOM_Event event)
-{
-    xfree(event);
-}
-
-static void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
-{
-    if (c->m_queue_back)
-    {
-        c->m_queue_back->prev = event;
-        assert(c->m_queue_front);
-    }
-    else
-    {
-        assert(!c->m_queue_front);
-        c->m_queue_front = event;
-    }
-    event->next = c->m_queue_back;
-    event->prev = 0;
-    c->m_queue_back = event;
-}
-
-static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
-{
-    ZOOM_Event event = c->m_queue_front;
-    if (!event)
-    {
-        c->last_event = ZOOM_EVENT_NONE;
-        return 0;
-    }
-    assert(c->m_queue_back);
-    c->m_queue_front = event->prev;
-    if (c->m_queue_front)
-    {
-        assert(c->m_queue_back);
-        c->m_queue_front->next = 0;
-    }
-    else
-        c->m_queue_back = 0;
-    c->last_event = event->kind;
-    return event;
-}
-
-static void ZOOM_connection_remove_events(ZOOM_connection c)
-{
-    ZOOM_Event event;
-    while ((event = ZOOM_connection_get_event(c)))
-        ZOOM_Event_destroy(event);
-}
-
-ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
-{
-    ZOOM_Event event = c->m_queue_front;
-
-    return event ? event->kind : ZOOM_EVENT_NONE;
-}
-
 void ZOOM_connection_remove_tasks(ZOOM_connection c);
 
-static void set_dset_error(ZOOM_connection c, int error,
-                           const char *dset,
-                           const char *addinfo, const char *addinfo2)
+void ZOOM_set_dset_error(ZOOM_connection c, int error,
+                         const char *dset,
+                         const char *addinfo, const char *addinfo2)
 {
     char *cp;
 
@@ -180,15 +70,16 @@ static void set_dset_error(ZOOM_connection c, int error,
     }
     if (addinfo && addinfo2)
     {
-        c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
+        c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 3);
         strcpy(c->addinfo, addinfo);
+        strcat(c->addinfo, ": ");
         strcat(c->addinfo, addinfo2);
     }
     else if (addinfo)
         c->addinfo = xstrdup(addinfo);
     if (error != ZOOM_ERROR_NONE)
     {
-        yaz_log(log_api, "%p set_dset_error %s %s:%d %s %s",
+        yaz_log(c->log_api, "%p set_dset_error %s %s:%d %s %s",
                 c, c->host_port ? c->host_port : "<>", dset, error,
                 addinfo ? addinfo : "",
                 addinfo2 ? addinfo2 : "");
@@ -196,36 +87,20 @@ static void set_dset_error(ZOOM_connection c, int error,
     }
 }
 
-static int uri_to_code(const char *uri)
+int ZOOM_uri_to_code(const char *uri)
 {
-    int code = 0;       
+    int code = 0;
     const char *cp;
     if ((cp = strrchr(uri, '/')))
         code = atoi(cp+1);
     return code;
 }
 
-#if YAZ_HAVE_XML2
-static void set_HTTP_error(ZOOM_connection c, int error,
-                           const char *addinfo, const char *addinfo2)
-{
-    set_dset_error(c, error, "HTTP", addinfo, addinfo2);
-}
-
-static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
-{
-    const char *uri = d->uri;
-    if (uri)
-        set_dset_error(c, uri_to_code(uri), uri, d->details, 0);
-}
-
-#endif
 
 
-static void set_ZOOM_error(ZOOM_connection c, int error,
-                           const char *addinfo)
+void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo)
 {
-    set_dset_error(c, error, "ZOOM", addinfo, 0);
+    ZOOM_set_dset_error(c, error, "ZOOM", addinfo, 0);
 }
 
 static void clear_error(ZOOM_connection c)
@@ -250,7 +125,7 @@ static void clear_error(ZOOM_connection c)
     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
         break;
     default:
-        set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+        ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     }
 }
 
@@ -261,9 +136,6 @@ void ZOOM_connection_show_task(ZOOM_task task)
     case ZOOM_TASK_SEARCH:
         yaz_log(YLOG_LOG, "search p=%p", task);
         break;
-    case ZOOM_TASK_RETRIEVE:
-        yaz_log(YLOG_LOG, "retrieve p=%p", task);
-        break;
     case ZOOM_TASK_CONNECT:
         yaz_log(YLOG_LOG, "connect p=%p", task);
         break;
@@ -308,7 +180,6 @@ ZOOM_task ZOOM_connection_insert_task(ZOOM_connection c, int which)
 
     task->running = 0;
     task->which = which;
-    clear_error(c);
     return task;
 }
 
@@ -325,11 +196,7 @@ void ZOOM_connection_remove_task(ZOOM_connection c)
             resultset_destroy(task->u.search.resultset);
             xfree(task->u.search.syntax);
             xfree(task->u.search.elementSetName);
-            break;
-        case ZOOM_TASK_RETRIEVE:
-            resultset_destroy(task->u.retrieve.resultset);
-            xfree(task->u.retrieve.syntax);
-            xfree(task->u.retrieve.elementSetName);
+            xfree(task->u.search.schema);
             break;
         case ZOOM_TASK_CONNECT:
             break;
@@ -362,9 +229,12 @@ void ZOOM_connection_remove_tasks(ZOOM_connection c)
         ZOOM_connection_remove_task(c);
 }
 
-static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
-                                       const char *syntax,
-                                       const char *elementSetName);
+static void odr_wrbuf_write(ODR o, void *handle, int type,
+                            const char *buf, int len)
+{
+    WRBUF w = (WRBUF) handle;
+    wrbuf_write(w, buf, len);
+}
 
 ZOOM_API(ZOOM_connection)
     ZOOM_connection_create(ZOOM_options options)
@@ -373,7 +243,10 @@ ZOOM_API(ZOOM_connection)
 
     initlog();
 
-    yaz_log(log_api, "%p ZOOM_connection_create", c);
+    c->log_api = log_api0;
+    c->log_details = log_details0;
+
+    yaz_log(c->log_api, "%p ZOOM_connection_create", c);
 
     c->proto = PROTO_Z3950;
     c->cs = 0;
@@ -382,7 +255,7 @@ ZOOM_API(ZOOM_connection)
     c->state = STATE_IDLE;
     c->addinfo = 0;
     c->diagset = 0;
-    set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+    ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     c->buf_in = 0;
     c->len_in = 0;
     c->buf_out = 0;
@@ -392,9 +265,9 @@ ZOOM_API(ZOOM_connection)
     c->options = ZOOM_options_create_with_parent(options);
 
     c->host_port = 0;
-    c->path = 0;
     c->proxy = 0;
-    
+    c->tproxy = 0;
+
     c->charset = c->lang = 0;
 
     c->cookie_out = 0;
@@ -405,6 +278,7 @@ ZOOM_API(ZOOM_connection)
     c->user = 0;
     c->group = 0;
     c->password = 0;
+    c->url_authentication = 0;
 
     c->maximum_record_size = 0;
     c->preferred_message_size = 0;
@@ -412,6 +286,7 @@ ZOOM_API(ZOOM_connection)
     c->odr_in = odr_createmem(ODR_DECODE);
     c->odr_out = odr_createmem(ODR_ENCODE);
     c->odr_print = 0;
+    c->odr_save = 0;
 
     c->async = 0;
     c->support_named_resultsets = 0;
@@ -422,28 +297,40 @@ ZOOM_API(ZOOM_connection)
 
     c->sru_version = 0;
     c->no_redirects = 0;
+    c->cookies = 0;
+    c->saveAPDU_wrbuf = 0;
+
+#if HAVE_LIBMEMCACHED_MEMCACHED_H
+    c->mc_st = 0;
+#endif
     return c;
 }
 
+ZOOM_API(void) ZOOM_connection_save_apdu_wrbuf(ZOOM_connection c, WRBUF w)
+{
+    if (c->odr_save)
+    {
+        odr_destroy(c->odr_save);
+        c->odr_save = 0;
+    }
+    if (w)
+    {
+        c->odr_save = odr_createmem(ODR_PRINT);
+        odr_set_stream(c->odr_save, w, odr_wrbuf_write, 0);
+    }
+}
 
 /* set database names. Take local databases (if set); otherwise
    take databases given in ZURL (if set); otherwise use Default */
-static char **set_DatabaseNames(ZOOM_connection con, ZOOM_options options,
-                                int *num, ODR odr)
+char **ZOOM_connection_get_databases(ZOOM_connection con, ZOOM_options options,
+                                     int *num, ODR odr)
 {
     char **databaseNames;
     const char *cp = ZOOM_options_get(options, "databaseName");
-    
+
     if ((!cp || !*cp) && con->host_port)
-    {
-        if (strncmp(con->host_port, "unix:", 5) == 0)
-            cp = strchr(con->host_port+5, ':');
-        else
-            cp = strchr(con->host_port, '/');
-        if (cp)
-            cp++;
-    }
-    if (!cp)
+        cs_get_host_args(con->host_port, &cp);
+    if (!cp || !*cp)
         cp = "Default";
     nmem_strsplit(odr_getmem(odr), "+", cp,  &databaseNames, num);
     return databaseNames;
@@ -468,6 +355,8 @@ static zoom_sru_mode get_sru_mode_from_string(const char *s)
         return zoom_sru_get;
     else if (!yaz_matchstr(s, "post"))
         return zoom_sru_post;
+    else if (!yaz_matchstr(s, "solr"))
+        return zoom_sru_solr;
     return zoom_sru_error;
 }
 
@@ -476,14 +365,14 @@ ZOOM_API(void)
                             const char *host, int portnum)
 {
     const char *val;
-    ZOOM_task task;
+    const char *http_lead;
 
     initlog();
 
-    yaz_log(log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
+    yaz_log(c->log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
             c, host ? host : "null", portnum);
 
-    set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+    ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     ZOOM_connection_remove_tasks(c);
 
     if (c->odr_print)
@@ -501,26 +390,35 @@ ZOOM_API(void)
 
     if (c->cs)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect reconnect ok", c);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect reconnect ok", c);
         c->reconnect_ok = 1;
         return;
     }
-    yaz_log(log_details, "%p ZOOM_connection_connect connect", c);
+    yaz_log(c->log_details, "%p ZOOM_connection_connect connect", c);
     xfree(c->proxy);
     c->proxy = 0;
     val = ZOOM_options_get(c->options, "proxy");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
         c->proxy = xstrdup(val);
     }
 
+    xfree(c->tproxy);
+    c->tproxy = 0;
+    val = ZOOM_options_get(c->options, "tproxy");
+    if (val && *val)
+    {
+        yaz_log(c->log_details, "%p ZOOM_connection_connect tproxy=%s", c, val);
+        c->tproxy = xstrdup(val);
+    }
+
     xfree(c->charset);
     c->charset = 0;
     val = ZOOM_options_get(c->options, "charset");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect charset=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect charset=%s", c, val);
         c->charset = xstrdup(val);
     }
 
@@ -528,24 +426,32 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "lang");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect lang=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect lang=%s", c, val);
         c->lang = xstrdup(val);
     }
     else
         c->lang = 0;
 
+    val = ZOOM_options_get(c->options, "sru");
+    if (val && *val && !strstr(host, "://"))
+        http_lead = "http://";
+    else
+        http_lead = "";
+    c->sru_mode = get_sru_mode_from_string(val);
+
     if (host)
     {
+        char hostn[128];
         xfree(c->host_port);
         if (portnum)
         {
-            char hostn[128];
             sprintf(hostn, "%.80s:%d", host, portnum);
-            c->host_port = xstrdup(hostn);
+            host = hostn;
         }
-        else
-            c->host_port = xstrdup(host);
-    }        
+        c->host_port = xmalloc(strlen(host) + strlen(http_lead) + 1);
+        strcpy(c->host_port, http_lead);
+        strcat(c->host_port, host);
+    }
 
     {
         /*
@@ -561,26 +467,25 @@ ZOOM_API(void)
         char *pcomma;
         char *pequals;
         while ((pcomma = strchr(remainder, ',')) != 0 &&
-               (pcolon == 0 || pcomma < pcolon)) {
+               (pcolon == 0 || pcomma < pcolon))
+        {
             *pcomma = '\0';
-            if ((pequals = strchr(remainder, '=')) != 0) {
+            if ((pequals = strchr(remainder, '=')) != 0)
+            {
                 *pequals = '\0';
-                /*printf("# setting '%s'='%s'\n", remainder, pequals+1);*/
                 ZOOM_connection_option_set(c, remainder, pequals+1);
             }
             remainder = pcomma+1;
         }
 
-        if (remainder != c->host_port) {
+        if (remainder != c->host_port)
+        {
+            remainder = xstrdup(remainder);
             xfree(c->host_port);
-            c->host_port = xstrdup(remainder);
-            /*printf("# reset hp='%s'\n", remainder);*/
+            c->host_port = remainder;
         }
     }
 
-    val = ZOOM_options_get(c->options, "sru");
-    c->sru_mode = get_sru_mode_from_string(val);
-
     xfree(c->sru_version);
     val = ZOOM_options_get(c->options, "sru_version");
     c->sru_version = xstrdup(val ? val : "1.2");
@@ -591,8 +496,8 @@ ZOOM_API(void)
     c->cookie_out = 0;
     val = ZOOM_options_get(c->options, "cookie");
     if (val && *val)
-    { 
-        yaz_log(log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
+    {
+        yaz_log(c->log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
         c->cookie_out = xstrdup(val);
     }
 
@@ -601,7 +506,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "clientIP");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect clientIP=%s",
+        yaz_log(c->log_details, "%p ZOOM_connection_connect clientIP=%s",
                 c, val);
         c->client_IP = xstrdup(val);
     }
@@ -623,181 +528,67 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "password");
     if (!val)
         val = ZOOM_options_get(c->options, "pass");
-
     if (val && *val)
         c->password = xstrdup(val);
-    
+
+    val = ZOOM_options_get(c->options, "authenticationMode");
+    if (val && !strcmp(val, "url"))
+        c->url_authentication = 1;
+    else
+        c->url_authentication = 0;
+
     c->maximum_record_size =
-        ZOOM_options_get_int(c->options, "maximumRecordSize", 1024*1024);
+        ZOOM_options_get_int(c->options, "maximumRecordSize", 64*1024*1024);
     c->preferred_message_size =
-        ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
+        ZOOM_options_get_int(c->options, "preferredMessageSize", 64*1024*1024);
 
     c->async = ZOOM_options_get_bool(c->options, "async", 0);
-    yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
-    task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
-
-    if (!c->async)
-    {
-        while (ZOOM_event(1, &c))
-            ;
-    }
-}
-
-ZOOM_API(ZOOM_query)
-    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;
-    s->odr = odr_createmem(ODR_ENCODE);
-    s->query_string = 0;
-
-    return s;
-}
 
-ZOOM_API(void)
-    ZOOM_query_destroy(ZOOM_query s)
-{
-    if (!s)
-        return;
+    yaz_cookies_destroy(c->cookies);
+    c->cookies = yaz_cookies_create();
 
-    (s->refcount)--;
-    yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
-    if (s->refcount == 0)
+#if HAVE_LIBMEMCACHED_MEMCACHED_H
+    if (c->mc_st)
     {
-        odr_destroy(s->odr);
-        xfree(s);
+        memcached_free(c->mc_st);
+        c->mc_st = 0;
     }
-}
-
-ZOOM_API(int)
-    ZOOM_query_prefix(ZOOM_query s, const char *str)
-{
-    s->query_string = odr_strdup(s->odr, str);
-    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
-    s->z_query->which = Z_Query_type_1;
-    s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
-    if (!s->z_query->u.type_1)
+#endif
+    val = ZOOM_options_get(c->options, "memcached");
+    if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_query_prefix str=%s failed", s, str);
-        s->z_query = 0;
-        return -1;
+#if HAVE_LIBMEMCACHED_MEMCACHED_H
+        c->mc_st = memcached(val, strlen(val));
+        if (!c->mc_st)
+        {
+            ZOOM_set_error(c, ZOOM_ERROR_MEMCACHED, val);
+            return;
+        }
+#else
+        ZOOM_set_error(c, ZOOM_ERROR_MEMCACHED, "not enabled");
+        return;
+#endif
     }
-    yaz_log(log_details, "%p ZOOM_query_prefix str=%s", s, str);
-    return 0;
-}
-
-ZOOM_API(int)
-    ZOOM_query_cql(ZOOM_query s, const char *str)
-{
-    Z_External *ext;
-
-    s->query_string = odr_strdup(s->odr, str);
-
-    ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
-    ext->direct_reference = odr_oiddup(s->odr, yaz_oid_userinfo_cql);
-    ext->indirect_reference = 0;
-    ext->descriptor = 0;
-    ext->which = Z_External_CQL;
-    ext->u.cql = s->query_string;
-    
-    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
-    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;
-}
-
-/*
- * Translate the CQL string client-side into RPN which is passed to
- * the server.  This is useful for server's that don't themselves
- * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
- * only as a place to stash diagnostics if compilation fails; if this
- * information is not needed, a null pointer may be used.
- */
-ZOOM_API(int)
-    ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
-{
-    char *rpn;
-    int ret;
-    ZOOM_connection freeme = 0;
-
-    yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn);
-    if (conn == 0)
-        conn = freeme = ZOOM_connection_create(0);
-
-    rpn = cql2pqf(conn, str);
-    if (freeme != 0)
-        ZOOM_connection_destroy(freeme);
-    if (rpn == 0)
-        return -1;
-
-    ret = ZOOM_query_prefix(s, rpn);
-    xfree(rpn);
-    return ret;
-}
-
-/*
- * Analogous in every way to ZOOM_query_cql2rpn(), except that there
- * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
- * to the server, as the YAZ GFS doesn't know how to handle this.
- */
-ZOOM_API(int)
-    ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
-                       int *ccl_error, const char **error_string,
-                       int *error_pos)
-{
-    int ret;
-    struct ccl_rpn_node *rpn;
-    CCL_bibset bibset = ccl_qual_mk();
-
-    if (config)
-        ccl_qual_buf(bibset, config);
-
-    rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
-    if (!rpn)
-    {
-        *error_string = ccl_err_msg(*ccl_error);
-        ret = -1;
-    }
-    else
+    if (c->sru_mode == zoom_sru_error)
     {
-        WRBUF wr = wrbuf_alloc();
-        ccl_pquery(wr, rpn);
-        ccl_rpn_delete(rpn);
-        ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
-        wrbuf_destroy(wr);
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, val);
+        ZOOM_connection_remove_tasks(c);
+        return;
     }
-    ccl_qual_rm(&bibset);
-    return ret;
-}
 
-ZOOM_API(int)
-    ZOOM_query_sortby(ZOOM_query s, const char *criteria)
-{
-    s->sort_spec = yaz_sort_spec(s->odr, criteria);
-    if (!s->sort_spec)
+    yaz_log(c->log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
+    ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
+
+    if (!c->async)
     {
-        yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s failed",
-                s, criteria);
-        return -1;
+        while (ZOOM_event(1, &c))
+            ;
     }
-    yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s", s, criteria);
-    return 0;
 }
 
-static zoom_ret do_write(ZOOM_connection c);
-
 ZOOM_API(void) ZOOM_resultset_release(ZOOM_resultset r)
 {
-#if ZOOM_RESULT_LISTS
-#else
     if (r->connection)
     {
         /* remove ourselves from the resultsets in connection */
@@ -814,42 +605,33 @@ ZOOM_API(void) ZOOM_resultset_release(ZOOM_resultset r)
         }
         r->connection = 0;
     }
-#endif
 }
 
 ZOOM_API(void)
     ZOOM_connection_destroy(ZOOM_connection c)
 {
-#if ZOOM_RESULT_LISTS
-    ZOOM_resultsets list;
-#else
     ZOOM_resultset r;
-#endif
     if (!c)
         return;
-    yaz_log(log_api, "%p ZOOM_connection_destroy", c);
+    yaz_log(c->log_api, "%p ZOOM_connection_destroy", c);
+
+#if HAVE_LIBMEMCACHED_MEMCACHED_H
+    if (c->mc_st)
+        memcached_free(c->mc_st);
+#endif
     if (c->cs)
         cs_close(c->cs);
 
-#if ZOOM_RESULT_LISTS
-    // Remove the connection's usage of resultsets
-    list = c->resultsets;
-    while (list) {
-        ZOOM_resultsets removed = list;
-        ZOOM_resultset_destroy(list->resultset);
-        list = list->next;
-        xfree(removed);
-    }
-#else
     for (r = c->resultsets; r; r = r->next)
         r->connection = 0;
-#endif
 
     xfree(c->buf_in);
     xfree(c->addinfo);
     xfree(c->diagset);
     odr_destroy(c->odr_in);
     odr_destroy(c->odr_out);
+    if (c->odr_save)
+        odr_destroy(c->odr_save);
     if (c->odr_print)
     {
         odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
@@ -859,8 +641,8 @@ ZOOM_API(void)
     ZOOM_connection_remove_tasks(c);
     ZOOM_connection_remove_events(c);
     xfree(c->host_port);
-    xfree(c->path);
     xfree(c->proxy);
+    xfree(c->tproxy);
     xfree(c->charset);
     xfree(c->lang);
     xfree(c->cookie_out);
@@ -870,6 +652,8 @@ ZOOM_API(void)
     xfree(c->group);
     xfree(c->password);
     xfree(c->sru_version);
+    yaz_cookies_destroy(c->cookies);
+    wrbuf_destroy(c->saveAPDU_wrbuf);
     xfree(c);
 }
 
@@ -879,12 +663,33 @@ void ZOOM_resultset_addref(ZOOM_resultset r)
     {
         yaz_mutex_enter(r->mutex);
         (r->refcount)++;
-        yaz_log(log_details, "%p ZOOM_resultset_addref count=%d",
+        yaz_log(log_details0, "%p ZOOM_resultset_addref count=%d",
                 r, r->refcount);
         yaz_mutex_leave(r->mutex);
     }
 }
 
+static int g_resultsets = 0;
+static YAZ_MUTEX g_resultset_mutex = 0;
+
+/* TODO We need to initialize this before running threaded:
+ * call resultset_use(0)  */
+
+static int resultset_use(int delta) {
+    int resultset_count;
+    if (g_resultset_mutex == 0)
+        yaz_mutex_create(&g_resultset_mutex);
+    yaz_mutex_enter(g_resultset_mutex);
+    g_resultsets += delta;
+    resultset_count = g_resultsets;
+    yaz_mutex_leave(g_resultset_mutex);
+    return resultset_count;
+}
+
+int resultsets_count(void) {
+    return resultset_use(0);
+}
+
 ZOOM_resultset ZOOM_resultset_create(void)
 {
     int i;
@@ -892,13 +697,12 @@ ZOOM_resultset ZOOM_resultset_create(void)
 
     initlog();
 
-    yaz_log(log_details, "%p ZOOM_resultset_create", r);
+    yaz_log(log_details0, "%p ZOOM_resultset_create", r);
     r->refcount = 1;
     r->size = 0;
     r->odr = odr_createmem(ODR_ENCODE);
     r->piggyback = 1;
     r->setname = 0;
-    r->schema = 0;
     r->step = 0;
     for (i = 0; i<RECORD_HASH_SIZE; i++)
         r->record_hash[i] = 0;
@@ -918,6 +722,9 @@ ZOOM_resultset ZOOM_resultset_create(void)
         YAZ_SHPTR_INIT(r->record_wrbuf, w);
     }
 #endif
+    resultset_use(1);
+    r->mc_key = 0;
+    r->live_set = 0;
     return r;
 }
 
@@ -938,20 +745,17 @@ ZOOM_API(ZOOM_resultset)
     ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
 {
     ZOOM_resultset r = ZOOM_resultset_create();
-    ZOOM_task task;
     const char *cp;
+    ZOOM_task task;
     int start, count;
-    const char *syntax, *elementSetName;
-#if ZOOM_RESULT_LISTS
-    ZOOM_resultsets set;
-#endif
-
-    yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
-    r->r_sort_spec = q->sort_spec;
+    const char *syntax, *elementSetName, *schema;
+    yaz_log(c->log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
+    r->r_sort_spec = ZOOM_query_get_sortspec(q);
     r->query = q;
+    ZOOM_query_addref(q);
 
     r->options = ZOOM_options_create_with_parent(c->options);
-    
+
     start = ZOOM_options_get_int(r->options, "start", 0);
     count = ZOOM_options_get_int(r->options, "count", 0);
     {
@@ -964,36 +768,72 @@ ZOOM_API(ZOOM_resultset)
     cp = ZOOM_options_get(r->options, "setname");
     if (cp)
         r->setname = xstrdup(cp);
-    cp = ZOOM_options_get(r->options, "schema");
-    if (cp)
-        r->schema = xstrdup(cp);
 
-    r->databaseNames = set_DatabaseNames(c, c->options, &r->num_databaseNames,
+    r->databaseNames = ZOOM_connection_get_databases(c, c->options, &r->num_databaseNames,
                                          r->odr);
-    
-    r->connection = c;
 
-#if ZOOM_RESULT_LISTS
-    yaz_log(log_details, "%p ZOOM_connection_search: Adding new resultset (%p) to resultsets (%p) ", c, r, c->resultsets);
-    set = xmalloc(sizeof(*set));
-    ZOOM_resultset_addref(r);
-    set->resultset = r;
-    set->next = c->resultsets;
-    c->resultsets = set;
-#else
+    r->connection = c;
     r->next = c->resultsets;
     c->resultsets = r;
+
+#if HAVE_LIBMEMCACHED_MEMCACHED_H
+    r->mc_key = wrbuf_alloc();
+    wrbuf_puts(r->mc_key, "0;");
+    wrbuf_puts(r->mc_key, c->host_port);
+    wrbuf_puts(r->mc_key, ";");
+    if (c->user)
+        wrbuf_puts(r->mc_key, c->user);
+    wrbuf_puts(r->mc_key, ";");
+    if (c->group)
+        wrbuf_puts(r->mc_key, c->group);
+    wrbuf_puts(r->mc_key, ";");
+    if (c->password)
+        wrbuf_sha1_puts(r->mc_key, c->password, 1);
+    wrbuf_puts(r->mc_key, ";");
+    wrbuf_sha1_puts(r->mc_key, ZOOM_query_get_query_string(q), 1);
+    wrbuf_puts(r->mc_key, ";");
+    /* TODO: add sorting */
+    if (c->mc_st)
+    {
+        size_t v_len;
+        uint32_t flags;
+        memcached_return_t rc;
+        char *v = memcached_get(c->mc_st, wrbuf_buf(r->mc_key),
+                                wrbuf_len(r->mc_key), &v_len, &flags, &rc);
+        if (v)
+        {
+            ZOOM_Event event;
+            WRBUF w = wrbuf_alloc();
+
+            wrbuf_write(w, v, v_len);
+            free(v);
+            r->size = odr_atoi(wrbuf_cstr(w));
+
+            yaz_log(YLOG_LOG, "For key %s got value %s",
+                    wrbuf_cstr(r->mc_key), wrbuf_cstr(w));
+
+            wrbuf_destroy(w);
+            event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
+            ZOOM_connection_put_event(c, event);
+            return r;
+        }
+        else
+        {
+            yaz_log(YLOG_LOG, "For key %s got NO value", wrbuf_cstr(r->mc_key));
+        }
+    }
 #endif
+
     if (c->host_port && c->proto == PROTO_HTTP)
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "ZOOM_connection_search: no comstack");
+            yaz_log(c->log_details, "ZOOM_connection_search: no comstack");
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "ZOOM_connection_search: reconnect");
+            yaz_log(c->log_details, "ZOOM_connection_search: reconnect");
             c->reconnect_ok = 1;
         }
     }
@@ -1004,15 +844,15 @@ ZOOM_API(ZOOM_resultset)
     task->u.search.count = count;
     task->u.search.recv_search_fired = 0;
 
-    syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
+    syntax = ZOOM_options_get(r->options, "preferredRecordSyntax");
     task->u.search.syntax = syntax ? xstrdup(syntax) : 0;
     elementSetName = ZOOM_options_get(r->options, "elementSetName");
-    task->u.search.elementSetName = elementSetName 
-        ? xstrdup(elementSetName) : 0;
-   
-    ZOOM_resultset_addref(r);
+    task->u.search.elementSetName = elementSetName ?
+        xstrdup(elementSetName) : 0;
+    schema = ZOOM_options_get(r->options, "schema");
+    task->u.search.schema = schema ? xstrdup(schema) : 0;
 
-    (q->refcount)++;
+    ZOOM_resultset_addref(r);
 
     if (!c->async)
     {
@@ -1041,7 +881,7 @@ ZOOM_API(int)
     if (ZOOM_query_sortby(newq, sort_spec) < 0)
         return -1;
 
-    yaz_log(log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
+    yaz_log(c->log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
             r, r, sort_type, sort_spec);
     if (!c)
         return 0;
@@ -1050,23 +890,23 @@ ZOOM_API(int)
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "%p ZOOM_resultset_sort: no comstack", r);
+            yaz_log(c->log_details, "%p ZOOM_resultset_sort: no comstack", r);
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "%p ZOOM_resultset_sort: prepare reconnect",
+            yaz_log(c->log_details, "%p ZOOM_resultset_sort: prepare reconnect",
                     r);
             c->reconnect_ok = 1;
         }
     }
-    
+
     ZOOM_resultset_cache_reset(r);
     task = ZOOM_connection_add_task(c, ZOOM_TASK_SORT);
     task->u.sort.resultset = r;
     task->u.sort.q = newq;
 
-    ZOOM_resultset_addref(r);  
+    ZOOM_resultset_addref(r);
 
     if (!c->async)
     {
@@ -1077,38 +917,6 @@ ZOOM_API(int)
     return 0;
 }
 
-static void ZOOM_record_release(ZOOM_record rec)
-{
-    if (!rec)
-        return;
-
-#if SHPTR
-    if (rec->record_wrbuf)
-        YAZ_SHPTR_DEC(rec->record_wrbuf, wrbuf_destroy);
-#else
-    if (rec->wrbuf)
-        wrbuf_destroy(rec->wrbuf);
-#endif
-
-    if (rec->odr)
-        odr_destroy(rec->odr);
-}
-
-ZOOM_API(void)
-    ZOOM_resultset_cache_reset(ZOOM_resultset r)
-{
-    int i;
-    for (i = 0; i<RECORD_HASH_SIZE; i++)
-    {
-        ZOOM_record_cache rc;
-        for (rc = r->record_hash[i]; rc; rc = rc->next)
-        {
-            ZOOM_record_release(&rc->rec);
-        }
-        r->record_hash[i] = 0;
-    }
-}
-
 ZOOM_API(void)
     ZOOM_resultset_destroy(ZOOM_resultset r)
 {
@@ -1121,24 +929,25 @@ static void resultset_destroy(ZOOM_resultset r)
         return;
     yaz_mutex_enter(r->mutex);
     (r->refcount)--;
-    yaz_log(log_details, "%p ZOOM_resultset_destroy r=%p count=%d",
+    yaz_log(log_details0, "%p ZOOM_resultset_destroy r=%p count=%d",
             r, r, r->refcount);
     if (r->refcount == 0)
     {
         yaz_mutex_leave(r->mutex);
 
-        yaz_log(log_details, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
+        yaz_log(log_details0, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
         ZOOM_resultset_cache_reset(r);
         ZOOM_resultset_release(r);
         ZOOM_query_destroy(r->query);
         ZOOM_options_destroy(r->options);
         odr_destroy(r->odr);
         xfree(r->setname);
-        xfree(r->schema);
         yaz_mutex_destroy(&r->mutex);
 #if SHPTR
         YAZ_SHPTR_DEC(r->record_wrbuf, wrbuf_destroy);
 #endif
+        wrbuf_destroy(r->mc_key);
+        resultset_use(-1);
         xfree(r);
     }
     else
@@ -1148,27 +957,16 @@ static void resultset_destroy(ZOOM_resultset r)
 ZOOM_API(size_t)
     ZOOM_resultset_size(ZOOM_resultset r)
 {
-    yaz_log(log_details, "ZOOM_resultset_size r=%p count=" ODR_INT_PRINTF,
-            r, r->size);
     return r->size;
 }
 
-static void do_close(ZOOM_connection c)
-{
-    if (c->cs)
-        cs_close(c->cs);
-    c->cs = 0;
-    ZOOM_connection_set_mask(c, 0);
-    c->state = STATE_IDLE;
-}
-
-static int ZOOM_test_reconnect(ZOOM_connection c)
+int ZOOM_test_reconnect(ZOOM_connection c)
 {
     ZOOM_Event event;
 
     if (!c->reconnect_ok)
         return 0;
-    do_close(c);
+    ZOOM_connection_close(c);
     c->reconnect_ok = 0;
     c->tasks->running = 0;
     ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
@@ -1189,7 +987,7 @@ static void ZOOM_resultset_retrieve(ZOOM_resultset r,
 
     if (!r)
         return;
-    yaz_log(log_details, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
+    yaz_log(log_details0, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
             " count=%d", r, force_sync, start, count);
     c = r->connection;
     if (!c)
@@ -1199,36 +997,29 @@ static void ZOOM_resultset_retrieve(ZOOM_resultset r,
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "%p ZOOM_resultset_retrieve: no comstack", r);
+            yaz_log(log_details0, "%p ZOOM_resultset_retrieve: no comstack", r);
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "%p ZOOM_resultset_retrieve: prepare "
+            yaz_log(log_details0, "%p ZOOM_resultset_retrieve: prepare "
                     "reconnect", r);
             c->reconnect_ok = 1;
         }
     }
-    task = ZOOM_connection_add_task(c, ZOOM_TASK_RETRIEVE);
-    task->u.retrieve.resultset = r;
-    task->u.retrieve.start = start;
-    task->u.retrieve.count = count;
+    task = ZOOM_connection_add_task(c, ZOOM_TASK_SEARCH);
+    task->u.search.resultset = r;
+    task->u.search.start = start;
+    task->u.search.count = count;
 
-    syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
-    task->u.retrieve.syntax = syntax ? xstrdup(syntax) : 0;
+    syntax = ZOOM_options_get(r->options, "preferredRecordSyntax");
+    task->u.search.syntax = syntax ? xstrdup(syntax) : 0;
     elementSetName = ZOOM_options_get(r->options, "elementSetName");
-    task->u.retrieve.elementSetName = elementSetName 
+    task->u.search.elementSetName = elementSetName
         ? xstrdup(elementSetName) : 0;
 
     cp = ZOOM_options_get(r->options, "schema");
-    if (cp)
-    {
-        if (!r->schema || strcmp(r->schema, cp))
-        {
-            xfree(r->schema);
-            r->schema = xstrdup(cp);
-        }
-    }
+    task->u.search.schema = cp ? xstrdup(cp) : 0;
 
     ZOOM_resultset_addref(r);
 
@@ -1245,7 +1036,7 @@ ZOOM_API(void)
 
     if (!r)
         return ;
-    yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
+    yaz_log(log_api0, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
             r, r, (long) start, (long) count);
     if (count && recs)
         force_present = 1;
@@ -1276,6 +1067,15 @@ ZOOM_API(ZOOM_facet_field)
     return 0;
 }
 
+ZOOM_API(ZOOM_facet_field)
+    ZOOM_resultset_get_facet_field_by_index(ZOOM_resultset r, int index) {
+    int num = r->num_facets;
+    ZOOM_facet_field *facets = r->facets;
+    if (index >= 0 && index < num) {
+        return facets[index];
+    }
+    return 0;
+}
 
 ZOOM_API(ZOOM_facet_field *)
     ZOOM_resultset_facets(ZOOM_resultset r)
@@ -1284,7 +1084,7 @@ ZOOM_API(ZOOM_facet_field *)
 }
 
 ZOOM_API(const char**)
-    ZOOM_resultset_facet_names(ZOOM_resultset r)
+    ZOOM_resultset_facets_names(ZOOM_resultset r)
 {
     return (const char **) r->facets_names;
 }
@@ -1312,7 +1112,7 @@ 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",
@@ -1322,48 +1122,29 @@ static void get_cert(ZOOM_connection c)
 }
 
 static zoom_ret do_connect_host(ZOOM_connection c,
-                                const char *effective_host,
                                 const char *logical_url);
 
 static zoom_ret do_connect(ZOOM_connection c)
 {
-    const char *effective_host;
-
-    if (c->proxy)
-        effective_host = c->proxy;
-    else
-        effective_host = c->host_port;
-    return do_connect_host(c, effective_host, c->host_port);
+    return do_connect_host(c, c->host_port);
 }
 
-static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
-    const char *logical_url)
+static zoom_ret do_connect_host(ZOOM_connection c, const char *logical_url)
 {
     void *add;
 
-    yaz_log(log_details, "%p do_connect effective_host=%s", c, effective_host);
-
     if (c->cs)
         cs_close(c->cs);
-    c->cs = cs_create_host(effective_host, 0, &add);
+    c->cs = cs_create_host_proxy(logical_url, 0, &add,
+                                 c->tproxy ? c->tproxy : c->proxy);
 
     if (c->cs && c->cs->protocol == PROTO_HTTP)
     {
 #if YAZ_HAVE_XML2
-        if (logical_url)
-        {
-            const char *db = 0;
-            
-            c->proto = PROTO_HTTP;
-            cs_get_host_args(logical_url, &db);
-            xfree(c->path);
-            
-            c->path = xmalloc(strlen(db) * 3 + 2);
-            yaz_encode_sru_dbpath_buf(c->path, db);
-        }
+        c->proto = PROTO_HTTP;
 #else
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
+        ZOOM_connection_close(c);
         return zoom_complete;
 #endif
     }
@@ -1376,7 +1157,7 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
             ZOOM_connection_put_event(c, event);
             get_cert(c);
             if (c->proto == PROTO_Z3950)
-                ZOOM_connection_send_init(c);
+                ZOOM_connection_Z3950_send_init(c);
             else
             {
                 /* no init request for SRW .. */
@@ -1396,1588 +1177,51 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
             if (c->cs->io_pending & CS_WANT_READ)
                 mask += ZOOM_SELECT_READ;
             ZOOM_connection_set_mask(c, mask);
-            c->state = STATE_CONNECTING; 
+            c->state = STATE_CONNECTING;
             return zoom_pending;
         }
     }
     c->state = STATE_IDLE;
-    set_ZOOM_error(c, ZOOM_ERROR_CONNECT, logical_url);
+    ZOOM_set_error(c, ZOOM_ERROR_CONNECT, logical_url);
     return zoom_complete;
 }
 
-static void otherInfo_attach(ZOOM_connection c, Z_APDU *a, ODR out)
-{
-    int i;
-    for (i = 0; i<200; i++)
-    {
-        size_t len;
-        Odr_oid *oid;
-        Z_OtherInformation **oi;
-        char buf[80];
-        const char *val;
-        const char *cp;
-
-        sprintf(buf, "otherInfo%d", i);
-        val = ZOOM_options_get(c->options, buf);
-        if (!val)
-            break;
-        cp = strchr(val, ':');
-        if (!cp)
-            continue;
-        len = cp - val;
-        if (len >= sizeof(buf))
-            len = sizeof(buf)-1;
-        memcpy(buf, val, len);
-        buf[len] = '\0';
-        
-        oid = yaz_string_to_oid_odr(yaz_oid_std(), CLASS_USERINFO,
-                                    buf, out);
-        if (!oid)
-            continue;
-        
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, oid, 1, cp+1);
-    }
-}
-
-static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
-{
-    assert(a);
-    if (c->cookie_out)
-    {
-        Z_OtherInformation **oi;
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_cookie, 
-                              1, c->cookie_out);
-    }
-    if (c->client_IP)
-    {
-        Z_OtherInformation **oi;
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_client_ip, 
-                              1, c->client_IP);
-    }
-    otherInfo_attach(c, a, out);
-    if (!z_APDU(out, &a, 0, 0))
-    {
-        FILE *outf = fopen("/tmp/apdu.txt", "a");
-        if (a && outf)
-        {
-            ODR odr_pr = odr_createmem(ODR_PRINT);
-            fprintf(outf, "a=%p\n", a);
-            odr_setprint(odr_pr, outf);
-            z_APDU(odr_pr, &a, 0, 0);
-            odr_destroy(odr_pr);
-        }
-        yaz_log(log_api, "%p encoding_APDU: encoding failed", c);
-        set_ZOOM_error(c, ZOOM_ERROR_ENCODE, 0);
-        odr_reset(out);
-        return -1;
-    }
-    if (c->odr_print)
-        z_APDU(c->odr_print, &a, 0, 0);
-    yaz_log(log_details, "%p encoding_APDU encoding OK", c);
-    return 0;
-}
-
-static zoom_ret send_APDU(ZOOM_connection c, Z_APDU *a)
-{
-    ZOOM_Event event;
-    assert(a);
-    if (encode_APDU(c, a, c->odr_out))
-        return zoom_complete;
-    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);
-    odr_reset(c->odr_out);
-    return do_write(c);
-}
-
-/* returns 1 if PDU was sent OK (still pending )
-   0 if PDU was not sent OK (nothing to wait for) 
-*/
-
-static zoom_ret ZOOM_connection_send_init(ZOOM_connection c)
-{
-    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
-    Z_InitRequest *ireq = apdu->u.initRequest;
-    Z_IdAuthentication *auth = (Z_IdAuthentication *)
-        odr_malloc(c->odr_out, sizeof(*auth));
-
-    ODR_MASK_SET(ireq->options, Z_Options_search);
-    ODR_MASK_SET(ireq->options, Z_Options_present);
-    ODR_MASK_SET(ireq->options, Z_Options_scan);
-    ODR_MASK_SET(ireq->options, Z_Options_sort);
-    ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
-    ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
-    
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
-    
-    ireq->implementationId =
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationId"),
-                    ireq->implementationId);
-    
-    ireq->implementationName = 
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationName"),
-                    odr_prepend(c->odr_out, "ZOOM-C",
-                                ireq->implementationName));
-    
-    ireq->implementationVersion = 
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationVersion"),
-                                ireq->implementationVersion);
-    
-    *ireq->maximumRecordSize = c->maximum_record_size;
-    *ireq->preferredMessageSize = c->preferred_message_size;
-    
-    if (c->group || c->password)
-    {
-        Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
-        pass->groupId = odr_strdup_null(c->odr_out, c->group);
-        pass->userId = odr_strdup_null(c->odr_out, c->user);
-        pass->password = odr_strdup_null(c->odr_out, c->password);
-        auth->which = Z_IdAuthentication_idPass;
-        auth->u.idPass = pass;
-        ireq->idAuthentication = auth;
-    }
-    else if (c->user)
-    {
-        auth->which = Z_IdAuthentication_open;
-        auth->u.open = odr_strdup(c->odr_out, c->user);
-        ireq->idAuthentication = auth;
-    }
-    if (c->proxy)
-    {
-        yaz_oi_set_string_oid(&ireq->otherInfo, c->odr_out,
-                              yaz_oid_userinfo_proxy, 1, c->host_port);
-    }
-    if (c->charset || c->lang)
-    {
-        Z_OtherInformation **oi;
-        Z_OtherInformationUnit *oi_unit;
-        
-        yaz_oi_APDU(apdu, &oi);
-        
-        if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
-        {
-            ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
-            oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
-            oi_unit->information.externallyDefinedInfo =
-                yaz_set_proposal_charneg_list(c->odr_out, " ",
-                                              c->charset, c->lang, 1);
-        }
-    }
-    assert(apdu);
-    return send_APDU(c, apdu);
-}
-
-#if YAZ_HAVE_XML2
-static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
-{
-    Z_GDU *gdu;
-    ZOOM_Event event;
-    const char *database =  ZOOM_options_get(c->options, "databaseName");
-    char *fdatabase = 0;
-    
-    if (database)
-        fdatabase = yaz_encode_sru_dbpath_odr(c->odr_out, database);
-    gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port,
-                                       fdatabase ? fdatabase : c->path);
-
-    if (c->sru_mode == zoom_sru_get)
-    {
-        yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
-    }
-    else if (c->sru_mode == zoom_sru_post)
-    {
-        yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
-    }
-    else if (c->sru_mode == zoom_sru_soap)
-    {
-        yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
-    }
-    if (!z_GDU(c->odr_out, &gdu, 0, 0))
-        return zoom_complete;
-    if (c->odr_print)
-        z_GDU(c->odr_print, &gdu, 0, 0);
-    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);
-    odr_reset(c->odr_out);
-    return do_write(c);
-}
-#endif
-
-#if YAZ_HAVE_XML2
-static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type)
-{
-    Z_SRW_PDU *sr = yaz_srw_get_pdu(c->odr_out, type, c->sru_version);
-    sr->username = c->user;
-    sr->password = c->password;
-    return sr;
-}
-#endif
-
-#if YAZ_HAVE_XML2
-static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
-{
-    int i;
-    int *start, *count;
-    ZOOM_resultset resultset = 0;
-    Z_SRW_PDU *sr = 0;
-    const char *option_val = 0;
-
-    if (c->error)                  /* don't continue on error */
-        return zoom_complete;
-    assert(c->tasks);
-    switch(c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        if (!resultset->setname)
-            resultset->setname = xstrdup("default");
-        ZOOM_options_set(resultset->options, "setname", resultset->setname);
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-
-        if (*start >= resultset->size)
-            return zoom_complete;
-        if (*start + *count > resultset->size)
-            *count = resultset->size - *start;
-
-        for (i = 0; i < *count; i++)
-        {
-            ZOOM_record rec =
-                record_cache_lookup(resultset, i + *start,
-                                    c->tasks->u.retrieve.syntax,
-                                    c->tasks->u.retrieve.elementSetName);
-            if (!rec)
-                break;
-            else
-            {
-                ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-                ZOOM_connection_put_event(c, event);
-            }
-        }
-        *start += i;
-        *count -= i;
-
-        if (*count == 0)
-            return zoom_complete;
-        break;
-    default:
-        return zoom_complete;
-    }
-    assert(resultset->query);
-        
-    sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
-    if (resultset->query->z_query->which == Z_Query_type_104
-        && resultset->query->z_query->u.type_104->which == Z_External_CQL)
-    {
-        sr->u.request->query_type = Z_SRW_query_type_cql;
-        sr->u.request->query.cql =resultset->query->z_query->u.type_104->u.cql;
-    }
-    else if (resultset->query->z_query->which == Z_Query_type_1 &&
-             resultset->query->z_query->u.type_1)
-    {
-        sr->u.request->query_type = Z_SRW_query_type_pqf;
-        sr->u.request->query.pqf = resultset->query->query_string;
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-    sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
-    sr->u.request->maximumRecords = odr_intdup(
-        c->odr_out, (resultset->step > 0 && resultset->step < *count) ? 
-        resultset->step : *count);
-    sr->u.request->recordSchema = resultset->schema;
-    
-    option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
-    if (option_val)
-        sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
-
-    option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
-    yaz_encode_sru_extra(sr, c->odr_out, option_val);
-    return send_srw(c, sr);
-}
-#else
-static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
-{
-    return zoom_complete;
-}
-#endif
-
-static zoom_ret ZOOM_connection_send_search(ZOOM_connection c)
-{
-    ZOOM_resultset r;
-    int lslb, ssub, mspn;
-    const char *syntax;
-    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
-    Z_SearchRequest *search_req = apdu->u.searchRequest;
-    const char *elementSetName;
-    const char *smallSetElementSetName;
-    const char *mediumSetElementSetName;
-    const char *facets;
-
-    assert(c->tasks);
-    assert(c->tasks->which == ZOOM_TASK_SEARCH);
-
-    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  =
-        ZOOM_options_get(r->options, "smallSetElementSetName");
-    mediumSetElementSetName =
-        ZOOM_options_get(r->options, "mediumSetElementSetName");
-
-    if (!smallSetElementSetName)
-        smallSetElementSetName = elementSetName;
-
-    if (!mediumSetElementSetName)
-        mediumSetElementSetName = elementSetName;
-
-    facets = ZOOM_options_get(r->options, "facets");
-    if (facets) {
-        Z_FacetList *facet_list = yaz_pqf_parse_facet_list(c->odr_out, facets);
-        if (facet_list) {
-            Z_OtherInformation **oi;
-            yaz_oi_APDU(apdu, &oi);
-            if (facet_list) {
-                yaz_oi_set_facetlist_oid(oi, c->odr_out, yaz_oid_userinfo_facet_1, 1, facet_list);
-            }
-        }
-    }
-
-    assert(r);
-    assert(r->query);
-
-    /* prepare query for the search request */
-    search_req->query = r->query->z_query;
-    if (!search_req->query)
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-        return zoom_complete;
-    }
-    if (r->query->z_query->which == Z_Query_type_1 || 
-        r->query->z_query->which == Z_Query_type_101)
-    {
-        const char *cp = ZOOM_options_get(r->options, "rpnCharset");
-        if (cp)
-        {
-            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
-            if (cd)
-            {
-                int r;
-                search_req->query = yaz_copy_Z_Query(search_req->query,
-                                                     c->odr_out);
-                
-                r = yaz_query_charset_convert_rpnquery_check(
-                    search_req->query->u.type_1,
-                    c->odr_out, cd);
-                yaz_iconv_close(cd);
-                if (r)
-                {  /* query could not be char converted */
-                    set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-                    return zoom_complete;
-                }
-            }
-        }
-    }
-    search_req->databaseNames = r->databaseNames;
-    search_req->num_databaseNames = r->num_databaseNames;
-
-    /* get syntax (no need to provide unless piggyback is in effect) */
-    syntax = c->tasks->u.search.syntax;
-
-    lslb = ZOOM_options_get_int(r->options, "largeSetLowerBound", -1);
-    ssub = ZOOM_options_get_int(r->options, "smallSetUpperBound", -1);
-    mspn = ZOOM_options_get_int(r->options, "mediumSetPresentNumber", -1);
-    if (lslb != -1 && ssub != -1 && mspn != -1)
-    {
-        /* So're a Z39.50 expert? Let's hope you don't do sort */
-        *search_req->largeSetLowerBound = lslb;
-        *search_req->smallSetUpperBound = ssub;
-        *search_req->mediumSetPresentNumber = mspn;
-    }
-    else if (c->tasks->u.search.start == 0 && c->tasks->u.search.count > 0
-             && r->piggyback && !r->r_sort_spec && !r->schema)
-    {
-        /* Regular piggyback - do it unless we're going to do sort */
-        *search_req->largeSetLowerBound = 2000000000;
-        *search_req->smallSetUpperBound = 1;
-        *search_req->mediumSetPresentNumber = 
-            r->step>0 ? r->step : c->tasks->u.search.count;
-    }
-    else
-    {
-        /* non-piggyback. Need not provide elementsets or syntaxes .. */
-        smallSetElementSetName = 0;
-        mediumSetElementSetName = 0;
-        syntax = 0;
-    }
-    if (smallSetElementSetName && *smallSetElementSetName)
-    {
-        Z_ElementSetNames *esn = (Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, smallSetElementSetName);
-        search_req->smallSetElementSetNames = esn;
-    }
-    if (mediumSetElementSetName && *mediumSetElementSetName)
-    {
-        Z_ElementSetNames *esn =(Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, mediumSetElementSetName);
-        search_req->mediumSetElementSetNames = esn;
-    }
-    if (syntax)
-        search_req->preferredRecordSyntax =
-            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
-    
-    if (!r->setname)
-    {
-        if (c->support_named_resultsets)
-        {
-            char setname[14];
-            int ord;
-            /* find the lowest unused ordinal so that we re-use
-               result sets on the server. */
-            for (ord = 1; ; ord++)
-            {
-#if ZOOM_RESULT_LISTS
-                ZOOM_resultsets rsp;
-                sprintf(setname, "%d", ord);
-                for (rsp = c->resultsets; rsp; rsp = rsp->next)
-                    if (rsp->resultset->setname && !strcmp(rsp->resultset->setname, setname))
-                        break;
-                if (!rsp)
-                    break;
-#else
-                ZOOM_resultset rp;
-                sprintf(setname, "%d", ord);
-                for (rp = c->resultsets; rp; rp = rp->next)
-                    if (rp->setname && !strcmp(rp->setname, setname))
-                        break;
-                if (!rp)
-                    break;
-#endif
-
-            }
-            r->setname = xstrdup(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);
-    return send_APDU(c, apdu);
-}
-
-static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
-{
-    char oid_name_buf[OID_STR_MAX];
-    const char *oid_name;
-    char *addinfo = 0;
-
-    oid_name = yaz_oid_to_string_buf(r->diagnosticSetId, 0, oid_name_buf);
-    switch (r->which)
-    {
-    case Z_DefaultDiagFormat_v2Addinfo:
-        addinfo = r->u.v2Addinfo;
-        break;
-    case Z_DefaultDiagFormat_v3Addinfo:
-        addinfo = r->u.v3Addinfo;
-        break;
-    }
-    xfree(c->addinfo);
-    c->addinfo = 0;
-    set_dset_error(c, *r->condition, oid_name, 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)
-{
-    char *buf;
-    int size;
-    ODR odr_enc;
-    ZOOM_record nrec;
-
-    odr_enc = odr_createmem(ODR_ENCODE);
-    if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
-        return 0;
-    buf = odr_getbuf(odr_enc, &size, 0);
-    
-    nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
-    yaz_log(log_details, "ZOOM_record create");
-    nrec->odr = odr_createmem(ODR_DECODE);
-#if SHPTR
-    nrec->record_wrbuf = 0;
-#else
-    nrec->wrbuf = 0;
-#endif
-    odr_setbuf(nrec->odr, buf, size, 0);
-    z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
-    
-    nrec->schema = odr_strdup_null(nrec->odr, srec->schema);
-    nrec->diag_uri = odr_strdup_null(nrec->odr, srec->diag_uri);
-    nrec->diag_message = odr_strdup_null(nrec->odr, srec->diag_message);
-    nrec->diag_details = odr_strdup_null(nrec->odr, srec->diag_details);
-    nrec->diag_set = odr_strdup_null(nrec->odr, srec->diag_set);
-    odr_destroy(odr_enc);
-    return nrec;
-}
-
-ZOOM_API(ZOOM_record)
-    ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
-{
-    const char *syntax =
-        ZOOM_options_get(s->options, "preferredRecordSyntax"); 
-    const char *elementSetName =
-        ZOOM_options_get(s->options, "elementSetName");
-
-    return record_cache_lookup(s, pos, syntax, elementSetName);
-}
-
-ZOOM_API(ZOOM_record)
-    ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
-{
-    ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
-
-    if (!rec)
-    {
-        /*
-         * MIKE: I think force_sync should always be zero, but I don't
-         * want to make this change until I get the go-ahead from
-         * Adam, in case something depends on the old synchronous
-         * behaviour.
-         */
-        int force_sync = 1;
-        if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
-        ZOOM_resultset_retrieve(r, force_sync, pos, 1);
-        rec = ZOOM_resultset_record_immediate(r, pos);
-    }
-    return rec;
-}
-
-ZOOM_API(void)
-    ZOOM_record_destroy(ZOOM_record rec)
-{
-    ZOOM_record_release(rec);
-    yaz_log(log_details, "ZOOM_record destroy");
-    xfree(rec);
-}
-
-
-static yaz_iconv_t iconv_create_charset(const char *record_charset)
-{
-    char to[40];
-    char from[40];
-    yaz_iconv_t cd = 0;
-
-    *from = '\0';
-    strcpy(to, "UTF-8");
-    if (record_charset && *record_charset)
-    {
-        /* Use "from,to" or just "from" */
-        const char *cp = strchr(record_charset, ',');
-        size_t clen = strlen(record_charset);
-        if (cp && cp[1])
-        {
-            strncpy( to, cp+1, sizeof(to)-1);
-            to[sizeof(to)-1] = '\0';
-            clen = cp - record_charset;
-        }
-        if (clen > sizeof(from)-1)
-            clen = sizeof(from)-1;
-        
-        if (clen)
-            strncpy(from, record_charset, clen);
-        from[clen] = '\0';
-    }
-    if (*from && *to)
-        cd = yaz_iconv_open(to, from);
-    return cd;
-}
-
-static const char *return_marc_record(WRBUF wrbuf,
-                                      int marc_type,
-                                      int *len,
-                                      const char *buf, int sz,
-                                      const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-    yaz_marc_t mt = yaz_marc_create();
-    const char *ret_string = 0;
-
-    if (cd)
-        yaz_marc_iconv(mt, cd);
-    yaz_marc_xml(mt, marc_type);
-    if (yaz_marc_decode_wrbuf(mt, buf, sz, wrbuf) > 0)
-    {
-        if (len)
-            *len = wrbuf_len(wrbuf);
-        ret_string = wrbuf_cstr(wrbuf);
-    }
-    yaz_marc_destroy(mt);
-    if (cd)
-        yaz_iconv_close(cd);
-    return ret_string;
-}
-
-static const char *return_opac_record(WRBUF wrbuf,
-                                      int marc_type,
-                                      int *len,
-                                      Z_OPACRecord *opac_rec,
-                                      const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-    yaz_marc_t mt = yaz_marc_create();
-
-    if (cd)
-        yaz_marc_iconv(mt, cd);
-    yaz_marc_xml(mt, marc_type);
-
-    yaz_opac_decode_wrbuf(mt, opac_rec, wrbuf);
-    yaz_marc_destroy(mt);
-
-    if (cd)
-        yaz_iconv_close(cd);
-    if (len)
-        *len = wrbuf_len(wrbuf);
-    return wrbuf_cstr(wrbuf);
-}
-
-static const char *return_string_record(WRBUF wrbuf,
-                                        int *len,
-                                        const char *buf, int sz,
-                                        const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-
-    if (cd)
-    {
-        wrbuf_iconv_write(wrbuf, cd, buf, sz);
-        wrbuf_iconv_reset(wrbuf, cd);
-
-        buf = wrbuf_cstr(wrbuf);
-        sz = wrbuf_len(wrbuf);
-        yaz_iconv_close(cd);
-    }
-    if (len)
-        *len = sz;
-    return buf;
-}
-
-static const char *return_record_wrbuf(WRBUF wrbuf, int *len,
-                                       Z_NamePlusRecord *npr,
-                                       int marctype, const char *charset)
-{
-    Z_External *r = (Z_External *) npr->u.databaseRecord;
-    const Odr_oid *oid = r->direct_reference;
-
-    wrbuf_rewind(wrbuf);
-    /* render bibliographic record .. */
-    if (r->which == Z_External_OPAC)
-    {
-        return return_opac_record(wrbuf, marctype, len,
-                                  r->u.opac, charset);
-    }
-    if (r->which == Z_External_sutrs)
-        return return_string_record(wrbuf, len,
-                                    (char*) r->u.sutrs->buf,
-                                    r->u.sutrs->len,
-                                    charset);
-    else if (r->which == Z_External_octet)
-    {
-        if (yaz_oid_is_iso2709(oid))
-        {
-            const char *ret_buf = return_marc_record(
-                wrbuf, marctype, len,
-                (const char *) r->u.octet_aligned->buf,
-                r->u.octet_aligned->len,
-                charset);
-            if (ret_buf)
-                return ret_buf;
-            /* bad ISO2709. Return fail unless raw (ISO2709) is wanted */
-            if (marctype != YAZ_MARC_ISO2709)
-                return 0;
-        }
-        return return_string_record(wrbuf, len,
-                                    (const char *) r->u.octet_aligned->buf,
-                                    r->u.octet_aligned->len,
-                                    charset);
-    }
-    else if (r->which == Z_External_grs1)
-    {
-        yaz_display_grs1(wrbuf, r->u.grs1, 0);
-        return return_string_record(wrbuf, len,
-                                    wrbuf_buf(wrbuf),
-                                    wrbuf_len(wrbuf),
-                                    charset);
-    }
-    return 0;
-}
-    
-ZOOM_API(int)
-    ZOOM_record_error(ZOOM_record rec, const char **cp,
-                      const char **addinfo, const char **diagset)
-{
-    Z_NamePlusRecord *npr;
-    
-    if (!rec)
-        return 0;
-
-    npr = rec->npr;
-    if (rec->diag_uri)
-    {
-        if (cp)
-            *cp = rec->diag_message;
-        if (addinfo)
-            *addinfo = rec->diag_details;
-        if (diagset)
-            *diagset = rec->diag_set;
-        return uri_to_code(rec->diag_uri);
-    }
-    if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
-    {
-        Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
-        int error = YAZ_BIB1_UNSPECIFIED_ERROR;
-        const char *add = 0;
-
-        if (diag_rec->which == Z_DiagRec_defaultFormat)
-        {
-            Z_DefaultDiagFormat *ddf = diag_rec->u.defaultFormat;
-            oid_class oclass;
-    
-            error = *ddf->condition;
-            switch (ddf->which)
-            {
-            case Z_DefaultDiagFormat_v2Addinfo:
-                add = ddf->u.v2Addinfo;
-                break;
-            case Z_DefaultDiagFormat_v3Addinfo:
-                add = ddf->u.v3Addinfo;
-                break;
-            }
-            if (diagset)
-                *diagset =
-                    yaz_oid_to_string(yaz_oid_std(),
-                                      ddf->diagnosticSetId, &oclass);
-        }
-        else
-        {
-            if (diagset)
-                *diagset = "Bib-1";
-        }
-        if (addinfo)
-            *addinfo = add ? add : "";
-        if (cp)
-            *cp = diagbib1_str(error);
-        return error;
-    }
-    return 0;
-}
-
-static const char *get_record_format(WRBUF wrbuf, int *len,
-                                     Z_NamePlusRecord *npr,
-                                     int marctype, const char *charset,
-                                     const char *format)
-{
-    const char *res = return_record_wrbuf(wrbuf, len, npr, marctype, charset);
-#if YAZ_HAVE_XML2
-    if (*format == '1' && len)
-    {
-        /* try to XML format res */
-        xmlDocPtr doc;
-        xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
-        doc = xmlParseMemory(res, *len);
-        if (doc)
-        {
-            xmlChar *xml_mem;
-            int xml_size;
-            xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
-            wrbuf_rewind(wrbuf);
-            wrbuf_write(wrbuf, (const char *) xml_mem, xml_size);
-            xmlFree(xml_mem);
-            xmlFreeDoc(doc);
-            res = wrbuf_cstr(wrbuf);
-            *len = wrbuf_len(wrbuf);
-        } 
-    }
-#endif
-    return res;
-}
-
-
-static const char *npr_format(Z_NamePlusRecord *npr, const char *schema,
-                              WRBUF wrbuf,
-                              const char *type_spec, int *len)
-{
-    size_t i;
-    char type[40];
-    char charset[40];
-    char format[3];
-    const char *cp = type_spec;
-
-    for (i = 0; cp[i] && cp[i] != ';' && cp[i] != ' ' && i < sizeof(type)-1;
-         i++)
-        type[i] = cp[i];
-    type[i] = '\0';
-    charset[0] = '\0';
-    format[0] = '\0';
-    while (1)
-    {
-        while (cp[i] == ' ')
-            i++;
-        if (cp[i] != ';')
-            break;
-        i++;
-        while (cp[i] == ' ')
-            i++;
-        if (!strncmp(cp + i, "charset=", 8))
-        {
-            size_t j = 0;
-            i = i + 8; /* skip charset= */
-            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
-            {
-                if (j < sizeof(charset)-1)
-                    charset[j++] = cp[i];
-            }
-            charset[j] = '\0';
-        }
-        else if (!strncmp(cp + i, "format=", 7))
-        {
-            size_t j = 0; 
-            i = i + 7;
-            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
-            {
-                if (j < sizeof(format)-1)
-                    format[j++] = cp[i];
-            }
-            format[j] = '\0';
-        } 
-    }
-    if (!strcmp(type, "database"))
-    {
-        if (len)
-            *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
-        return npr->databaseName;
-    }
-    else if (!strcmp(type, "schema"))
-    {
-        if (len)
-            *len = schema ? strlen(schema) : 0;
-        return schema;
-    }
-    else if (!strcmp(type, "syntax"))
-    {
-        const char *desc = 0;   
-        if (npr->which == Z_NamePlusRecord_databaseRecord)
-        {
-            Z_External *r = (Z_External *) npr->u.databaseRecord;
-            desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
-        }
-        if (!desc)
-            desc = "none";
-        if (len)
-            *len = strlen(desc);
-        return desc;
-    }
-    if (npr->which != Z_NamePlusRecord_databaseRecord)
-        return 0;
-
-    /* from now on - we have a database record .. */
-    if (!strcmp(type, "render"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_LINE, charset, format);
-    }
-    else if (!strcmp(type, "xml"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
-                                 format);
-    }
-    else if (!strcmp(type, "txml"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_TURBOMARC, charset,
-                                 format);
-    }
-    else if (!strcmp(type, "raw"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_ISO2709, charset,
-            format);
-    }
-    else if (!strcmp(type, "ext"))
-    {
-        if (len) *len = -1;
-        return (const char *) npr->u.databaseRecord;
-    }
-    else if (!strcmp(type, "opac"))
-    {
-        if (npr->u.databaseRecord->which == Z_External_OPAC)
-            return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
-                                     format);
-    }
-    return 0;
-}
-
-ZOOM_API(const char *)
-    ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
-{
-    WRBUF wrbuf;
-    
-    if (len)
-        *len = 0; /* default return */
-        
-    if (!rec || !rec->npr)
-        return 0;
-
-#if SHPTR
-    if (!rec->record_wrbuf)
-    {
-        WRBUF w = wrbuf_alloc();
-        YAZ_SHPTR_INIT(rec->record_wrbuf, w);
-    }
-    wrbuf = rec->record_wrbuf->ptr;
-#else
-    if (!rec->wrbuf)
-        rec->wrbuf = wrbuf_alloc();
-    wrbuf = rec->wrbuf;
-#endif
-    return npr_format(rec->npr, rec->schema, wrbuf, type_spec, len);
-}
-
-static int strcmp_null(const char *v1, const char *v2)
-{
-    if (!v1 && !v2)
-        return 0;
-    if (!v1 || !v2)
-        return -1;
-    return strcmp(v1, v2);
-}
-
-static size_t record_hash(int pos)
-{
-    if (pos < 0)
-        pos = 0;
-    return pos % RECORD_HASH_SIZE;
-}
-
-static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
-                             int pos,
-                             const char *syntax, const char *elementSetName,
-                             const char *schema,
-                             Z_SRW_diagnostic *diag)
-{
-    ZOOM_record_cache rc = 0;
-    
-    ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-    ZOOM_connection_put_event(r->connection, event);
-
-    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
-    {
-        if (pos == rc->pos 
-            && strcmp_null(r->schema, rc->schema) == 0
-            && strcmp_null(elementSetName,rc->elementSetName) == 0
-            && strcmp_null(syntax, rc->syntax) == 0)
-            break;
-    }
-    if (!rc)
-    {
-        rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
-        rc->rec.odr = 0;
-#if SHPTR
-        YAZ_SHPTR_INC(r->record_wrbuf);
-        rc->rec.record_wrbuf = r->record_wrbuf;
-#else
-        rc->rec.wrbuf = 0;
-#endif
-        rc->elementSetName = odr_strdup_null(r->odr, elementSetName);
-        
-        rc->syntax = odr_strdup_null(r->odr, syntax);
-        
-        rc->schema = odr_strdup_null(r->odr, r->schema);
-
-        rc->pos = pos;
-        rc->next = r->record_hash[record_hash(pos)];
-        r->record_hash[record_hash(pos)] = rc;
-    }
-    rc->rec.npr = npr;
-    rc->rec.schema = odr_strdup_null(r->odr, schema);
-    rc->rec.diag_set = 0;
-    rc->rec.diag_uri = 0;
-    rc->rec.diag_message = 0;
-    rc->rec.diag_details = 0;
-    if (diag)
-    {
-        if (diag->uri)
-        {
-            char *cp;
-            rc->rec.diag_set = odr_strdup(r->odr, diag->uri);
-            if ((cp = strrchr(rc->rec.diag_set, '/')))
-                *cp = '\0';
-            rc->rec.diag_uri = odr_strdup(r->odr, diag->uri);
-        }
-        rc->rec.diag_message = odr_strdup_null(r->odr, diag->message);            
-        rc->rec.diag_details = odr_strdup_null(r->odr, diag->details);
-    }
-}
-
-static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
-                                       const char *syntax,
-                                       const char *elementSetName)
-{
-    ZOOM_record_cache rc;
-    
-    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
-    {
-        if (pos == rc->pos)
-        {
-            if (strcmp_null(r->schema, rc->schema))
-                continue;
-            if (strcmp_null(elementSetName,rc->elementSetName))
-                continue;
-            if (strcmp_null(syntax, rc->syntax))
-                continue;
-            return &rc->rec;
-        }
-    }
-    return 0;
-}
-                                             
-static void handle_records(ZOOM_connection c, Z_Records *sr,
-                           int present_phase)
-{
-    ZOOM_resultset resultset;
-    int *start, *count;
-    const char *syntax = 0, *elementSetName = 0;
-
-    if (!c->tasks)
-        return ;
-    switch (c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;        
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
-        return;
-    }
-    if (sr && sr->which == Z_Records_NSD)
-        response_default_diag(c, sr->u.nonSurrogateDiagnostic);
-    else if (sr && sr->which == Z_Records_multipleNSD)
-    {
-        if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
-            response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
-        else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-    }
-    else 
-    {
-        if (*count + *start > resultset->size)
-            *count = resultset->size - *start;
-        if (*count < 0)
-            *count = 0;
-        if (sr && sr->which == Z_Records_DBOSD)
-        {
-            int i;
-            NMEM nmem = odr_extract_mem(c->odr_in);
-            Z_NamePlusRecordList *p =
-                sr->u.databaseOrSurDiagnostics;
-            for (i = 0; i<p->num_records; i++)
-            {
-                record_cache_add(resultset, p->records[i], i + *start,
-                                 syntax, elementSetName,
-                                 elementSetName, 0);
-            }
-            *count -= i;
-            if (*count < 0)
-                *count = 0;
-            *start += i;
-            yaz_log(log_details, 
-                    "handle_records resultset=%p start=%d count=%d",
-                    resultset, *start, *count);
-
-            /* transfer our response to search_nmem .. we need it later */
-            nmem_transfer(odr_getmem(resultset->odr), nmem);
-            nmem_destroy(nmem);
-            if (present_phase && p->num_records == 0)
-            {
-                /* present response and we didn't get any records! */
-                Z_NamePlusRecord *myrec = 
-                    zget_surrogateDiagRec(
-                        resultset->odr, 0, 
-                        YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
-                        "ZOOM C generated. Present phase and no records");
-                record_cache_add(resultset, myrec, *start,
-                                 syntax, elementSetName, 0, 0);
-            }
-        }
-        else if (present_phase)
-        {
-            /* present response and we didn't get any records! */
-            Z_NamePlusRecord *myrec = 
-                zget_surrogateDiagRec(
-                    resultset->odr, 0,
-                    YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
-                    "ZOOM C generated: Present response and no records");
-            record_cache_add(resultset, myrec, *start, syntax, elementSetName,
-                             0, 0);
-        }
-    }
-}
-
-static void handle_present_response(ZOOM_connection c, Z_PresentResponse *pr)
-{
-    handle_records(c, pr->records, 1);
-}
-
-static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
-                                       Z_Term *term)
-{
-    switch (term->which)
-    {
-    case Z_Term_general:
-        ZOOM_options_setl(opt, name,
-                          (const char *)(term->u.general->buf), 
-                          term->u.general->len);
-        break;
-    case Z_Term_characterString:
-        ZOOM_options_set(opt, name, term->u.characterString);
-        break;
-    case Z_Term_numeric:
-        ZOOM_options_set_int(opt, name, *term->u.numeric);
-        break;
-    }
-}
-
-static void handle_queryExpression(ZOOM_options opt, const char *name,
-                                   Z_QueryExpression *exp)
-{
-    char opt_name[80];
-    
-    switch (exp->which)
-    {
-    case Z_QueryExpression_term:
-        if (exp->u.term && exp->u.term->queryTerm)
-        {
-            sprintf(opt_name, "%s.term", name);
-            handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
-        }
-        break;
-    case Z_QueryExpression_query:
-        break;
-    }
-}
-
-static void handle_search_result(ZOOM_connection c, ZOOM_resultset resultset,
-                                Z_OtherInformation *o)
-{
-    int i;
-    for (i = 0; o && i < o->num_elements; i++)
-    {
-        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
-        {
-            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
-            
-            if (ext->which == Z_External_searchResult1)
-            {
-                int j;
-                Z_SearchInfoReport *sr = ext->u.searchResult1;
-                
-                if (sr->num)
-                    ZOOM_options_set_int(
-                        resultset->options, "searchresult.size", sr->num);
-
-                for (j = 0; j < sr->num; j++)
-                {
-                    Z_SearchInfoReport_s *ent =
-                        ext->u.searchResult1->elements[j];
-                    char pref[80];
-                    
-                    sprintf(pref, "searchresult.%d", j);
-
-                    if (ent->subqueryId)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.id", pref);
-                        ZOOM_options_set(resultset->options, opt_name,
-                                         ent->subqueryId);
-                    }
-                    if (ent->subqueryExpression)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.subquery", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryExpression);
-                    }
-                    if (ent->subqueryInterpretation)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.interpretation", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryInterpretation);
-                    }
-                    if (ent->subqueryRecommendation)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.recommendation", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryRecommendation);
-                    }
-                    if (ent->subqueryCount)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.count", pref);
-                        ZOOM_options_set_int(resultset->options, opt_name,
-                                             *ent->subqueryCount);
-                    }                                             
-                }
-            }
-        }
-    }
-}
-
-static char *get_term_cstr(ODR odr, Z_Term *term) {
-
-    switch (term->which) {
-    case Z_Term_general:
-            return odr_strdupn(odr, (const char *) term->u.general->buf, (size_t) term->u.general->len);
-        break;
-    case Z_Term_characterString:
-        return odr_strdup(odr, term->u.characterString);
-    }
-    return 0;
-}
-
-static ZOOM_facet_field get_zoom_facet_field(ODR odr, Z_FacetField *facet) {
-    int term_index;
-    struct attrvalues attr_values;
-    ZOOM_facet_field facet_field = odr_malloc(odr, sizeof(*facet_field));
-    memset(&attr_values, 0, sizeof(attr_values));
-    facetattrs(facet->attributes, &attr_values);
-    facet_field->facet_name = odr_strdup(odr, attr_values.useattr);
-    facet_field->num_terms = facet->num_terms;
-    yaz_log(YLOG_DEBUG, "ZOOM_facet_field %s %d terms %d", attr_values.useattr, attr_values.limit, facet->num_terms);
-    facet_field->facet_terms = odr_malloc(odr, facet_field->num_terms * sizeof(*facet_field->facet_terms));
-    for (term_index = 0 ; term_index < facet->num_terms; term_index++) {
-        Z_FacetTerm *facetTerm = facet->terms[term_index];
-        facet_field->facet_terms[term_index].frequency = *facetTerm->count;
-        facet_field->facet_terms[term_index].term = get_term_cstr(odr, facetTerm->term);
-        yaz_log(YLOG_DEBUG, "    term[%d] %s %d",
-                term_index, facet_field->facet_terms[term_index].term, facet_field->facet_terms[term_index].frequency);
-    }
-    return facet_field;
-}
-
-static void handle_facet_result(ZOOM_connection c, ZOOM_resultset r,
-                                Z_OtherInformation *o)
-{
-    int i;
-    for (i = 0; o && i < o->num_elements; i++)
-    {
-        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
-        {
-            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
-            if (ext->which == Z_External_userFacets)
-            {
-                int j;
-                Z_FacetList *fl = ext->u.facetList;
-                r->num_facets   = fl->num;
-                yaz_log(YLOG_DEBUG, "Facets found: %d", fl->num);
-                r->facets       =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets));
-                r->facets_names =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets_names));
-                for (j = 0; j < fl->num; j++)
-                {
-                    r->facets[j] = get_zoom_facet_field(r->odr, fl->elements[j]);
-                    if (!r->facets[j])
-                        yaz_log(YLOG_DEBUG, "Facet field missing on index %d !", j);
-                    r->facets_names[j] = (char *) ZOOM_facet_field_name(r->facets[j]);
-                }
-            }
-        }
-    }
-}
-
-
-static void handle_search_response(ZOOM_connection c, Z_SearchResponse *sr)
-{
-    ZOOM_resultset resultset;
-    ZOOM_Event event;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
-        return ;
-
-    event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
-    ZOOM_connection_put_event(c, event);
-
-    resultset = c->tasks->u.search.resultset;
-
-    if (sr->resultSetStatus)
-    {
-        ZOOM_options_set_int(resultset->options, "resultSetStatus",
-                             *sr->resultSetStatus);
-    }
-    if (sr->presentStatus)
-    {
-        ZOOM_options_set_int(resultset->options, "presentStatus",
-                             *sr->presentStatus);
-    }
-    handle_search_result(c, resultset, sr->additionalSearchInfo);
-
-    handle_facet_result(c, resultset, sr->additionalSearchInfo);
-
-    resultset->size = *sr->resultCount;
-    handle_records(c, sr->records, 0);
-}
-
-static void sort_response(ZOOM_connection c, Z_SortResponse *res)
-{
-    if (res->diagnostics && res->num_diagnostics > 0)
-        response_diag(c, res->diagnostics[0]);
-}
+/* returns 1 if PDU was sent OK (still pending )
+   0 if PDU was not sent OK (nothing to wait for)
+*/
 
-static int scan_response(ZOOM_connection c, Z_ScanResponse *res)
+ZOOM_API(ZOOM_record)
+    ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
 {
-    NMEM nmem = odr_extract_mem(c->odr_in);
-    ZOOM_scanset scan;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
-        return 0;
-    scan = c->tasks->u.scan.scan;
-
-    if (res->entries && res->entries->nonsurrogateDiagnostics)
-        response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
-    scan->scan_response = res;
-    scan->srw_scan_response = 0;
-    nmem_transfer(odr_getmem(scan->odr), nmem);
-    if (res->stepSize)
-        ZOOM_options_set_int(scan->options, "stepSize", *res->stepSize);
-    if (res->positionOfTerm)
-        ZOOM_options_set_int(scan->options, "position", *res->positionOfTerm);
-    if (res->scanStatus)
-        ZOOM_options_set_int(scan->options, "scanStatus", *res->scanStatus);
-    if (res->numberOfEntriesReturned)
-        ZOOM_options_set_int(scan->options, "number",
-                             *res->numberOfEntriesReturned);
-    nmem_destroy(nmem);
-    return 1;
-}
+    const char *syntax =
+        ZOOM_options_get(s->options, "preferredRecordSyntax");
+    const char *elementSetName =
+        ZOOM_options_get(s->options, "elementSetName");
+    const char *schema =
+        ZOOM_options_get(s->options, "schema");
 
-static zoom_ret send_sort(ZOOM_connection c,
-                          ZOOM_resultset resultset)
-{
-    if (c->error)
-        resultset->r_sort_spec = 0;
-    if (resultset->r_sort_spec)
-    {
-        Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
-        Z_SortRequest *req = apdu->u.sortRequest;
-        
-        req->num_inputResultSetNames = 1;
-        req->inputResultSetNames = (Z_InternationalString **)
-            odr_malloc(c->odr_out, sizeof(*req->inputResultSetNames));
-        req->inputResultSetNames[0] =
-            odr_strdup(c->odr_out, resultset->setname);
-        req->sortedResultSetName = odr_strdup(c->odr_out, resultset->setname);
-        req->sortSequence = resultset->r_sort_spec;
-        resultset->r_sort_spec = 0;
-        return send_APDU(c, apdu);
-    }
-    return zoom_complete;
+    return ZOOM_record_cache_lookup(s, pos, syntax, elementSetName, schema);
 }
 
-static zoom_ret send_present(ZOOM_connection c)
+ZOOM_API(ZOOM_record)
+    ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
 {
-    Z_APDU *apdu = 0;
-    Z_PresentRequest *req = 0;
-    int i = 0;
-    const char *syntax = 0;
-    const char *elementSetName = 0;
-    ZOOM_resultset  resultset;
-    int *start, *count;
-
-    if (!c->tasks)
-    {
-        yaz_log(log_details, "%p send_present no tasks", c);
-        return zoom_complete;
-    }
-    
-    switch (c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
-        return zoom_complete;
-    }
-    yaz_log(log_details, "%p send_present start=%d count=%d",
-            c, *start, *count);
-
-    if (*start < 0 || *count < 0 || *start + *count > resultset->size)
-    {
-        set_dset_error(c, YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, "Bib-1",
-                       "", 0);
-    }
-    if (c->error)                  /* don't continue on error */
-        return zoom_complete;
-    yaz_log(log_details, "send_present resultset=%p start=%d count=%d",
-            resultset, *start, *count);
-
-    for (i = 0; i < *count; i++)
-    {
-        ZOOM_record rec =
-            record_cache_lookup(resultset, i + *start, syntax, elementSetName);
-        if (!rec)
-            break;
-        else
-        {
-            ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-            ZOOM_connection_put_event(c, event);
-        }
-    }
-    *start += i;
-    *count -= i;
-
-    if (*count == 0)
-    {
-        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;
-
-    if (i)
-        yaz_log(log_details, "%p send_present skip=%d", c, i);
-
-    *req->resultSetStartPoint = *start + 1;
-
-    if (resultset->step > 0 && resultset->step < *count)
-        *req->numberOfRecordsRequested = resultset->step;
-    else
-        *req->numberOfRecordsRequested = *count;
-    
-    if (*req->numberOfRecordsRequested + *start > resultset->size)
-        *req->numberOfRecordsRequested = resultset->size - *start;
-    assert(*req->numberOfRecordsRequested > 0);
-
-    if (syntax && *syntax)
-        req->preferredRecordSyntax =
-            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
-
-    if (resultset->schema && *resultset->schema)
-    {
-        Z_RecordComposition *compo = (Z_RecordComposition *)
-            odr_malloc(c->odr_out, sizeof(*compo));
-
-        req->recordComposition = compo;
-        compo->which = Z_RecordComp_complex;
-        compo->u.complex = (Z_CompSpec *)
-            odr_malloc(c->odr_out, sizeof(*compo->u.complex));
-        compo->u.complex->selectAlternativeSyntax = (bool_t *) 
-            odr_malloc(c->odr_out, sizeof(bool_t));
-        *compo->u.complex->selectAlternativeSyntax = 0;
-
-        compo->u.complex->generic = (Z_Specification *)
-            odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
-
-        compo->u.complex->generic->which = Z_Schema_oid;
-        compo->u.complex->generic->schema.oid = (Odr_oid *)
-            zoom_yaz_str_to_z3950oid (c, CLASS_SCHEMA, resultset->schema);
-
-        if (!compo->u.complex->generic->schema.oid)
-        {
-            /* OID wasn't a schema! Try record syntax instead. */
+    ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
 
-            compo->u.complex->generic->schema.oid = (Odr_oid *)
-                zoom_yaz_str_to_z3950oid (c, CLASS_RECSYN, resultset->schema);
-        }
-        if (elementSetName && *elementSetName)
-        {
-            compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
-                odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
-            compo->u.complex->generic->elementSpec->which =
-                Z_ElementSpec_elementSetName;
-            compo->u.complex->generic->elementSpec->u.elementSetName =
-                odr_strdup(c->odr_out, elementSetName);
-        }
-        else
-            compo->u.complex->generic->elementSpec = 0;
-        compo->u.complex->num_dbSpecific = 0;
-        compo->u.complex->dbSpecific = 0;
-        compo->u.complex->num_recordSyntax = 0;
-        compo->u.complex->recordSyntax = 0;
-    }
-    else if (elementSetName && *elementSetName)
+    if (!rec)
     {
-        Z_ElementSetNames *esn = (Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        Z_RecordComposition *compo = (Z_RecordComposition *)
-            odr_malloc(c->odr_out, sizeof(*compo));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, elementSetName);
-        compo->which = Z_RecordComp_simple;
-        compo->u.simple = esn;
-        req->recordComposition = compo;
+        /*
+         * MIKE: I think force_sync should always be zero, but I don't
+         * want to make this change until I get the go-ahead from
+         * Adam, in case something depends on the old synchronous
+         * behaviour.
+         */
+        int force_sync = 1;
+        if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
+        ZOOM_resultset_retrieve(r, force_sync, pos, 1);
+        rec = ZOOM_resultset_record_immediate(r, pos);
     }
-    req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
-    return send_APDU(c, apdu);
+    return rec;
 }
 
 ZOOM_API(ZOOM_scanset)
@@ -2998,8 +1242,9 @@ ZOOM_API(ZOOM_scanset)
     ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
 {
     ZOOM_scanset scan = 0;
+    Z_Query *z_query = ZOOM_query_get_Z_Query(q);
 
-    if (!q->z_query)
+    if (!z_query)
         return 0;
     scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
     scan->connection = c;
@@ -3010,8 +1255,8 @@ ZOOM_API(ZOOM_scanset)
     scan->srw_scan_response = 0;
 
     scan->query = q;
-    (q->refcount)++;
-    scan->databaseNames = set_DatabaseNames(c, c->options,
+    ZOOM_query_addref(q);
+    scan->databaseNames = ZOOM_connection_get_databases(c, c->options,
                                             &scan->num_databaseNames,
                                             scan->odr);
 
@@ -3019,7 +1264,7 @@ ZOOM_API(ZOOM_scanset)
     {
         ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
         task->u.scan.scan = scan;
-        
+
         (scan->refcount)++;
         if (!c->async)
         {
@@ -3041,7 +1286,7 @@ ZOOM_API(void)
         ZOOM_query_destroy(scan->query);
 
         odr_destroy(scan->odr);
-        
+
         ZOOM_options_destroy(scan->options);
         xfree(scan);
     }
@@ -3051,137 +1296,19 @@ static zoom_ret send_package(ZOOM_connection c)
 {
     ZOOM_Event event;
 
-    yaz_log(log_details, "%p send_package", c);
+    yaz_log(c->log_details, "%p send_package", c);
     if (!c->tasks)
         return zoom_complete;
     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
-    
+
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
     ZOOM_connection_put_event(c, event);
-    
+
     c->buf_out = c->tasks->u.package->buf_out;
     c->len_out = c->tasks->u.package->len_out;
 
-    return do_write(c);
-}
-
-static zoom_ret ZOOM_connection_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);
-    scan = c->tasks->u.scan.scan;
-
-    /* Z39.50 scan can only carry RPN */
-    if (scan->query->z_query->which == Z_Query_type_1 ||
-        scan->query->z_query->which == Z_Query_type_101)
-    {
-        Z_RPNQuery *rpn = scan->query->z_query->u.type_1;
-        const char *cp = ZOOM_options_get(scan->options, "rpnCharset");
-        if (cp)
-        {
-            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
-            if (cd)
-            {
-                rpn = yaz_copy_z_RPNQuery(rpn, c->odr_out);
-
-                yaz_query_charset_convert_rpnquery(
-                    rpn, c->odr_out, cd);
-                yaz_iconv_close(cd);
-            }
-        }
-        req->attributeSet = rpn->attributeSetId;
-        if (!req->attributeSet)
-            req->attributeSet = odr_oiddup(c->odr_out, yaz_oid_attset_bib_1);
-        if (rpn->RPNStructure->which == Z_RPNStructure_simple &&
-            rpn->RPNStructure->u.simple->which == Z_Operand_APT)
-        {
-            req->termListAndStartPoint =
-                rpn->RPNStructure->u.simple->u.attributesPlusTerm;
-        }
-        else
-        {
-            set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-            return zoom_complete;
-        }
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-
-    *req->numberOfTermsRequested =
-        ZOOM_options_get_int(scan->options, "number", 20);
-
-    req->preferredPositionInResponse =
-        odr_intdup(c->odr_out,
-                   ZOOM_options_get_int(scan->options, "position", 1));
-
-    req->stepSize =
-        odr_intdup(c->odr_out,
-                   ZOOM_options_get_int(scan->options, "stepSize", 0));
-    
-    req->databaseNames = scan->databaseNames;
-    req->num_databaseNames = scan->num_databaseNames;
-
-    return send_APDU(c, apdu);
-}
-
-#if YAZ_HAVE_XML2
-static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
-{
-    ZOOM_scanset scan;
-    Z_SRW_PDU *sr = 0;
-    const char *option_val = 0;
-
-    if (!c->tasks)
-        return zoom_complete;
-    assert (c->tasks->which == ZOOM_TASK_SCAN);
-    scan = c->tasks->u.scan.scan;
-        
-    sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
-
-    /* SRU scan can only carry CQL and PQF */
-    if (scan->query->z_query->which == Z_Query_type_104)
-    {
-        sr->u.scan_request->query_type = Z_SRW_query_type_cql;
-        sr->u.scan_request->scanClause.cql = scan->query->query_string;
-    }
-    else if (scan->query->z_query->which == Z_Query_type_1
-             || scan->query->z_query->which == Z_Query_type_101)
-    {
-        sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
-        sr->u.scan_request->scanClause.pqf = scan->query->query_string;
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-
-    sr->u.scan_request->maximumTerms = odr_intdup(
-        c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
-    
-    sr->u.scan_request->responsePosition = odr_intdup(
-        c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
-    
-    option_val = ZOOM_options_get(scan->options, "extraArgs");
-    yaz_encode_sru_extra(sr, c->odr_out, option_val);
-    return send_srw(c, sr);
+    return ZOOM_send_buf(c);
 }
-#else
-static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
-{
-    return zoom_complete;
-}
-#endif
-
 
 ZOOM_API(size_t)
     ZOOM_scanset_size(ZOOM_scanset scan)
@@ -3202,7 +1329,7 @@ static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
                                 const char **disp_term, size_t *disp_len)
 {
     size_t noent = ZOOM_scanset_size(scan);
-    
+
     *value_term = 0;
     *value_len = 0;
 
@@ -3218,7 +1345,7 @@ static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
         if (res->entries->entries[pos]->which == Z_Entry_termInfo)
         {
             Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
-            
+
             *value_term = (const char *) t->term->u.general->buf;
             *value_len = t->term->u.general->len;
             if (t->displayTerm)
@@ -3264,7 +1391,7 @@ ZOOM_API(const char *)
 
     ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
                         &disp_term, &disp_len);
-    
+
     *len = value_len;
     return value_term;
 }
@@ -3280,7 +1407,7 @@ ZOOM_API(const char *)
 
     ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
                         &disp_term, &disp_len);
-    
+
     *len = disp_len;
     return disp_term;
 }
@@ -3295,493 +1422,10 @@ ZOOM_API(void)
     ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
                             const char *val)
 {
-    ZOOM_options_set(scan->options, key, val);
-}
-
-static Z_APDU *create_es_package(ZOOM_package p, const Odr_oid *oid)
-{
-    const char *str;
-    Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
-    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
-    
-    str = ZOOM_options_get(p->options, "package-name");
-    if (str && *str)
-        req->packageName = odr_strdup(p->odr_out, str);
-    
-    str = ZOOM_options_get(p->options, "user-id");
-    if (str)
-        req->userId = odr_strdup_null(p->odr_out, str);
-    
-    req->packageType = odr_oiddup(p->odr_out, oid);
-
-    str = ZOOM_options_get(p->options, "function");
-    if (str)
-    {
-        if (!strcmp (str, "create"))
-            *req->function = Z_ExtendedServicesRequest_create;
-        if (!strcmp (str, "delete"))
-            *req->function = Z_ExtendedServicesRequest_delete;
-        if (!strcmp (str, "modify"))
-            *req->function = Z_ExtendedServicesRequest_modify;
-    }
-
-    str = ZOOM_options_get(p->options, "waitAction");
-    if (str)
-    {
-        if (!strcmp (str, "wait"))
-            *req->waitAction = Z_ExtendedServicesRequest_wait;
-        if (!strcmp (str, "waitIfPossible"))
-            *req->waitAction = Z_ExtendedServicesRequest_waitIfPossible;
-        if (!strcmp (str, "dontWait"))
-            *req->waitAction = Z_ExtendedServicesRequest_dontWait;
-        if (!strcmp (str, "dontReturnPackage"))
-            *req->waitAction = Z_ExtendedServicesRequest_dontReturnPackage;
-    }
-    return apdu;
-}
-
-static const char *ill_array_lookup(void *clientData, const char *idx)
-{
-    ZOOM_package p = (ZOOM_package) clientData;
-    return ZOOM_options_get(p->options, idx+4);
-}
-
-static Z_External *encode_ill_request(ZOOM_package p)
-{
-    ODR out = p->odr_out;
-    ILL_Request *req;
-    Z_External *r = 0;
-    struct ill_get_ctl ctl;
-        
-    ctl.odr = p->odr_out;
-    ctl.clientData = p;
-    ctl.f = ill_array_lookup;
-        
-    req = ill_get_ILLRequest(&ctl, "ill", 0);
-        
-    if (!ill_Request(out, &req, 0, 0))
-    {
-        int ill_request_size;
-        char *ill_request_buf = odr_getbuf(out, &ill_request_size, 0);
-        if (ill_request_buf)
-            odr_setbuf(out, ill_request_buf, ill_request_size, 1);
-        return 0;
-    }
-    else
-    {
-        int illRequest_size = 0;
-        char *illRequest_buf = odr_getbuf(out, &illRequest_size, 0);
-                
-        r = (Z_External *) odr_malloc(out, sizeof(*r));
-        r->direct_reference = odr_oiddup(out, yaz_oid_general_isoill_1);
-        r->indirect_reference = 0;
-        r->descriptor = 0;
-        r->which = Z_External_single;
-                
-        r->u.single_ASN1_type =
-            odr_create_Odr_oct(out,
-                               (unsigned char *)illRequest_buf,
-                               illRequest_size);
-    }
-    return r;
-}
-
-static Z_ItemOrder *encode_item_order(ZOOM_package p)
-{
-    Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc(p->odr_out, sizeof(*req));
-    const char *str;
-    int len;
-    
-    req->which = Z_IOItemOrder_esRequest;
-    req->u.esRequest = (Z_IORequest *) 
-        odr_malloc(p->odr_out,sizeof(Z_IORequest));
-
-    /* to keep part ... */
-    req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
-        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
-    req->u.esRequest->toKeep->supplDescription = 0;
-    req->u.esRequest->toKeep->contact = (Z_IOContact *)
-        odr_malloc(p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
-        
-    str = ZOOM_options_get(p->options, "contact-name");
-    req->u.esRequest->toKeep->contact->name =
-        odr_strdup_null(p->odr_out, str);
-        
-    str = ZOOM_options_get(p->options, "contact-phone");
-    req->u.esRequest->toKeep->contact->phone =
-        odr_strdup_null(p->odr_out, str);
-        
-    str = ZOOM_options_get(p->options, "contact-email");
-    req->u.esRequest->toKeep->contact->email =
-        odr_strdup_null(p->odr_out, str);
-        
-    req->u.esRequest->toKeep->addlBilling = 0;
-        
-    /* not to keep part ... */
-    req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
-        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
-        
-    str = ZOOM_options_get(p->options, "itemorder-setname");
-    if (!str)
-        str = "default";
-
-    if (!*str) 
-        req->u.esRequest->notToKeep->resultSetItem = 0;
-    else
-    {
-        req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
-            odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
-
-        req->u.esRequest->notToKeep->resultSetItem->resultSetId =
-            odr_strdup(p->odr_out, str);
-        req->u.esRequest->notToKeep->resultSetItem->item =
-            odr_intdup(p->odr_out, 0);
-        
-        str = ZOOM_options_get(p->options, "itemorder-item");
-        *req->u.esRequest->notToKeep->resultSetItem->item =
-            (str ? atoi(str) : 1);
-    }
-
-    str = ZOOM_options_getl(p->options, "doc", &len);
-    if (str)
-    {
-        req->u.esRequest->notToKeep->itemRequest =
-            z_ext_record_xml(p->odr_out, str, len);
-    }
-    else
-        req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
-    
-    return req;
-}
-
-Z_APDU *create_admin_package(ZOOM_package p, int type, 
-                             Z_ESAdminOriginPartToKeep **toKeepP,
-                             Z_ESAdminOriginPartNotToKeep **notToKeepP)
-{
-    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_admin);
-    if (apdu)
-    {
-        Z_ESAdminOriginPartToKeep  *toKeep;
-        Z_ESAdminOriginPartNotToKeep  *notToKeep;
-        Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
-        const char *first_db = "Default";
-        int num_db;
-        char **db = set_DatabaseNames(p->connection, p->options, &num_db,
-                                      p->odr_out);
-        if (num_db > 0)
-            first_db = db[0];
-            
-        r->direct_reference = odr_oiddup(p->odr_out, yaz_oid_extserv_admin);
-        r->descriptor = 0;
-        r->indirect_reference = 0;
-        r->which = Z_External_ESAdmin;
-        
-        r->u.adminService = (Z_Admin *)
-            odr_malloc(p->odr_out, sizeof(*r->u.adminService));
-        r->u.adminService->which = Z_Admin_esRequest;
-        r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
-            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
-        
-        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->databaseName = odr_strdup(p->odr_out, first_db);
-        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();
-        if (toKeepP)
-            *toKeepP = toKeep;
-        if (notToKeepP)
-            *notToKeepP = notToKeep;
-    }
-    return apdu;
-}
-
-static Z_APDU *create_xmlupdate_package(ZOOM_package p)
-{
-    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_xml_es);
-    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
-    Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
-    int len;
-    const char *doc = ZOOM_options_getl(p->options, "doc", &len);
-
-    if (!doc)
-    {
-        doc = "";
-        len = 0;
-    }
-
-    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_create_Odr_oct(p->odr_out, (const unsigned char *) doc, len);
-    return apdu;
-}
-
-static Z_APDU *create_update_package(ZOOM_package p)
-{
-    Z_APDU *apdu = 0;
-    const char *first_db = "Default";
-    int num_db;
-    char **db = set_DatabaseNames(p->connection, p->options, &num_db, p->odr_out);
-    const char *action = ZOOM_options_get(p->options, "action");
-    int recordIdOpaque_len;
-    const char *recordIdOpaque = ZOOM_options_getl(p->options, "recordIdOpaque",
-        &recordIdOpaque_len);
-    const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
-    int record_len;
-    const char *record_buf = ZOOM_options_getl(p->options, "record",
-        &record_len);
-    int recordOpaque_len;
-    const char *recordOpaque_buf = ZOOM_options_getl(p->options, "recordOpaque",
-        &recordOpaque_len);
-    const char *syntax_str = ZOOM_options_get(p->options, "syntax");
-    const char *version = ZOOM_options_get(p->options, "updateVersion");
-
-    const char *correlationInfo_note =
-        ZOOM_options_get(p->options, "correlationInfo.note");
-    const char *correlationInfo_id =
-        ZOOM_options_get(p->options, "correlationInfo.id");
-    int action_no = -1;
-    Odr_oid *syntax_oid = 0;
-    const Odr_oid *package_oid = yaz_oid_extserv_database_update;
-
-    if (!version)
-        version = "3";
-    if (!syntax_str)
-        syntax_str = "xml";
-    if (!record_buf && !recordOpaque_buf)
-    {
-        record_buf = "void";
-        record_len = 4;
-        syntax_str = "SUTRS";
-    }
-
-    if (syntax_str)
-    {
-        syntax_oid = yaz_string_to_oid_odr(yaz_oid_std(),
-                                           CLASS_RECSYN, syntax_str,
-                                           p->odr_out);
-    }
-    if (!syntax_oid)
-        return 0;
-
-    if (num_db > 0)
-        first_db = db[0];
-    
-    switch(*version)
-    {
-    case '1':
-        package_oid = yaz_oid_extserv_database_update_first_version;
-        /* old update does not support specialUpdate */
-        if (!action)
-            action = "recordInsert";
-        break;
-    case '2':
-        if (!action)
-            action = "specialUpdate";
-        package_oid = yaz_oid_extserv_database_update_second_version;
-        break;
-    case '3':
-        if (!action)
-            action = "specialUpdate";
-        package_oid = yaz_oid_extserv_database_update;
-        break;
-    default:
-        return 0;
-    }
-    
-    if (!strcmp(action, "recordInsert"))
-        action_no = Z_IUOriginPartToKeep_recordInsert;
-    else if (!strcmp(action, "recordReplace"))
-        action_no = Z_IUOriginPartToKeep_recordReplace;
-    else if (!strcmp(action, "recordDelete"))
-        action_no = Z_IUOriginPartToKeep_recordDelete;
-    else if (!strcmp(action, "elementUpdate"))
-        action_no = Z_IUOriginPartToKeep_elementUpdate;
-    else if (!strcmp(action, "specialUpdate"))
-        action_no = Z_IUOriginPartToKeep_specialUpdate;
-    else
-        return 0;
-
-    apdu = create_es_package(p, package_oid);
-    if (apdu)
-    {
-        Z_IUOriginPartToKeep *toKeep;
-        Z_IUSuppliedRecords *notToKeep;
-        Z_External *r = (Z_External *)
-            odr_malloc(p->odr_out, sizeof(*r));
-        const char *elementSetName =
-            ZOOM_options_get(p->options, "elementSetName");
-        
-        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-        
-        r->direct_reference = odr_oiddup(p->odr_out, package_oid);
-        r->descriptor = 0;
-        r->which = Z_External_update;
-        r->indirect_reference = 0;
-        r->u.update = (Z_IUUpdate *)
-            odr_malloc(p->odr_out, sizeof(*r->u.update));
-        
-        r->u.update->which = Z_IUUpdate_esRequest;
-        r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
-            odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
-        toKeep = r->u.update->u.esRequest->toKeep = 
-            (Z_IUOriginPartToKeep *)
-            odr_malloc(p->odr_out, sizeof(*toKeep));
-        
-        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
-        toKeep->schema = 0;
-        
-        toKeep->elementSetName = odr_strdup_null(p->odr_out, elementSetName);
-            
-        toKeep->actionQualifier = 0;
-        toKeep->action = odr_intdup(p->odr_out, action_no);
-        
-        notToKeep = r->u.update->u.esRequest->notToKeep = 
-            (Z_IUSuppliedRecords *)
-            odr_malloc(p->odr_out, sizeof(*notToKeep));
-        notToKeep->num = 1;
-        notToKeep->elements = (Z_IUSuppliedRecords_elem **)
-            odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
-        notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
-            odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
-        notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
-        if (recordIdOpaque)
-        {
-            notToKeep->elements[0]->u.opaque = 
-                odr_create_Odr_oct(p->odr_out,
-                                   (const unsigned char *) recordIdOpaque,
-                                   recordIdOpaque_len);
-        }
-        else if (recordIdNumber)
-        {
-            notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
-            
-            notToKeep->elements[0]->u.number =
-                odr_intdup(p->odr_out, atoi(recordIdNumber));
-        }
-        else
-            notToKeep->elements[0]->u.opaque = 0;
-        notToKeep->elements[0]->supplementalId = 0;
-        if (correlationInfo_note || correlationInfo_id)
-        {
-            Z_IUCorrelationInfo *ci;
-            ci = notToKeep->elements[0]->correlationInfo =
-                (Z_IUCorrelationInfo *) odr_malloc(p->odr_out, sizeof(*ci));
-            ci->note = odr_strdup_null(p->odr_out, correlationInfo_note);
-            ci->id = correlationInfo_id ?
-                odr_intdup(p->odr_out, atoi(correlationInfo_id)) : 0;
-        }
-        else
-            notToKeep->elements[0]->correlationInfo = 0;
-        if (recordOpaque_buf)
-        {
-            notToKeep->elements[0]->record =
-                z_ext_record_oid_any(p->odr_out, syntax_oid,
-                                 recordOpaque_buf, recordOpaque_len);
-        }
-        else
-        {
-            notToKeep->elements[0]->record =
-                z_ext_record_oid(p->odr_out, syntax_oid,
-                                 record_buf, record_len);
-        }
-    }
-    if (0 && apdu)
-    {
-        ODR print = odr_createmem(ODR_PRINT);
-
-        z_APDU(print, &apdu, 0, 0);
-        odr_destroy(print);
-    }
-    return apdu;
-}
-
-ZOOM_API(void)
-    ZOOM_package_send(ZOOM_package p, const char *type)
-{
-    Z_APDU *apdu = 0;
-    ZOOM_connection c;
-    if (!p)
-        return;
-    c = p->connection;
-    odr_reset(p->odr_out);
-    xfree(p->buf_out);
-    p->buf_out = 0;
-    if (!strcmp(type, "itemorder"))
-    {
-        apdu = create_es_package(p, yaz_oid_extserv_item_order);
-        if (apdu)
-        {
-            Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
-            
-            r->direct_reference = 
-                odr_oiddup(p->odr_out, yaz_oid_extserv_item_order);
-            r->descriptor = 0;
-            r->which = Z_External_itemOrder;
-            r->indirect_reference = 0;
-            r->u.itemOrder = encode_item_order(p);
-
-            apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-        }
-    }
-    else if (!strcmp(type, "create"))  /* create database */
-    {
-        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
-                                    0, 0);
-    }   
-    else if (!strcmp(type, "drop"))  /* drop database */
-    {
-        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
-                                    0, 0);
-    }
-    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)
-        {
-            char *buf;
-
-            ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_PACKAGE);
-            task->u.package = p;
-            buf = odr_getbuf(p->odr_out, &p->len_out, 0);
-            p->buf_out = (char *) xmalloc(p->len_out);
-            memcpy(p->buf_out, buf, p->len_out);
-            
-            (p->refcount)++;
-            if (!c->async)
-            {
-                while (ZOOM_event(1, &c))
-                    ;
-            }
-        }
-    }
+    ZOOM_options_set(scan->options, key, val);
 }
 
+
 ZOOM_API(ZOOM_package)
     ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
 {
@@ -3806,7 +1450,7 @@ ZOOM_API(void)
     {
         odr_destroy(p->odr_out);
         xfree(p->buf_out);
-        
+
         ZOOM_options_destroy(p->options);
         xfree(p);
     }
@@ -3846,18 +1490,18 @@ ZOOM_API(int)
 
     if (!task)
         return 0;
-    yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
+    yaz_log(c->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_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->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_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "task already running", c);
         return 0;
     }
@@ -3871,13 +1515,7 @@ ZOOM_API(int)
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_search(c);
             else
-                ret = ZOOM_connection_send_search(c);
-            break;
-        case ZOOM_TASK_RETRIEVE:
-            if (c->proto == PROTO_HTTP)
-                ret = ZOOM_connection_srw_send_search(c);
-            else
-                ret = send_present(c);
+                ret = ZOOM_connection_Z3950_send_search(c);
             break;
         case ZOOM_TASK_CONNECT:
             ret = do_connect(c);
@@ -3886,630 +1524,145 @@ ZOOM_API(int)
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_scan(c);
             else
-                ret = ZOOM_connection_send_scan(c);
+                ret = ZOOM_connection_Z3950_send_scan(c);
             break;
         case ZOOM_TASK_PACKAGE:
             ret = send_package(c);
             break;
         case ZOOM_TASK_SORT:
-            c->tasks->u.sort.resultset->r_sort_spec = 
-                c->tasks->u.sort.q->sort_spec;
-            ret = send_sort(c, c->tasks->u.sort.resultset);
+            c->tasks->u.sort.resultset->r_sort_spec =
+                ZOOM_query_get_sortspec(c->tasks->u.sort.q);
+            ret = send_Z3950_sort(c, c->tasks->u.sort.resultset);
             break;
         }
     }
     else
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->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_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "task removed (complete)", c);
         ZOOM_connection_remove_task(c);
         return 0;
     }
-    yaz_log(log_details, "%p ZOOM_connection_exec_task "
+    yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
             "task pending", c);
     return 1;
 }
 
-static zoom_ret send_sort_present(ZOOM_connection c)
-{
-    zoom_ret r = zoom_complete;
-
-    if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
-        r = send_sort(c, c->tasks->u.search.resultset);
-    if (r == zoom_complete)
-        r = send_present(c);
-    return r;
-}
-
-static int es_response_taskpackage_update(ZOOM_connection c,
-               Z_IUUpdateTaskPackage *utp)
-{
-       if (utp && utp->targetPart)
-       {
-               Z_IUTargetPart *targetPart = utp->targetPart;
-               switch ( *targetPart->updateStatus ) {
-                       case Z_IUTargetPart_success:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "success");
-                               break;
-                       case Z_IUTargetPart_partial:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "partial");
-                               break;
-                       case Z_IUTargetPart_failure:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "failure");
-                               if (targetPart->globalDiagnostics && targetPart->num_globalDiagnostics > 0)
-                                       response_diag(c, targetPart->globalDiagnostics[0]);
-                               break;
-               }
-               // NOTE: Individual record status, surrogate diagnostics, and supplemental diagnostics ARE NOT REPORTED.
-       }
-    return 1;
-}
-
-static int es_response_taskpackage(ZOOM_connection c,
-                                   Z_TaskPackage *taskPackage)
-{
-       // targetReference
-       Odr_oct *id = taskPackage->targetReference;
-       if (id)
-               ZOOM_options_setl(c->tasks->u.package->options,
-                                                       "targetReference", (char*) id->buf, id->len);
-       
-       // taskStatus
-       switch ( *taskPackage->taskStatus ) {
-               case Z_TaskPackage_pending:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "pending");
-                       break;
-               case Z_TaskPackage_active:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "active");
-                       break;
-               case Z_TaskPackage_complete:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "complete");
-                       break;
-               case Z_TaskPackage_aborted:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "aborted");
-                       if ( taskPackage->num_packageDiagnostics && taskPackage->packageDiagnostics )
-                               response_diag(c, taskPackage->packageDiagnostics[0]);
-                       break;
-       }
-       
-       // taskSpecificParameters
-       // NOTE: Only Update implemented, no others.
-       if ( taskPackage->taskSpecificParameters->which == Z_External_update ) {
-                       Z_IUUpdateTaskPackage *utp = taskPackage->taskSpecificParameters->u.update->u.taskPackage;
-                       es_response_taskpackage_update(c, utp);
-       }
-       return 1;
-}
-
-
-static int es_response(ZOOM_connection c,
-                       Z_ExtendedServicesResponse *res)
-{
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
-        return 0;
-    switch (*res->operationStatus) {
-        case Z_ExtendedServicesResponse_done:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "done");
-            break;
-        case Z_ExtendedServicesResponse_accepted:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "accepted");
-            break;
-        case Z_ExtendedServicesResponse_failure:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "failure");
-            if (res->diagnostics && res->num_diagnostics > 0)
-                response_diag(c, res->diagnostics[0]);
-            break;
-    }
-    if (res->taskPackage &&
-        res->taskPackage->which == Z_External_extendedService)
-    {
-        Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
-        es_response_taskpackage(c, taskPackage);
-    }
-    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);
-    }
-}
-
+#if YAZ_HAVE_XML2
 
-static void interpret_otherinformation_field(ZOOM_connection c,
-                                             Z_OtherInformation *ui)
+static zoom_ret send_HTTP_redirect(ZOOM_connection c, const char *uri)
 {
-    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 set_init_option(const char *name, void *clientData) {
-    ZOOM_connection c = (ZOOM_connection) clientData;
-    char buf[80];
-
-    sprintf(buf, "init_opt_%.70s", name);
-    ZOOM_connection_option_set(c, buf, "1");
-}
-
+    Z_GDU *gdu = z_get_HTTP_Request_uri(c->odr_out, uri, 0, c->proxy ? 1 : 0);
 
-static void recv_apdu(ZOOM_connection c, Z_APDU *apdu)
-{
-    Z_InitResponse *initrs;
-    
-    ZOOM_connection_set_mask(c, 0);
-    yaz_log(log_details, "%p recv_apdu apdu->which=%d", c, apdu->which);
-    switch(apdu->which)
+    gdu->u.HTTP_Request->method = odr_strdup(c->odr_out, "GET");
+    z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, "Accept",
+                      "text/xml");
+    yaz_cookies_request(c->cookies, c->odr_out, gdu->u.HTTP_Request);
+    if (c->user && c->password)
     {
-    case Z_APDU_initResponse:
-        yaz_log(log_api, "%p recv_apdu: Received Init response", c);
-        initrs = apdu->u.initResponse;
-        ZOOM_connection_option_set(c, "serverImplementationId",
-                                   initrs->implementationId ?
-                                   initrs->implementationId : "");
-        ZOOM_connection_option_set(c, "serverImplementationName",
-                                   initrs->implementationName ?
-                                   initrs->implementationName : "");
-        ZOOM_connection_option_set(c, "serverImplementationVersion",
-                                   initrs->implementationVersion ?
-                                   initrs->implementationVersion : "");
-        /* Set the three old options too, for old applications */
-        ZOOM_connection_option_set(c, "targetImplementationId",
-                                   initrs->implementationId ?
-                                   initrs->implementationId : "");
-        ZOOM_connection_option_set(c, "targetImplementationName",
-                                   initrs->implementationName ?
-                                   initrs->implementationName : "");
-        ZOOM_connection_option_set(c, "targetImplementationVersion",
-                                   initrs->implementationVersion ?
-                                   initrs->implementationVersion : "");
-
-        /* Make initrs->options available as ZOOM-level options */
-        yaz_init_opt_decode(initrs->options, set_init_option, (void*) c);
-
-        if (!*initrs->result)
-        {
-            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
-        {
-            char *cookie =
-                yaz_oi_get_string_oid(&apdu->u.initResponse->otherInfo,
-                                      yaz_oid_userinfo_cookie, 1, 0);
-            xfree(c->cookie_in);
-            c->cookie_in = 0;
-            if (cookie)
-                c->cookie_in = xstrdup(cookie);
-            if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
-                ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
-                c->support_named_resultsets = 1;
-            if (c->tasks)
-            {
-                assert(c->tasks->which == ZOOM_TASK_CONNECT);
-                ZOOM_connection_remove_task(c);
-            }
-            ZOOM_connection_exec_task(c);
-        }
-        if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
-        {
-            NMEM tmpmem = nmem_create();
-            Z_CharSetandLanguageNegotiation *p =
-                yaz_get_charneg_record(initrs->otherInfo);
-            
-            if (p)
-            {
-                char *charset = NULL, *lang = NULL;
-                int sel;
-                
-                yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
-                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",
-                                               charset);
-                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 (!ZOOM_test_reconnect(c))
-        {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-            do_close(c);
-        }
-        break;
-    default:
-        yaz_log(log_api, "%p Received unknown PDU", c);
-        set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-        do_close(c);
+        z_HTTP_header_add_basic_auth(c->odr_out, &gdu->u.HTTP_Request->headers,
+                                     c->user, c->password);
     }
+    return ZOOM_send_GDU(c, gdu);
 }
 
-#if YAZ_HAVE_XML2
-static zoom_ret handle_srw_response(ZOOM_connection c,
-                                    Z_SRW_searchRetrieveResponse *res)
+zoom_ret ZOOM_send_GDU(ZOOM_connection c, Z_GDU *gdu)
 {
-    ZOOM_resultset resultset = 0;
-    int i;
-    NMEM nmem;
     ZOOM_Event event;
-    int *start, *count;
-    const char *syntax, *elementSetName;
-
-    if (!c->tasks)
-        return zoom_complete;
-
-    switch(c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;        
 
-        if (!c->tasks->u.search.recv_search_fired)
-        {
-            event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
-            ZOOM_connection_put_event(c, event);
-            c->tasks->u.search.recv_search_fired = 1;
-        }
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
+    int r = z_GDU(c->odr_out, &gdu, 0, 0);
+    if (!r)
         return zoom_complete;
-    }
-
-    resultset->size = 0;
-
-    if (res->resultSetId)
-        ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
+    if (c->odr_print)
+        z_GDU(c->odr_print, &gdu, 0, 0);
+    if (c->odr_save)
+        z_GDU(c->odr_save, &gdu, 0, 0);
+    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
+    odr_reset(c->odr_out);
 
-    yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
+    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
+    ZOOM_connection_put_event(c, event);
 
-    if (res->num_diagnostics > 0)
-    {
-        set_SRU_error(c, &res->diagnostics[0]);
-    }
-    else
-    {
-        if (res->numberOfRecords)
-            resultset->size = *res->numberOfRecords;
-        for (i = 0; i<res->num_records; i++)
-        {
-            int pos;
-            Z_SRW_record *sru_rec;
-            Z_SRW_diagnostic *diag = 0;
-            int num_diag;
-            
-            Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
-                odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
-            
-            if (res->records[i].recordPosition && 
-                *res->records[i].recordPosition > 0)
-                pos = *res->records[i].recordPosition - 1;
-            else
-                pos = *start + i;
-            
-            sru_rec = &res->records[i];
-            
-            npr->databaseName = 0;
-            npr->which = Z_NamePlusRecord_databaseRecord;
-            npr->u.databaseRecord = (Z_External *)
-                odr_malloc(c->odr_in, sizeof(Z_External));
-            npr->u.databaseRecord->descriptor = 0;
-            npr->u.databaseRecord->direct_reference =
-                odr_oiddup(c->odr_in, yaz_oid_recsyn_xml);
-            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 = (unsigned char*)
-                sru_rec->recordData_buf;
-            npr->u.databaseRecord->u.octet_aligned->len = 
-                npr->u.databaseRecord->u.octet_aligned->size = 
-                sru_rec->recordData_len;
-            
-            if (sru_rec->recordSchema 
-                && !strcmp(sru_rec->recordSchema,
-                           "info:srw/schema/1/diagnostics-v1.1"))
-            {
-                sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
-                                                 sru_rec->recordData_len,
-                                                 &diag, &num_diag,
-                                                 resultset->odr);
-            }
-            record_cache_add(resultset, npr, pos, syntax, elementSetName,
-                             sru_rec->recordSchema, diag);
-        }
-        *count -= i;
-        *start += i;
-        if (*count + *start > resultset->size)
-            *count = resultset->size - *start;
-        if (*count < 0)
-            *count = 0;
-        
-        nmem = odr_extract_mem(c->odr_in);
-        nmem_transfer(odr_getmem(resultset->odr), nmem);
-        nmem_destroy(nmem);
-
-        if (*count > 0)
-            return ZOOM_connection_srw_send_search(c);
-    }
-    return zoom_complete;
+    return ZOOM_send_buf(c);
 }
-#endif
 
 #if YAZ_HAVE_XML2
-static void handle_srw_scan_response(ZOOM_connection c,
-                                     Z_SRW_scanResponse *res)
+void ZOOM_set_HTTP_error(ZOOM_connection c, int error,
+                         const char *addinfo, const char *addinfo2)
 {
-    NMEM nmem = odr_extract_mem(c->odr_in);
-    ZOOM_scanset scan;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
-        return;
-    scan = c->tasks->u.scan.scan;
-
-    if (res->num_diagnostics > 0)
-        set_SRU_error(c, &res->diagnostics[0]);
-
-    scan->scan_response = 0;
-    scan->srw_scan_response = res;
-    nmem_transfer(odr_getmem(scan->odr), nmem);
-
-    ZOOM_options_set_int(scan->options, "number", res->num_terms);
-    nmem_destroy(nmem);
+    ZOOM_set_dset_error(c, error, "HTTP", addinfo, addinfo2);
 }
 #endif
 
-#if YAZ_HAVE_XML2
-static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
-{
-    Z_GDU *p = z_get_HTTP_Request(odr);
-    const char *host = url;
-    const char *cp0 = strstr(host, "://");
-    const char *cp1 = 0;
-    if (cp0)
-        cp0 = cp0+3;
-    else
-        cp0 = host;
-    
-    cp1 = strchr(cp0, '/');
-    if (!cp1)
-        cp1 = cp0 + strlen(cp0);
-    
-    if (cp0 && cp1)
-    {
-        char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
-        memcpy (h, cp0, cp1 - cp0);
-        h[cp1-cp0] = '\0';
-        z_HTTP_header_add(odr, &p->u.HTTP_Request->headers, "Host", h);
-    }
-    p->u.HTTP_Request->path = odr_strdup(odr, *cp1 ? cp1 : "/");
-    return p;
-}
-
-static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
-                                  Z_HTTP_Response *cookie_hres)
-{
-    struct Z_HTTP_Header *h;
-    Z_GDU *gdu = get_HTTP_Request_url(c->odr_out, uri);
-    char *combined_cookies;
-    int combined_cookies_len = 0;
-
-    gdu->u.HTTP_Request->method = odr_strdup(c->odr_out, "GET");
-    z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, "Accept",
-                      "text/xml");
-
-    for (h = cookie_hres->headers; h; h = h->next)
-    {
-        if (!strcmp(h->name, "Set-Cookie"))
-        {
-            char *cp;
-
-            if (!(cp = strchr(h->value, ';')))
-                cp = h->value + strlen(h->value);
-            if (cp - h->value >= 1) {
-                combined_cookies = xrealloc(combined_cookies, combined_cookies_len + cp - h->value + 3);
-                memcpy(combined_cookies+combined_cookies_len, h->value, cp - h->value);
-                combined_cookies[combined_cookies_len + cp - h->value] = '\0';
-                strcat(combined_cookies,"; ");
-                combined_cookies_len = strlen(combined_cookies);
-            }
-        }
-    }
-
-    if (combined_cookies_len)
-    {
-        z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
-                          "Cookie", combined_cookies);
-        xfree(combined_cookies);
-    }
-
-    if (c->user && c->password)
-    {
-        z_HTTP_header_add_basic_auth(c->odr_out, &gdu->u.HTTP_Request->headers,
-                                     c->user, c->password);
-    }
-    if (!z_GDU(c->odr_out, &gdu, 0, 0))
-        return zoom_complete;
-    if (c->odr_print)
-        z_GDU(c->odr_print, &gdu, 0, 0);
-    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
-
-    odr_reset(c->odr_out);
-    return do_write(c);
-}
 
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 {
     zoom_ret cret = zoom_complete;
     int ret = -1;
-    const char *addinfo = 0;
+    char *addinfo = 0;
     const char *connection_head = z_HTTP_header_lookup(hres->headers,
                                                        "Connection");
     const char *location;
 
     ZOOM_connection_set_mask(c, 0);
-    yaz_log(log_details, "%p handle_http", c);
-    
+    yaz_log(c->log_details, "%p handle_http", c);
+
+    yaz_cookies_response(c->cookies, hres);
     if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
         && (location = z_HTTP_header_lookup(hres->headers, "Location")))
     {
         c->no_redirects++;
         if (c->no_redirects > 10)
         {
-            set_HTTP_error(c, hres->code, 0, 0);
+            ZOOM_set_HTTP_error(c, hres->code, 0, 0);
             c->no_redirects = 0;
-            do_close(c);
+            ZOOM_connection_close(c);
         }
         else
         {
             /* since redirect may change host we just reconnect. A smarter
                implementation might check whether it's the same server */
-            do_connect_host(c, location, 0);
-            send_SRW_redirect(c, location, hres);
-            /* we're OK for now. Operation is not really complete */
-            ret = 0;
-            cret = zoom_pending;
+
+            int host_change = 0;
+            location = yaz_check_location(c->odr_in, c->host_port,
+                                          location, &host_change);
+            if (do_connect_host(c, location) == zoom_complete)
+                return;  /* connect failed.. */
+            send_HTTP_redirect(c, location);
+            return;
         }
     }
-    else 
-    {   /* not redirect (normal response) */
-        if (!yaz_srw_check_content_type(hres))
-            addinfo = "content-type";
-        else
-        {
-            Z_SOAP *soap_package = 0;
-            ODR o = c->odr_in;
-            Z_SOAP_Handler soap_handlers[2] = {
-                {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
-                {0, 0, 0}
-            };
-            ret = z_soap_codec(o, &soap_package,
-                               &hres->content_buf, &hres->content_len,
-                               soap_handlers);
-            if (!ret && soap_package->which == Z_SOAP_generic &&
-                soap_package->u.generic->no == 0)
-            {
-                Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
-                
-                ZOOM_options_set(c->options, "sru_version", sr->srw_version);
-                ZOOM_options_setl(c->options, "sru_extra_response_data",
-                                  sr->extraResponseData_buf, sr->extraResponseData_len);
-                if (sr->which == Z_SRW_searchRetrieve_response)
-                    cret = handle_srw_response(c, sr->u.response);
-                else if (sr->which == Z_SRW_scan_response)
-                    handle_srw_scan_response(c, sr->u.scan_response);
-                else
-                    ret = -1;
-            }
-            else if (!ret && (soap_package->which == Z_SOAP_fault
-                              || soap_package->which == Z_SOAP_error))
-            {
-                set_HTTP_error(c, hres->code,
-                               soap_package->u.fault->fault_code,
-                               soap_package->u.fault->fault_string);
-            }
-            else
-                ret = -1;
-        }   
+    else
+    {
+        ret = ZOOM_handle_sru(c, hres, &cret, &addinfo);
         if (ret == 0)
         {
             if (c->no_redirects) /* end of redirect. change hosts again */
-                do_close(c);
+                ZOOM_connection_close(c);
         }
         c->no_redirects = 0;
     }
     if (ret)
     {
         if (hres->code != 200)
-            set_HTTP_error(c, hres->code, 0, 0);
+            ZOOM_set_HTTP_error(c, hres->code, 0, 0);
         else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, addinfo);
-        do_close(c);
+        {
+            yaz_log(YLOG_LOG, "set error... addinfo=%s", addinfo ?
+                    addinfo : "NULL");
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, addinfo);
+        }
+        ZOOM_connection_close(c);
     }
     if (cret == zoom_complete)
     {
-        yaz_log(YLOG_LOG, "removing tasks in handle_http");
+        yaz_log(c->log_details, "removing tasks in handle_http");
         ZOOM_connection_remove_task(c);
     }
     {
@@ -4528,7 +1681,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         }
         if (must_close)
         {
-            do_close(c);
+            ZOOM_connection_close(c);
             if (c->tasks)
             {
                 c->tasks->running = 0;
@@ -4536,6 +1689,8 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
                 c->reconnect_ok = 0;
             }
         }
+        else
+            c->reconnect_ok = 1; /* if the server closes anyway */
     }
 }
 #endif
@@ -4544,21 +1699,21 @@ static int do_read(ZOOM_connection c)
 {
     int r, more;
     ZOOM_Event event;
-    
+
     event = ZOOM_Event_create(ZOOM_EVENT_RECV_DATA);
     ZOOM_connection_put_event(c, event);
-    
+
     r = cs_get(c->cs, &c->buf_in, &c->len_in);
     more = cs_more(c->cs);
-    yaz_log(log_details, "%p do_read len=%d more=%d", c, r, more);
+    yaz_log(c->log_details, "%p do_read len=%d more=%d", c, r, more);
     if (r == 1)
         return 0;
     if (r <= 0)
     {
         if (!ZOOM_test_reconnect(c))
         {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-            do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+            ZOOM_connection_close(c);
         }
     }
     else
@@ -4581,32 +1736,33 @@ static int do_read(ZOOM_connection c)
                     "ODR code %d:%d element=%s offset=%d",
                     err, x, element ? element : "<unknown>",
                     odr_offset(c->odr_in));
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, msg);
-            if (log_api)
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, msg);
+            if (c->log_api)
             {
                 FILE *ber_file = yaz_log_file();
                 if (ber_file)
                     odr_dumpBER(ber_file, c->buf_in, r);
             }
-            do_close(c);
+            ZOOM_connection_close(c);
         }
         else
         {
             if (c->odr_print)
                 z_GDU(c->odr_print, &gdu, 0, 0);
+            if (c->odr_save)
+                z_GDU(c->odr_save, &gdu, 0, 0);
             if (gdu->which == Z_GDU_Z3950)
-                recv_apdu(c, gdu->u.z3950);
+                ZOOM_handle_Z3950_apdu(c, gdu->u.z3950);
             else if (gdu->which == Z_GDU_HTTP_Response)
             {
 #if YAZ_HAVE_XML2
                 handle_http(c, gdu->u.HTTP_Response);
 #else
-                set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-                do_close(c);
+                ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+                ZOOM_connection_close(c);
 #endif
             }
         }
-        c->reconnect_ok = 0;
     }
     return 1;
 }
@@ -4615,46 +1771,46 @@ static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
 {
     int r;
     ZOOM_Event event;
-    
+
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
     ZOOM_connection_put_event(c, event);
 
-    yaz_log(log_details, "%p do_write_ex len=%d", c, len_out);
+    yaz_log(c->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);
+        yaz_log(c->log_details, "%p do_write_ex write failed", c);
         if (ZOOM_test_reconnect(c))
         {
             return zoom_pending;
         }
         if (c->state == STATE_CONNECTING)
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
         else
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-        do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+        ZOOM_connection_close(c);
         return zoom_complete;
     }
     else if (r == 1)
-    {    
+    {
         int mask = ZOOM_SELECT_EXCEPT;
         if (c->cs->io_pending & CS_WANT_WRITE)
             mask += ZOOM_SELECT_WRITE;
         if (c->cs->io_pending & CS_WANT_READ)
             mask += ZOOM_SELECT_READ;
         ZOOM_connection_set_mask(c, mask);
-        yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
+        yaz_log(c->log_details, "%p do_write_ex write incomplete mask=%d",
                 c, c->mask);
     }
     else
     {
         ZOOM_connection_set_mask(c, ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT);
-        yaz_log(log_details, "%p do_write_ex write complete mask=%d",
+        yaz_log(c->log_details, "%p do_write_ex write complete mask=%d",
                 c, c->mask);
     }
     return zoom_pending;
 }
 
-static zoom_ret do_write(ZOOM_connection c)
+zoom_ret ZOOM_send_buf(ZOOM_connection c)
 {
     return do_write_ex(c, c->buf_out, c->len_out);
 }
@@ -4663,20 +1819,56 @@ static zoom_ret do_write(ZOOM_connection c)
 ZOOM_API(const char *)
     ZOOM_connection_option_get(ZOOM_connection c, const char *key)
 {
-    return ZOOM_options_get(c->options, key);
+    if (!strcmp(key, "APDU"))
+    {
+        return c->saveAPDU_wrbuf ? wrbuf_cstr(c->saveAPDU_wrbuf) : "";
+    }
+    else
+        return ZOOM_options_get(c->options, key);
 }
 
 ZOOM_API(const char *)
     ZOOM_connection_option_getl(ZOOM_connection c, const char *key, int *lenp)
 {
-    return ZOOM_options_getl(c->options, key, lenp);
+    if (!strcmp(key, "APDU"))
+    {
+        if (c->saveAPDU_wrbuf)
+        {
+            *lenp = wrbuf_len(c->saveAPDU_wrbuf);
+            return wrbuf_cstr(c->saveAPDU_wrbuf);
+        }
+        else
+        {
+            *lenp = 0;
+            return "";
+        }
+    }
+    else
+        return ZOOM_options_getl(c->options, key, lenp);
 }
 
 ZOOM_API(void)
     ZOOM_connection_option_set(ZOOM_connection c, const char *key,
                                const char *val)
 {
-    ZOOM_options_set(c->options, key, val);
+    if (!strcmp(key, "saveAPDU"))
+    {
+        if (val && strcmp(val, "0"))
+        {
+            if (!c->saveAPDU_wrbuf)
+                c->saveAPDU_wrbuf = wrbuf_alloc();
+            else
+                wrbuf_rewind(c->saveAPDU_wrbuf);
+        }
+        else
+        {
+            wrbuf_destroy(c->saveAPDU_wrbuf);
+            c->saveAPDU_wrbuf = 0;
+        }
+        ZOOM_connection_save_apdu_wrbuf(c, c->saveAPDU_wrbuf);
+    }
+    else
+        ZOOM_options_set(c->options, key, val);
 }
 
 ZOOM_API(void)
@@ -4767,6 +1959,14 @@ ZOOM_API(const char *)
         return "CCL configuration error";
     case ZOOM_ERROR_CCL_PARSE:
         return "CCL parsing error";
+    case ZOOM_ERROR_ES_INVALID_ACTION:
+        return "Extended Service. invalid action";
+    case ZOOM_ERROR_ES_INVALID_VERSION:
+        return "Extended Service. invalid version";
+    case ZOOM_ERROR_ES_INVALID_SYNTAX:
+        return "Extended Service. invalid syntax";
+    case ZOOM_ERROR_MEMCACHED:
+        return "Memcached";
     default:
         return diagbib1_str(error);
     }
@@ -4808,20 +2008,20 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
 {
     ZOOM_Event event = 0;
     int r = cs_look(c->cs);
-    yaz_log(log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
+    yaz_log(c->log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
             c, mask, r);
-    
+
     if (r == CS_NONE)
     {
         event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
-        set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+        ZOOM_connection_close(c);
         ZOOM_connection_put_event(c, event);
     }
     else if (r == CS_CONNECT)
     {
         int ret = ret = cs_rcvconnect(c->cs);
-        yaz_log(log_details, "%p ZOOM_connection_do_io "
+        yaz_log(c->log_details, "%p ZOOM_connection_do_io "
                 "cs_rcvconnect returned %d", c, ret);
         if (ret == 1)
         {
@@ -4840,7 +2040,7 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
             ZOOM_connection_put_event(c, event);
             get_cert(c);
             if (c->proto == PROTO_Z3950)
-                ZOOM_connection_send_init(c);
+                ZOOM_connection_Z3950_send_init(c);
             else
             {
                 /* no init request for SRW .. */
@@ -4853,8 +2053,8 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
         }
         else
         {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
-            do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+            ZOOM_connection_close(c);
         }
     }
     else
@@ -4863,15 +2063,15 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
         {
             if (!ZOOM_test_reconnect(c))
             {
-                set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-                do_close(c);
+                ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+                ZOOM_connection_close(c);
             }
             return;
         }
         if (mask & ZOOM_SELECT_READ)
             do_read(c);
         if (c->cs && (mask & ZOOM_SELECT_WRITE))
-            do_write(c);
+            ZOOM_send_buf(c);
     }
 }
 
@@ -4884,77 +2084,14 @@ ZOOM_API(int)
 }
 
 
-static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
-{
-    WRBUF wrbuf = (WRBUF) client_data;
-    wrbuf_puts(wrbuf, buf);
-}
-
-/*
- * Returns an xmalloc()d string containing RPN that corresponds to the
- * CQL passed in.  On error, sets the Connection object's error state
- * and returns a null pointer.
- * ### We could cache CQL parser and/or transformer in Connection.
- */
-static char *cql2pqf(ZOOM_connection c, const char *cql)
-{
-    CQL_parser parser;
-    int error;
-    const char *cqlfile;
-    cql_transform_t trans;
-    char *result = 0;
-
-    parser = cql_parser_create();
-    if ((error = cql_parser_string(parser, cql)) != 0) {
-        cql_parser_destroy(parser);
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_PARSE, cql);
-        return 0;
-    }
-
-    cqlfile = ZOOM_connection_option_get(c, "cqlfile");
-    if (cqlfile == 0) 
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
-    }
-    else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
-    {
-        char buf[512];        
-        sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
-                cqlfile, strerror(errno));
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
-    }
-    else 
-    {
-        WRBUF wrbuf_result = wrbuf_alloc();
-        error = cql_transform(trans, cql_parser_result(parser),
-                              cql2pqf_wrbuf_puts, wrbuf_result);
-        if (error != 0) {
-            char buf[512];
-            const char *addinfo;
-            error = cql_transform_error(trans, &addinfo);
-            sprintf(buf, "%.200s (addinfo=%.200s)", 
-                    cql_strerror(error), addinfo);
-            set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
-        }
-        else
-        {
-            result = xstrdup(wrbuf_cstr(wrbuf_result));
-        }
-        cql_transform_close(trans);
-        wrbuf_destroy(wrbuf_result);
-    }
-    cql_parser_destroy(parser);
-    return result;
-}
-
 ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
 {
     if (c->mask)
     {
         ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
         /* timeout and this connection was waiting */
-        set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_TIMEOUT, 0);
+        ZOOM_connection_close(c);
         ZOOM_connection_put_event(c, event);
     }
     return 0;
@@ -4988,8 +2125,8 @@ ZOOM_API(int)
 {
     int i;
 
-    yaz_log(log_details, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
-    
+    yaz_log(log_details0, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
+
     for (i = 0; i<no; i++)
     {
         ZOOM_connection c = cs[i];
@@ -5018,7 +2155,7 @@ ZOOM_API(int) ZOOM_connection_set_mask(ZOOM_connection c, int mask)
 {
     c->mask = mask;
     if (!c->cs)
-        return -1; 
+        return -1;
     return 0;
 }
 
@@ -5036,7 +2173,11 @@ ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
 
 ZOOM_API(void) ZOOM_connection_close(ZOOM_connection c)
 {
-    do_close(c);
+    if (c->cs)
+        cs_close(c->cs);
+    c->cs = 0;
+    ZOOM_connection_set_mask(c, 0);
+    c->state = STATE_IDLE;
 }
 
 /*