Refactor ZOOM Z39.50 functions to zoom-z3950.c
authorAdam Dickmeiss <adam@indexdata.dk>
Wed, 11 Aug 2010 11:09:37 +0000 (13:09 +0200)
committerAdam Dickmeiss <adam@indexdata.dk>
Wed, 11 Aug 2010 11:09:37 +0000 (13:09 +0200)
src/Makefile.am
src/zoom-c.c
src/zoom-p.h
src/zoom-z3950.c [new file with mode: 0644]
win/makefile

index e79d237..4fbb1b3 100644 (file)
@@ -88,7 +88,7 @@ libyaz_la_SOURCES=version.c options.c log.c \
   zget.c yaz-ccl.c diag-entry.c diag-entry.h \
   logrpn.c \
   otherinfo.c pquery.c sortspec.c charneg.c initopt.c \
-  zoom-c.c zoom-socket.c zoom-opt.c zoom-p.h \
+  zoom-c.c zoom-z3950.c zoom-socket.c zoom-opt.c zoom-p.h \
   grs1disp.c zgdu.c soap.c srw.c srwutil.c uri.c \
   opacdisp.c cclfind.c ccltoken.c cclerrms.c cclqual.c cclptree.c cclp.h \
   cclqfile.c cclstr.c cclxmlconfig.c ccl_stop_words.c \
index d95b04d..923e500 100644 (file)
 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);
 
@@ -66,39 +60,23 @@ ZOOM_API(const char *) ZOOM_get_event_str(int event)
     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 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;
 }
 
@@ -107,7 +85,7 @@ static void ZOOM_Event_destroy(ZOOM_Event event)
     xfree(event);
 }
 
-static void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
+void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
 {
     if (c->m_queue_back)
     {
@@ -161,9 +139,9 @@ ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
 
 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;
 
@@ -188,7 +166,7 @@ static void set_dset_error(ZOOM_connection c, int error,
         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 : "");
@@ -209,23 +187,22 @@ static int uri_to_code(const char *uri)
 static void set_HTTP_error(ZOOM_connection c, int error,
                            const char *addinfo, const char *addinfo2)
 {
-    set_dset_error(c, error, "HTTP", addinfo, addinfo2);
+    ZOOM_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);
+        ZOOM_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 +227,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);
     }
 }
 
@@ -362,9 +339,6 @@ 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);
 
 ZOOM_API(ZOOM_connection)
     ZOOM_connection_create(ZOOM_options options)
@@ -373,7 +347,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 +359,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;
@@ -428,8 +405,8 @@ ZOOM_API(ZOOM_connection)
 
 /* 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");
@@ -480,10 +457,10 @@ ZOOM_API(void)
 
     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,17 +478,17 @@ 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);
     }
 
@@ -520,7 +497,7 @@ ZOOM_API(void)
     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,7 +505,7 @@ 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
@@ -592,7 +569,7 @@ ZOOM_API(void)
     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 +578,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);
     }
@@ -633,7 +610,7 @@ ZOOM_API(void)
         ZOOM_options_get_int(c->options, "preferredMessageSize", 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);
+    yaz_log(c->log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
  
     task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
 
@@ -649,7 +626,6 @@ ZOOM_API(ZOOM_query)
 {
     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;
@@ -666,7 +642,6 @@ ZOOM_API(void)
         return;
 
     (s->refcount)--;
-    yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
     if (s->refcount == 0)
     {
         odr_destroy(s->odr);
@@ -683,11 +658,9 @@ ZOOM_API(int)
     s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
     if (!s->z_query->u.type_1)
     {
-        yaz_log(log_details, "%p ZOOM_query_prefix str=%s failed", s, str);
         s->z_query = 0;
         return -1;
     }
-    yaz_log(log_details, "%p ZOOM_query_prefix str=%s", s, str);
     return 0;
 }
 
@@ -709,8 +682,6 @@ ZOOM_API(int)
     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;
 }
 
@@ -728,7 +699,6 @@ ZOOM_API(int)
     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);
 
@@ -783,17 +753,10 @@ ZOOM_API(int)
 {
     s->sort_spec = yaz_sort_spec(s->odr, criteria);
     if (!s->sort_spec)
-    {
-        yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s failed",
-                s, criteria);
         return -1;
-    }
-    yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s", s, criteria);
     return 0;
 }
 
-static zoom_ret do_write(ZOOM_connection c);
-
 ZOOM_API(void) ZOOM_resultset_release(ZOOM_resultset r)
 {
 #if ZOOM_RESULT_LISTS
@@ -827,7 +790,7 @@ ZOOM_API(void)
 #endif
     if (!c)
         return;
-    yaz_log(log_api, "%p ZOOM_connection_destroy", c);
+    yaz_log(c->log_api, "%p ZOOM_connection_destroy", c);
     if (c->cs)
         cs_close(c->cs);
 
@@ -879,7 +842,7 @@ 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);
     }
@@ -892,7 +855,7 @@ 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);
@@ -946,7 +909,7 @@ ZOOM_API(ZOOM_resultset)
     ZOOM_resultsets set;
 #endif
 
-    yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
+    yaz_log(c->log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
     r->r_sort_spec = q->sort_spec;
     r->query = q;
 
@@ -968,7 +931,7 @@ ZOOM_API(ZOOM_resultset)
     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;
@@ -988,12 +951,12 @@ ZOOM_API(ZOOM_resultset)
     {
         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;
         }
     }
@@ -1041,7 +1004,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,12 +1013,12 @@ 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;
         }
@@ -1121,13 +1084,13 @@ 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);
@@ -1148,27 +1111,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 +1141,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,12 +1151,12 @@ 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;
         }
@@ -1245,7 +1197,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;
@@ -1341,7 +1293,7 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
 {
     void *add;
 
-    yaz_log(log_details, "%p do_connect effective_host=%s", c, effective_host);
+    yaz_log(c->log_details, "%p do_connect effective_host=%s", c, effective_host);
 
     if (c->cs)
         cs_close(c->cs);
@@ -1362,8 +1314,8 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
             yaz_encode_sru_dbpath_buf(c->path, db);
         }
 #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 +1328,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 .. */
@@ -1401,181 +1353,14 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
         }
     }
     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)
 {
@@ -1610,7 +1395,7 @@ static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
     ZOOM_connection_put_event(c, event);
     odr_reset(c->odr_out);
-    return do_write(c);
+    return ZOOM_send_buf(c);
 }
 #endif
 
@@ -1660,9 +1445,9 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
         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);
+                ZOOM_record_cache_lookup(resultset, i + *start,
+                                         c->tasks->u.retrieve.syntax,
+                                         c->tasks->u.retrieve.elementSetName);
             if (!rec)
                 break;
             else
@@ -1697,7 +1482,7 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
     }
     else
     {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
         return zoom_complete;
     }
     sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
@@ -1721,213 +1506,6 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
 }
 #endif
 
-static zoom_ret ZOOM_connection_Z3950_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(oi, c->odr_out, 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)
 {
@@ -1942,7 +1520,7 @@ ZOOM_API(ZOOM_record)
     buf = odr_getbuf(odr_enc, &size, 0);
     
     nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
-    yaz_log(log_details, "ZOOM_record create");
+    yaz_log(log_details0, "ZOOM_record create");
     nrec->odr = odr_createmem(ODR_DECODE);
 #if SHPTR
     nrec->record_wrbuf = 0;
@@ -1969,7 +1547,7 @@ ZOOM_API(ZOOM_record)
     const char *elementSetName =
         ZOOM_options_get(s->options, "elementSetName");
 
-    return record_cache_lookup(s, pos, syntax, elementSetName);
+    return ZOOM_record_cache_lookup(s, pos, syntax, elementSetName);
 }
 
 ZOOM_API(ZOOM_record)
@@ -1997,7 +1575,7 @@ ZOOM_API(void)
     ZOOM_record_destroy(ZOOM_record rec)
 {
     ZOOM_record_release(rec);
-    yaz_log(log_details, "ZOOM_record destroy");
+    yaz_log(log_details0, "ZOOM_record destroy");
     xfree(rec);
 }
 
@@ -2396,11 +1974,11 @@ static size_t record_hash(int pos)
     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)
+void ZOOM_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;
     
@@ -2456,9 +2034,9 @@ static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr,
     }
 }
 
-static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
-                                       const char *syntax,
-                                       const char *elementSetName)
+ZOOM_record ZOOM_record_cache_lookup(ZOOM_resultset r, int pos,
+                                     const char *syntax,
+                                     const char *elementSetName)
 {
     ZOOM_record_cache rc;
     
@@ -2478,573 +2056,71 @@ static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
     return 0;
 }
                                              
-static void handle_Z3950_records(ZOOM_connection c, Z_Records *sr,
-                                 int present_phase)
+
+ZOOM_API(ZOOM_scanset)
+    ZOOM_connection_scan(ZOOM_connection c, const char *start)
 {
-    ZOOM_resultset resultset;
-    int *start, *count;
-    const char *syntax = 0, *elementSetName = 0;
+    ZOOM_scanset s;
+    ZOOM_query q = ZOOM_query_create();
 
-    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);
-        }
-    }
-}
+    ZOOM_query_prefix(q, start);
 
-static void handle_Z3950_present_response(ZOOM_connection c,
-                                          Z_PresentResponse *pr)
-{
-    handle_Z3950_records(c, pr->records, 1);
-}
+    s = ZOOM_connection_scan1(c, q);
+    ZOOM_query_destroy(q);
+    return s;
 
-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)
+ZOOM_API(ZOOM_scanset)
+    ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
 {
-    char opt_name[80];
-    
-    switch (exp->which)
+    ZOOM_scanset scan = 0;
+
+    if (!q->z_query)
+        return 0;
+    scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
+    scan->connection = c;
+    scan->odr = odr_createmem(ODR_DECODE);
+    scan->options = ZOOM_options_create_with_parent(c->options);
+    scan->refcount = 1;
+    scan->scan_response = 0;
+    scan->srw_scan_response = 0;
+
+    scan->query = q;
+    (q->refcount)++;
+    scan->databaseNames = ZOOM_connection_get_databases(c, c->options,
+                                            &scan->num_databaseNames,
+                                            scan->odr);
+
+    if (1)
     {
-    case Z_QueryExpression_term:
-        if (exp->u.term && exp->u.term->queryTerm)
+        ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
+        task->u.scan.scan = scan;
+        
+        (scan->refcount)++;
+        if (!c->async)
         {
-            sprintf(opt_name, "%s.term", name);
-            handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
+            while (ZOOM_event(1, &c))
+                ;
         }
-        break;
-    case Z_QueryExpression_query:
-        break;
     }
+    return scan;
 }
 
-static void handle_search_result(ZOOM_connection c, ZOOM_resultset resultset,
-                                Z_OtherInformation *o)
+ZOOM_API(void)
+    ZOOM_scanset_destroy(ZOOM_scanset scan)
 {
-    int i;
-    for (i = 0; o && i < o->num_elements; i++)
+    if (!scan)
+        return;
+    (scan->refcount)--;
+    if (scan->refcount == 0)
     {
-        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) {
+        ZOOM_query_destroy(scan->query);
 
-    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 yaz_facet_attr attr_values;
-    ZOOM_facet_field facet_field = odr_malloc(odr, sizeof(*facet_field));
-    yaz_facet_attr_init(&attr_values);
-    yaz_facet_attr_get_z_attributes(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_Z3950_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_Z3950_records(c, sr->records, 0);
-}
-
-static void handle_Z3950_sort_response(ZOOM_connection c, Z_SortResponse *res)
-{
-    if (res->diagnostics && res->num_diagnostics > 0)
-        response_diag(c, res->diagnostics[0]);
-}
-
-static int handle_Z3950_scan_response(ZOOM_connection c, Z_ScanResponse *res)
-{
-    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;
-}
-
-static zoom_ret send_Z3950_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;
-}
-
-static zoom_ret send_Z3950_present(ZOOM_connection c)
-{
-    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. */
-
-            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)
-    {
-        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;
-    }
-    req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
-    return send_APDU(c, apdu);
-}
-
-ZOOM_API(ZOOM_scanset)
-    ZOOM_connection_scan(ZOOM_connection c, const char *start)
-{
-    ZOOM_scanset s;
-    ZOOM_query q = ZOOM_query_create();
-
-    ZOOM_query_prefix(q, start);
-
-    s = ZOOM_connection_scan1(c, q);
-    ZOOM_query_destroy(q);
-    return s;
-
-}
-
-ZOOM_API(ZOOM_scanset)
-    ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
-{
-    ZOOM_scanset scan = 0;
-
-    if (!q->z_query)
-        return 0;
-    scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
-    scan->connection = c;
-    scan->odr = odr_createmem(ODR_DECODE);
-    scan->options = ZOOM_options_create_with_parent(c->options);
-    scan->refcount = 1;
-    scan->scan_response = 0;
-    scan->srw_scan_response = 0;
-
-    scan->query = q;
-    (q->refcount)++;
-    scan->databaseNames = set_DatabaseNames(c, c->options,
-                                            &scan->num_databaseNames,
-                                            scan->odr);
-
-    if (1)
-    {
-        ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
-        task->u.scan.scan = scan;
-        
-        (scan->refcount)++;
-        if (!c->async)
-        {
-            while (ZOOM_event(1, &c))
-                ;
-        }
-    }
-    return scan;
-}
-
-ZOOM_API(void)
-    ZOOM_scanset_destroy(ZOOM_scanset scan)
-{
-    if (!scan)
-        return;
-    (scan->refcount)--;
-    if (scan->refcount == 0)
-    {
-        ZOOM_query_destroy(scan->query);
-
-        odr_destroy(scan->odr);
-        
-        ZOOM_options_destroy(scan->options);
-        xfree(scan);
+        odr_destroy(scan->odr);
+        
+        ZOOM_options_destroy(scan->options);
+        xfree(scan);
     }
 }
 
@@ -3052,7 +2128,7 @@ 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);
@@ -3063,75 +2139,7 @@ static zoom_ret send_package(ZOOM_connection c)
     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_Z3950_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);
+    return ZOOM_send_buf(c);
 }
 
 #if YAZ_HAVE_XML2
@@ -3162,7 +2170,7 @@ static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
     }
     else
     {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
         return zoom_complete;
     }
 
@@ -3299,489 +2307,6 @@ ZOOM_API(void)
     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_API(ZOOM_package)
     ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
@@ -3847,18 +2372,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;
     }
@@ -3901,302 +2426,22 @@ ZOOM_API(int)
     }
     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_Z3950_sort_present(ZOOM_connection c)
-{
-    zoom_ret r = zoom_complete;
-
-    if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
-        r = send_Z3950_sort(c, c->tasks->u.search.resultset);
-    if (r == zoom_complete)
-        r = send_Z3950_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 handle_Z3950_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);
-    }
-}
-
-
-static void interpret_otherinformation_field(ZOOM_connection c,
-                                             Z_OtherInformation *ui)
-{
-    int i;
-    for (i = 0; i < ui->num_elements; i++)
-    {
-        Z_OtherInformationUnit *unit = ui->list[i];
-        if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
-            unit->information.externallyDefinedInfo &&
-            unit->information.externallyDefinedInfo->which ==
-            Z_External_diag1) 
-        {
-            interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
-        } 
-    }
-}
-
-
-static void 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");
-}
-
-
-static void handle_Z3950_apdu(ZOOM_connection c, Z_APDU *apdu)
-{
-    Z_InitResponse *initrs;
-    
-    ZOOM_connection_set_mask(c, 0);
-    yaz_log(log_details, "%p handle_Z3950_apdu apdu->which=%d", c, apdu->which);
-    switch (apdu->which)
-    {
-    case Z_APDU_initResponse:
-        yaz_log(log_api, "%p handle_Z3950_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 handle_Z3950_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 handle_Z3950_apdu Search response", c);
-        handle_Z3950_search_response(c, apdu->u.searchResponse);
-        if (send_Z3950_sort_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_presentResponse:
-        yaz_log(log_api, "%p handle_Z3950_apdu Present response", c);
-        handle_Z3950_present_response(c, apdu->u.presentResponse);
-        if (send_Z3950_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_sortResponse:
-        yaz_log(log_api, "%p handle_Z3950_apdu Sort response", c);
-        handle_Z3950_sort_response(c, apdu->u.sortResponse);
-        if (send_Z3950_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_scanResponse:
-        yaz_log(log_api, "%p handle_Z3950_apdu Scan response", c);
-        handle_Z3950_scan_response(c, apdu->u.scanResponse);
-        ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_extendedServicesResponse:
-        yaz_log(log_api, "%p handle_Z3950_apdu Extended Services response", c);
-        handle_Z3950_es_response(c, apdu->u.extendedServicesResponse);
-        ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_close:
-        yaz_log(log_api, "%p handle_Z3950_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);
-    }
-}
-
 #if YAZ_HAVE_XML2
 static zoom_ret handle_srw_response(ZOOM_connection c,
                                     Z_SRW_searchRetrieveResponse *res)
@@ -4243,7 +2488,7 @@ static zoom_ret handle_srw_response(ZOOM_connection c,
     if (res->resultSetId)
         ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
 
-    yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
+    yaz_log(c->log_details, "%p handle_srw_response got SRW response OK", c);
 
     if (res->num_diagnostics > 0)
     {
@@ -4297,8 +2542,8 @@ static zoom_ret handle_srw_response(ZOOM_connection c,
                                                  &diag, &num_diag,
                                                  resultset->odr);
             }
-            record_cache_add(resultset, npr, pos, syntax, elementSetName,
-                             sru_rec->recordSchema, diag);
+            ZOOM_record_cache_add(resultset, npr, pos, syntax, elementSetName,
+                                  sru_rec->recordSchema, diag);
         }
         *count -= i;
         *start += i;
@@ -4417,7 +2662,7 @@ static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
 
     odr_reset(c->odr_out);
-    return do_write(c);
+    return ZOOM_send_buf(c);
 }
 
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
@@ -4430,7 +2675,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
     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);
     
     if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
         && (location = z_HTTP_header_lookup(hres->headers, "Location")))
@@ -4440,7 +2685,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         {
             set_HTTP_error(c, hres->code, 0, 0);
             c->no_redirects = 0;
-            do_close(c);
+            ZOOM_connection_close(c);
         }
         else
         {
@@ -4496,7 +2741,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         if (ret == 0)
         {
             if (c->no_redirects) /* end of redirect. change hosts again */
-                do_close(c);
+                ZOOM_connection_close(c);
         }
         c->no_redirects = 0;
     }
@@ -4505,8 +2750,8 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         if (hres->code != 200)
             set_HTTP_error(c, hres->code, 0, 0);
         else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, addinfo);
-        do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, addinfo);
+        ZOOM_connection_close(c);
     }
     if (cret == zoom_complete)
     {
@@ -4529,7 +2774,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;
@@ -4551,15 +2796,15 @@ static int do_read(ZOOM_connection c)
     
     r = cs_get(c->cs, &c->buf_in, &c->len_in);
     more = cs_more(c->cs);
-    yaz_log(log_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
@@ -4582,28 +2827,28 @@ 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 (gdu->which == Z_GDU_Z3950)
-                handle_Z3950_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
             }
         }
@@ -4620,19 +2865,19 @@ static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
     ZOOM_connection_put_event(c, event);
 
-    yaz_log(log_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)
@@ -4643,19 +2888,19 @@ static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
         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);
 }
@@ -4809,20 +3054,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)
         {
@@ -4841,7 +3086,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 .. */
@@ -4854,8 +3099,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
@@ -4864,15 +3109,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);
     }
 }
 
@@ -4908,21 +3153,21 @@ static char *cql2pqf(ZOOM_connection c, const char *cql)
     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);
+        ZOOM_set_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");
+        ZOOM_set_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);
+        ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
     }
     else 
     {
@@ -4935,7 +3180,7 @@ static char *cql2pqf(ZOOM_connection c, const char *cql)
             error = cql_transform_error(trans, &addinfo);
             sprintf(buf, "%.200s (addinfo=%.200s)", 
                     cql_strerror(error), addinfo);
-            set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+            ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
         }
         else
         {
@@ -4954,8 +3199,8 @@ ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
     {
         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;
@@ -4989,7 +3234,7 @@ 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++)
     {
@@ -5037,7 +3282,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;
 }
 
 /*
index 51125b5..6a95040 100644 (file)
@@ -116,6 +116,9 @@ struct ZOOM_connection_p {
     ZOOM_Event m_queue_back;
     zoom_sru_mode sru_mode;
     int no_redirects; /* 0 for no redirects. >0 for number of redirects */
+
+    int log_details;
+    int log_api;
 };
 
 #if ZOOM_RESULT_LISTS
@@ -277,8 +280,47 @@ struct ZOOM_Event_p {
     ZOOM_Event prev;
 };
 
+typedef enum {
+    zoom_pending,
+    zoom_complete
+} zoom_ret;
+
 void ZOOM_options_addref (ZOOM_options opt);
 
+void ZOOM_handle_Z3950_apdu(ZOOM_connection c, Z_APDU *apdu);
+
+void ZOOM_set_dset_error(ZOOM_connection c, int error,
+                         const char *dset,
+                         const char *addinfo, const char *addinfo2);
+
+void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo);
+
+ZOOM_Event ZOOM_Event_create(int kind);
+void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event);
+
+zoom_ret ZOOM_connection_Z3950_send_search(ZOOM_connection c);
+zoom_ret send_Z3950_present(ZOOM_connection c);
+zoom_ret ZOOM_connection_Z3950_send_scan(ZOOM_connection c);
+zoom_ret ZOOM_send_buf(ZOOM_connection c);
+zoom_ret send_Z3950_sort(ZOOM_connection c, ZOOM_resultset resultset);
+char **ZOOM_connection_get_databases(ZOOM_connection con, ZOOM_options options,
+                                     int *num, ODR odr);
+zoom_ret ZOOM_connection_Z3950_send_init(ZOOM_connection c);
+
+ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which);
+void ZOOM_connection_remove_task(ZOOM_connection c);
+int ZOOM_test_reconnect(ZOOM_connection c);
+
+ZOOM_record ZOOM_record_cache_lookup(ZOOM_resultset r, int pos,
+                                     const char *syntax,
+                                     const char *elementSetName);
+void ZOOM_record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
+                           int pos,
+                           const char *syntax, const char *elementSetName,
+                           const char *schema,
+                           Z_SRW_diagnostic *diag);
+
+
 /*
  * Local variables:
  * c-basic-offset: 4
diff --git a/src/zoom-z3950.c b/src/zoom-z3950.c
new file mode 100644 (file)
index 0000000..bf31a7c
--- /dev/null
@@ -0,0 +1,1776 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-z3950.c
+ * \brief Implements ZOOM Z39.50 handling
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/yaz-util.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>
+
+#include <yaz/shptr.h>
+
+/*
+ * 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 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 = ZOOM_connection_get_databases(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 = ZOOM_connection_get_databases(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;
+}
+
+
+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(c->log_api, "%p encoding_APDU: encoding failed", c);
+        ZOOM_set_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(c->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(c->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 ZOOM_send_buf(c);
+}
+
+zoom_ret ZOOM_connection_Z3950_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);
+}
+
+zoom_ret ZOOM_connection_Z3950_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(c->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(oi, c->odr_out, facet_list);
+            }
+        }
+    }
+
+    assert(r);
+    assert(r->query);
+
+    /* prepare query for the search request */
+    search_req->query = r->query->z_query;
+    if (!search_req->query)
+    {
+        ZOOM_set_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 */
+                    ZOOM_set_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(c->log_details, "%p ZOOM_connection_send_search: "
+                    "allocating set %s", c, r->setname);
+        }
+        else
+        {
+            yaz_log(c->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);
+}
+
+zoom_ret ZOOM_connection_Z3950_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(c->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
+        {
+            ZOOM_set_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
+            return zoom_complete;
+        }
+    }
+    else
+    {
+        ZOOM_set_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);
+}
+
+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))
+                    ;
+            }
+        }
+    }
+}
+
+
+static void handle_Z3950_records(ZOOM_connection c, Z_Records *sr,
+                                 int present_phase);
+
+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;
+    ZOOM_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)
+        ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+    else
+        response_default_diag(c, p->u.defaultFormat);
+}
+
+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 handle_Z3950_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);
+    }
+}
+
+
+static void interpret_otherinformation_field(ZOOM_connection c,
+                                             Z_OtherInformation *ui)
+{
+    int i;
+    for (i = 0; i < ui->num_elements; i++)
+    {
+        Z_OtherInformationUnit *unit = ui->list[i];
+        if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
+            unit->information.externallyDefinedInfo &&
+            unit->information.externallyDefinedInfo->which ==
+            Z_External_diag1) 
+        {
+            interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
+        } 
+    }
+}
+
+static 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 yaz_facet_attr attr_values;
+    ZOOM_facet_field facet_field = odr_malloc(odr, sizeof(*facet_field));
+    yaz_facet_attr_init(&attr_values);
+    yaz_facet_attr_get_z_attributes(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_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 void handle_Z3950_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_Z3950_records(c, sr->records, 0);
+}
+
+static void handle_Z3950_sort_response(ZOOM_connection c, Z_SortResponse *res)
+{
+    if (res->diagnostics && res->num_diagnostics > 0)
+        response_diag(c, res->diagnostics[0]);
+}
+
+static int handle_Z3950_scan_response(ZOOM_connection c, Z_ScanResponse *res)
+{
+    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;
+}
+
+static void handle_Z3950_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
+            ZOOM_set_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++)
+            {
+                ZOOM_record_cache_add(resultset, p->records[i], i + *start,
+                                      syntax, elementSetName,
+                                      elementSetName, 0);
+            }
+            *count -= i;
+            if (*count < 0)
+                *count = 0;
+            *start += i;
+            yaz_log(c->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");
+                ZOOM_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");
+            ZOOM_record_cache_add(resultset, myrec, *start,
+                                  syntax, elementSetName, 0, 0);
+        }
+    }
+}
+
+static void handle_Z3950_present_response(ZOOM_connection c,
+                                          Z_PresentResponse *pr)
+{
+    handle_Z3950_records(c, pr->records, 1);
+}
+
+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");
+}
+
+
+zoom_ret send_Z3950_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;
+}
+
+zoom_ret send_Z3950_present(ZOOM_connection c)
+{
+    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(c->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(c->log_details, "%p send_present start=%d count=%d",
+            c, *start, *count);
+
+    if (*start < 0 || *count < 0 || *start + *count > resultset->size)
+    {
+        ZOOM_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(c->log_details, "send_present resultset=%p start=%d count=%d",
+            resultset, *start, *count);
+
+    for (i = 0; i < *count; i++)
+    {
+        ZOOM_record rec =
+            ZOOM_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(c->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(c->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. */
+
+            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)
+    {
+        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;
+    }
+    req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
+    return send_APDU(c, apdu);
+}
+
+static zoom_ret send_Z3950_sort_present(ZOOM_connection c)
+{
+    zoom_ret r = zoom_complete;
+
+    if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
+        r = send_Z3950_sort(c, c->tasks->u.search.resultset);
+    if (r == zoom_complete)
+        r = send_Z3950_present(c);
+    return r;
+}
+
+void ZOOM_handle_Z3950_apdu(ZOOM_connection c, Z_APDU *apdu)
+{
+    Z_InitResponse *initrs;
+    
+    ZOOM_connection_set_mask(c, 0);
+    yaz_log(c->log_details, "%p handle_Z3950_apdu apdu->which=%d",
+            c, apdu->which);
+    switch (apdu->which)
+    {
+    case Z_APDU_initResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_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;
+
+            ZOOM_set_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(c->log_details, "%p handle_Z3950_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(c->log_api, "%p handle_Z3950_apdu Search response", c);
+        handle_Z3950_search_response(c, apdu->u.searchResponse);
+        if (send_Z3950_sort_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_presentResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Present response", c);
+        handle_Z3950_present_response(c, apdu->u.presentResponse);
+        if (send_Z3950_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_sortResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Sort response", c);
+        handle_Z3950_sort_response(c, apdu->u.sortResponse);
+        if (send_Z3950_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_scanResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Scan response", c);
+        handle_Z3950_scan_response(c, apdu->u.scanResponse);
+        ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_extendedServicesResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Extended Services response", c);
+        handle_Z3950_es_response(c, apdu->u.extendedServicesResponse);
+        ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_close:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Close PDU", c);
+        if (!ZOOM_test_reconnect(c))
+        {
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+            ZOOM_connection_close(c);
+        }
+        break;
+    default:
+        yaz_log(c->log_api, "%p Received unknown PDU", c);
+        ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+        ZOOM_connection_close(c);
+    }
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index 5405807..51d1c24 100644 (file)
@@ -473,6 +473,7 @@ MISC_OBJS= \
    $(OBJDIR)\srw.obj \
    $(OBJDIR)\srwutil.obj \
    $(OBJDIR)\zoom-c.obj \
+   $(OBJDIR)\zoom-z3950.obj \
    $(OBJDIR)\facet.obj \
    $(OBJDIR)\zoom-opt.obj \
    $(OBJDIR)\zoom-socket.obj \