Added cs_get_SSL. yaz-client-ssl prints peer info
[yaz-moved-to-github.git] / src / zoom-c.c
index 0c3e85f..b5241f3 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000-2003, Index Data
+ * Copyright (c) 2000-2004, Index Data
  * See the file LICENSE for details.
  *
- * $Id: zoom-c.c,v 1.5 2003-11-24 11:25:07 mike Exp $
+ * $Id: zoom-c.c,v 1.26 2004-04-06 17:47:24 adam Exp $
  *
  * ZOOM layer for C, connections, result sets, queries.
  */
@@ -90,10 +90,18 @@ static void set_dset_error (ZOOM_connection c, int error,
                             const char *dset,
                             const char *addinfo, const char *addinfo2)
 {
+    char *cp;
     xfree (c->addinfo);
     c->addinfo = 0;
     c->error = error;
-    c->diagset = dset;
+    if (!c->diagset || strcmp(dset, c->diagset))
+    {
+        xfree(c->diagset);
+        c->diagset = xstrdup(dset);
+       /* remove integer part from SRW diagset .. */
+        if ((cp = strrchr(c->diagset, '/')))
+          *cp = '\0';
+    }
     if (addinfo && addinfo2)
     {
         c->addinfo = xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
@@ -191,6 +199,10 @@ void ZOOM_connection_remove_task (ZOOM_connection c)
         case ZOOM_TASK_PACKAGE:
             ZOOM_package_destroy (task->u.package);
             break;
+       case ZOOM_TASK_SORT:
+           ZOOM_resultset_destroy (task->u.sort.resultset);
+           ZOOM_query_destroy(task->u.sort.q);
+           break;
        default:
            assert (0);
        }
@@ -219,6 +231,7 @@ ZOOM_connection_create (ZOOM_options options)
     c->reconnect_ok = 0;
     c->state = STATE_IDLE;
     c->addinfo = 0;
+    c->diagset = 0;
     set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
     c->buf_in = 0;
     c->len_in = 0;
@@ -422,7 +435,10 @@ ZOOM_query_prefix(ZOOM_query s, const char *str)
     s->z_query->which = Z_Query_type_1;
     s->z_query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
     if (!s->z_query->u.type_1)
+    {
+       s->z_query = 0;
        return -1;
+    }
     return 0;
 }
 
@@ -471,6 +487,7 @@ ZOOM_connection_destroy(ZOOM_connection c)
 
     xfree (c->buf_in);
     xfree (c->addinfo);
+    xfree (c->diagset);
     odr_destroy (c->odr_in);
     odr_destroy (c->odr_out);
     ZOOM_options_destroy (c->options);
@@ -495,6 +512,7 @@ void ZOOM_resultset_addref (ZOOM_resultset r)
                  r, r->refcount);
     }
 }
+
 ZOOM_resultset ZOOM_resultset_create ()
 {
     ZOOM_resultset r = (ZOOM_resultset) xmalloc (sizeof(*r));
@@ -544,7 +562,12 @@ ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
 
     r->start = ZOOM_options_get_int(r->options, "start", 0);
     r->count = ZOOM_options_get_int(r->options, "count", 0);
-    r->step = ZOOM_options_get_int(r->options, "step", 0);
+    {
+       /* If "presentChunk" is defined use that; otherwise "step" */
+       const char *cp = ZOOM_options_get (r->options, "presentChunk");
+       r->step = ZOOM_options_get_int(r->options,
+                                      (cp != 0 ? "presentChunk": "step"), 0);
+    }
     r->piggyback = ZOOM_options_get_bool (r->options, "piggyback", 1);
     cp = ZOOM_options_get (r->options, "setname");
     if (cp)
@@ -587,6 +610,62 @@ ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
 }
 
 ZOOM_API(void)
+    ZOOM_resultset_sort(ZOOM_resultset r,
+                       const char *sort_type, const char *sort_spec)
+{
+    ZOOM_connection c = r->connection;
+    ZOOM_task task;
+
+    if (!c)
+       return;
+
+    if (c->host_port && c->proto == PROTO_HTTP)
+    {
+        if (!c->cs)
+        {
+            yaz_log(LOG_DEBUG, "NO COMSTACK");
+            ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
+        }
+        else
+        {
+            yaz_log(LOG_DEBUG, "PREPARE FOR RECONNECT");
+            c->reconnect_ok = 1;
+        }
+    }
+    
+    ZOOM_resultset_cache_reset(r);
+    task = ZOOM_connection_add_task (c, ZOOM_TASK_SORT);
+    task->u.sort.resultset = r;
+    task->u.sort.q = ZOOM_query_create();
+    ZOOM_query_sortby(task->u.sort.q, sort_spec);
+
+    ZOOM_resultset_addref (r);  
+
+    if (!c->async)
+    {
+       while (ZOOM_event (1, &c))
+           ;
+    }
+}
+
+ZOOM_API(void)
+    ZOOM_resultset_cache_reset(ZOOM_resultset r)
+{
+    ZOOM_record_cache rc;
+    
+    for (rc = r->record_cache; rc; rc = rc->next)
+    {
+       if (rc->rec.wrbuf_marc)
+           wrbuf_free (rc->rec.wrbuf_marc, 1);
+       if (rc->rec.wrbuf_iconv)
+           wrbuf_free (rc->rec.wrbuf_iconv, 1);
+       if (rc->rec.wrbuf_opac)
+           wrbuf_free (rc->rec.wrbuf_opac, 1);
+    }
+    r->record_cache = 0;
+}
+
+ZOOM_API(void)
 ZOOM_resultset_destroy(ZOOM_resultset r)
 {
     if (!r)
@@ -596,17 +675,8 @@ ZOOM_resultset_destroy(ZOOM_resultset r)
              r, r->refcount);
     if (r->refcount == 0)
     {
-        ZOOM_record_cache rc;
+       ZOOM_resultset_cache_reset(r);
 
-        for (rc = r->record_cache; rc; rc = rc->next)
-       {
-            if (rc->rec.wrbuf_marc)
-                wrbuf_free (rc->rec.wrbuf_marc, 1);
-            if (rc->rec.wrbuf_iconv)
-                wrbuf_free (rc->rec.wrbuf_iconv, 1);
-            if (rc->rec.wrbuf_opac)
-                wrbuf_free (rc->rec.wrbuf_opac, 1);
-       }
        if (r->connection)
        {
            /* remove ourselves from the resultsets in connection */
@@ -884,15 +954,19 @@ static zoom_ret send_APDU (ZOOM_connection c, Z_APDU *a)
 
 static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
 {
-    const char *impid, *impname, *impver;
     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));
     const char *auth_groupId = ZOOM_options_get (c->options, "group");
     const char *auth_userId = ZOOM_options_get (c->options, "user");
-    const char *auth_password = ZOOM_options_get (c->options, "pass");
-    
+    const char *auth_password = ZOOM_options_get (c->options, "password");
+    char *version;
+
+    /* support the pass for backwards compatibility */
+    if (!auth_password)
+       auth_password = ZOOM_options_get (c->options, "pass");
+       
     ODR_MASK_SET(ireq->options, Z_Options_search);
     ODR_MASK_SET(ireq->options, Z_Options_present);
     ODR_MASK_SET(ireq->options, Z_Options_scan);
@@ -903,40 +977,22 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
-    
-    impid = ZOOM_options_get (c->options, "implementationId");
-    ireq->implementationId =
-       (char *) odr_malloc (c->odr_out, 15 + (impid ? strlen(impid) : 0));
-    strcpy (ireq->implementationId, "");
-    if (impid)
-    {
-       strcat (ireq->implementationId, impid);
-       strcat (ireq->implementationId, "/");
-    }                                         
-    strcat (ireq->implementationId, "81"); /* Index's implementor ID */
-    
-    impname = ZOOM_options_get (c->options, "implementationName");
-    ireq->implementationName =
-       (char *) odr_malloc (c->odr_out, 15 + (impname ? strlen(impname) : 0));
-    strcpy (ireq->implementationName, "");
-    if (impname)
-    {
-       strcat (ireq->implementationName, impname);
-       strcat (ireq->implementationName, "/");
-    }                                         
-    strcat (ireq->implementationName, "ZOOM-C/YAZ");
-    
-    impver = ZOOM_options_get (c->options, "implementationVersion");
-    ireq->implementationVersion =
-       (char *) odr_malloc (c->odr_out, strlen("$Revision: 1.5 $") + 2 +
-                            (impver ? strlen(impver) : 0));
-    strcpy (ireq->implementationVersion, "");
-    if (impver)
-    {
-       strcat (ireq->implementationVersion, impver);
-       strcat (ireq->implementationVersion, "/");
-    }                                         
-    strcat (ireq->implementationVersion, "$Revision: 1.5 $");
+
+    /* Index Data's Z39.50 Implementor Id is 81 */
+    ireq->implementationId = odr_prepend(c->odr_out,
+       ZOOM_options_get(c->options, "implementationId"),
+       odr_prepend(c->odr_out, "81", ireq->implementationId));
+
+    ireq->implementationName = odr_prepend(c->odr_out,
+       ZOOM_options_get(c->options, "implementationName"),
+       odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName));
+
+    version = odr_strdup(c->odr_out, "$Revision: 1.26 $");
+    if (strlen(version) > 10)  /* check for unexpanded CVS strings */
+       version[strlen(version)-2] = '\0';
+    ireq->implementationVersion = odr_prepend(c->odr_out,
+       ZOOM_options_get(c->options, "implementationVersion"),
+       odr_prepend(c->odr_out, &version[11], ireq->implementationVersion));
 
     *ireq->maximumRecordSize =
        ZOOM_options_get_int (c->options, "maximumRecordSize", 1024*1024);
@@ -1017,7 +1073,7 @@ static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr)
 {
     char ctype[50];
     Z_SOAP_Handler h[2] = {
-        {"http://www.loc.gov/zing/srw/v1.0/", 0, (Z_SOAP_fun) yaz_srw_codec},
+        {"http://www.loc.gov/zing/srw/", 0, (Z_SOAP_fun) yaz_srw_codec},
         {0, 0, 0}
     };
     ODR o = odr_createmem(ODR_ENCODE);
@@ -1201,6 +1257,11 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
 
     /* 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;
+    }
 
     search_req->databaseNames =
        set_DatabaseNames (c, r->options, &search_req->num_databaseNames);
@@ -1372,6 +1433,62 @@ ZOOM_record_destroy (ZOOM_record rec)
     xfree (rec);
 }
 
+static const char *marc_iconv_return(ZOOM_record rec, int marc_type,
+                                    int *len,
+                                    const char *buf, int sz,
+                                    const char *record_charset)
+{
+    char to[40];
+    char from[40];
+    yaz_iconv_t cd = 0;
+    yaz_marc_t mt = yaz_marc_create();
+
+    *from = '\0';
+    strcpy(to, "UTF-8");
+    if (record_charset && *record_charset)
+    {
+       /* Use "from,to" or just "from" */
+       const char *cp =strchr(record_charset, ',');
+       int clen = strlen(record_charset);
+       if (cp && cp[1])
+       {
+           strncpy( to, cp+1, sizeof(to)-1);
+           to[sizeof(to)-1] = '\0';
+           clen = cp - record_charset;
+       }
+       if (clen > sizeof(from)-1)
+           clen = sizeof(from)-1;
+       
+       if (clen)
+           strncpy(from, record_charset, clen);
+       from[clen] = '\0';
+    }
+
+    if (*from && *to)
+    {
+       cd = yaz_iconv_open(to, from);
+       yaz_marc_iconv(mt, cd);
+    }
+
+    yaz_marc_xml(mt, marc_type);
+    if (!rec->wrbuf_marc)
+       rec->wrbuf_marc = wrbuf_alloc();
+    wrbuf_rewind (rec->wrbuf_marc);
+    if (yaz_marc_decode_wrbuf (mt, buf, sz, rec->wrbuf_marc) > 0)
+    {
+       yaz_marc_destroy(mt);
+       if (cd)
+           yaz_iconv_close(cd);
+       if (len)
+           *len = wrbuf_len(rec->wrbuf_marc);
+       return wrbuf_buf(rec->wrbuf_marc);
+    }
+    yaz_marc_destroy(mt);
+    if (cd)
+       yaz_iconv_close(cd);
+    return 0;
+}
+
 static const char *record_iconv_return(ZOOM_record rec, int *len,
                                       const char *buf, int sz,
                                       const char *record_charset)
@@ -1442,6 +1559,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
 {
     char type[40];
     char charset[40];
+    char xpath[512];
     const char *cp;
     int i;
     Z_NamePlusRecord *npr;
@@ -1464,24 +1582,34 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
     }
     type[i] = '\0';
     charset[0] = '\0';
-    if (type_spec[i] == ';')
+    while (type_spec[i] == ';')
     {
        i++;
        while (type_spec[i] == ' ')
            i++;
        if (!strncmp(type_spec+i, "charset=", 8))
        {
-           cp = type_spec+i+8;
-           for (i = 0; cp[i] && i < sizeof(charset)-1; i++)
+           int j = 0;
+           i = i + 8; /* skip charset= */
+           for (j = 0; type_spec[i]  && j < sizeof(charset)-1; i++, j++)
            {
-               if (cp[i] == ';' || cp[i] == ' ')
+               if (type_spec[i] == ';' || type_spec[i] == ' ')
                    break;
-               charset[i] = cp[i];
+               charset[j] = cp[i];
            }
-           charset[i] = '\0';
+           charset[j] = '\0';
        }
-    }  
-
+       else if (!strncmp(type_spec+i, "xpath=", 6))
+       {
+           int j = 0; 
+           i = i + 6;
+           for (j = 0; type_spec[i] && j < sizeof(xpath)-1; i++, j++)
+               xpath[j] = cp[i];
+           xpath[j] = '\0';
+       } 
+       while (type_spec[i] == ' ')
+           i++;
+    }
     if (!strcmp (type, "database"))
     {
        if (len)
@@ -1504,8 +1632,11 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
             *len = strlen(desc);
        return desc;
     }
-    else if (!strcmp (type, "render") && 
-             npr->which == Z_NamePlusRecord_databaseRecord)
+    if (npr->which != Z_NamePlusRecord_databaseRecord)
+       return 0;
+
+    /* from now on - we have a database record .. */
+    if (!strcmp (type, "render"))
     {
         Z_External *r = (Z_External *) npr->u.databaseRecord;
         oident *ent = oid_getentbyoid(r->direct_reference);
@@ -1524,7 +1655,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
                                       charset);
         else if (r->which == Z_External_octet)
         {
-            yaz_marc_t mt;
+           const char *ret_buf;
             switch (ent->value)
             {
             case VAL_SOIF:
@@ -1535,23 +1666,13 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
             case VAL_APPLICATION_XML:
                 break;
             default:
-                if (!rec->wrbuf_marc)
-                    rec->wrbuf_marc = wrbuf_alloc();
-
-                mt = yaz_marc_create();
-                wrbuf_rewind (rec->wrbuf_marc);
-                if (yaz_marc_decode_wrbuf (
-                        mt, (const char *) r->u.octet_aligned->buf,
-                        r->u.octet_aligned->len,
-                        rec->wrbuf_marc) > 0)
-                {
-                    yaz_marc_destroy(mt);
-                   return record_iconv_return(rec, len,
-                                              wrbuf_buf(rec->wrbuf_marc),
-                                              wrbuf_len(rec->wrbuf_marc),
-                                              charset);
-                }
-                yaz_marc_destroy(mt);
+               ret_buf = marc_iconv_return(
+                   rec, YAZ_MARC_LINE, len,
+                   (const char *) r->u.octet_aligned->buf,
+                   r->u.octet_aligned->len,
+                   charset);
+               if (ret_buf)
+                   return ret_buf;
             }
            return record_iconv_return(rec, len,
                                       (const char *) r->u.octet_aligned->buf,
@@ -1571,8 +1692,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
         }
        return 0;
     }
-    else if (npr->which == Z_NamePlusRecord_databaseRecord &&
-             (!strcmp (type, "xml") || !strcmp(type, "oai")))
+    else if (!strcmp (type, "xml") || !strcmp(type, "oai"))
     {
         Z_External *r = (Z_External *) npr->u.databaseRecord;
         oident *ent = oid_getentbyoid(r->direct_reference);
@@ -1593,7 +1713,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
                                       charset);
         else if (r->which == Z_External_octet)
         {
-            yaz_marc_t mt;
+           const char *ret_buf;
             int marc_decode_type = YAZ_MARC_MARCXML;
 
             if (!strcmp(type, "oai"))
@@ -1608,24 +1728,13 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
             case VAL_APPLICATION_XML:
                 break;
             default:
-                if (!rec->wrbuf_marc)
-                    rec->wrbuf_marc = wrbuf_alloc();
-                wrbuf_rewind (rec->wrbuf_marc);
-                mt = yaz_marc_create();
-
-                yaz_marc_xml(mt, YAZ_MARC_MARCXML);
-                if (yaz_marc_decode_wrbuf (
-                        mt, (const char *) r->u.octet_aligned->buf,
-                        r->u.octet_aligned->len,
-                        rec->wrbuf_marc) > 0)
-                {
-                    yaz_marc_destroy(mt);
-                   return record_iconv_return(rec, len,
-                                              wrbuf_buf(rec->wrbuf_marc),
-                                              wrbuf_len(rec->wrbuf_marc),
-                                              charset);
-                }
-                yaz_marc_destroy(mt);
+               ret_buf = marc_iconv_return(
+                   rec, marc_decode_type, len,
+                   (const char *) r->u.octet_aligned->buf,
+                   r->u.octet_aligned->len,
+                   charset);
+               if (ret_buf)
+                   return ret_buf;
             }
            return record_iconv_return(rec, len,
                                       (const char *) r->u.octet_aligned->buf,
@@ -1641,36 +1750,31 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
     }
     else if (!strcmp (type, "raw"))
     {
-       if (npr->which == Z_NamePlusRecord_databaseRecord)
+       Z_External *r = (Z_External *) npr->u.databaseRecord;
+       
+       if (r->which == Z_External_sutrs)
        {
-           Z_External *r = (Z_External *) npr->u.databaseRecord;
-           
-           if (r->which == Z_External_sutrs)
-           {
-               if (len) *len = r->u.sutrs->len;
-               return (const char *) r->u.sutrs->buf;
-           }
-           else if (r->which == Z_External_octet)
-           {
-               if (len) *len = r->u.octet_aligned->len;
-               return (const char *) r->u.octet_aligned->buf;
-           }
-           else /* grs-1, explain, OPAC, ... */
-           {
-               if (len) *len = -1;
-                return (const char *) npr->u.databaseRecord;
-           }
+           if (len) *len = r->u.sutrs->len;
+           return (const char *) r->u.sutrs->buf;
+       }
+       else if (r->which == Z_External_octet)
+       {
+           if (len) *len = r->u.octet_aligned->len;
+           return (const char *) r->u.octet_aligned->buf;
+       }
+       else /* grs-1, explain, OPAC, ... */
+       {
+           if (len) *len = -1;
+           return (const char *) npr->u.databaseRecord;
        }
        return 0;
     }
     else if (!strcmp (type, "ext"))
     {
-       if (npr->which == Z_NamePlusRecord_databaseRecord)
-            return (const char *) npr->u.databaseRecord;
-       return 0;
+       if (len) *len = -1;
+       return (const char *) npr->u.databaseRecord;
     }
-    else if (npr->which == Z_NamePlusRecord_databaseRecord &&
-             !strcmp (type, "opac"))
+    else if (!strcmp (type, "opac"))
             
     {
        Z_External *r = (Z_External *) npr->u.databaseRecord;
@@ -1899,20 +2003,11 @@ static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
     return 1;
 }
 
-static zoom_ret send_sort (ZOOM_connection c)
+static zoom_ret send_sort (ZOOM_connection c,
+                          ZOOM_resultset resultset)
 {
-    ZOOM_resultset  resultset;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
-       return zoom_complete;
-
-    resultset = c->tasks->u.search.resultset;
-
     if (c->error)
-    {
        resultset->r_sort_spec = 0;
-       return zoom_complete;
-    }
     if (resultset->r_sort_spec)
     {
        Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
@@ -2192,7 +2287,7 @@ ZOOM_scanset_display_term (ZOOM_scanset scan, size_t pos,
 
         if (t->displayTerm)
        {
-           term = (const char *) t->term->u.general->buf;
+           term = t->displayTerm;
            *len = strlen(term);
        }
        else if (t->term->which == Z_Term_general)
@@ -2363,6 +2458,175 @@ static Z_ItemOrder *encode_item_order(ZOOM_package 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, VAL_ADMINSERVICE);
+    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);
+       if (num_db > 0)
+           first_db = db[0];
+           
+       r->direct_reference =
+           yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
+                                  VAL_ADMINSERVICE);
+       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_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);
+    const char *action = ZOOM_options_get(p->options, "action");
+    const char *recordIdOpaque = ZOOM_options_get(p->options, "recordIdOpaque");
+    const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
+    const char *record_buf = ZOOM_options_get(p->options, "record");
+    const char *syntax_str = ZOOM_options_get(p->options, "syntax");
+    int syntax_oid = VAL_NONE;
+    int action_no = -1;
+    
+    if (syntax_str)
+       syntax_oid = oid_getvalbyname(syntax_str);
+    if (!record_buf)
+    {
+       record_buf = "void";
+       syntax_oid = VAL_SUTRS;
+    }
+    if (syntax_oid != VAL_NONE)
+       syntax_oid = VAL_TEXT_XML;
+    
+    if (num_db > 0)
+       first_db = db[0];
+    
+    if (!action)
+       action = "specialUpdate";
+    
+    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, VAL_DBUPDATE);
+    if (apdu)
+    {
+       Z_IUOriginPartToKeep *toKeep;
+       Z_IUSuppliedRecords *notToKeep;
+       Z_External *r = (Z_External *)
+           odr_malloc (p->odr_out, sizeof(*r));
+       
+       apdu->u.extendedServicesRequest->taskSpecificParameters = r;
+       
+       r->direct_reference =
+           yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
+                                  VAL_DBUPDATE);
+       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 = 0;
+       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_oct *)
+               odr_malloc (p->odr_out, sizeof(Odr_oct));
+           notToKeep->elements[0]->u.opaque->size =
+               notToKeep->elements[0]->u.opaque->len = strlen(recordIdOpaque);
+           notToKeep->elements[0]->u.opaque->buf =
+               odr_strdup(p->odr_out, recordIdOpaque);
+       }
+       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;
+       notToKeep->elements[0]->correlationInfo = 0;
+       notToKeep->elements[0]->record =
+           z_ext_record(p->odr_out, syntax_oid,
+                        record_buf, strlen(record_buf));
+    }
+    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)
 {
@@ -2376,11 +2640,10 @@ ZOOM_API(void)
     p->buf_out = 0;
     if (!strcmp(type, "itemorder"))
     {
-        Z_External *r;
         apdu = create_es_package (p, VAL_ITEMORDER);
         if (apdu)
         {
-            r = (Z_External *) odr_malloc (p->odr_out, sizeof(*r));
+            Z_External *r = (Z_External *) odr_malloc (p->odr_out, sizeof(*r));
             
             r->direct_reference =
                 yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
@@ -2393,6 +2656,20 @@ ZOOM_API(void)
             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
         }
     }
+    if (!strcmp(type, "create"))  /* create database */
+    {
+       apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
+                                   0, 0);
+    }  
+    if (!strcmp(type, "drop"))  /* drop database */
+    {
+       apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
+                                   0, 0);
+    }
+    if (!strcmp(type, "update")) /* update record(s) */
+    {
+       apdu = create_update_package(p);
+    }
     if (apdu)
     {
         if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
@@ -2508,6 +2785,11 @@ static int ZOOM_connection_exec_task (ZOOM_connection c)
             break;
         case ZOOM_TASK_PACKAGE:
             ret = send_package(c);
+           break;
+       case ZOOM_TASK_SORT:
+           c->tasks->u.sort.resultset->r_sort_spec = 
+               c->tasks->u.sort.q->sort_spec;
+           ret = send_sort(c, c->tasks->u.sort.resultset);
             break;
         }
     }
@@ -2528,7 +2810,10 @@ static int ZOOM_connection_exec_task (ZOOM_connection c)
 
 static zoom_ret send_sort_present (ZOOM_connection c)
 {
-    zoom_ret r = send_sort (c);
+    zoom_ret r = zoom_complete;
+
+    if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
+       r = send_sort (c, c->tasks->u.search.resultset);
     if (r == zoom_complete)
        r = send_present (c);
     return r;
@@ -2735,8 +3020,16 @@ static void handle_srw_response(ZOOM_connection c,
     }
     if (res->num_diagnostics > 0)
     {
-        set_dset_error(c, *res->diagnostics[0].code, "SRW",
-                       res->diagnostics[0].details, 0);
+       const char *uri = res->diagnostics[0].uri;
+       if (uri)
+       {
+           int code = 0;       
+           const char *cp;
+           if ((cp = strrchr(uri, '/')))
+               code = atoi(cp+1);
+           set_dset_error(c, code, uri,
+                          res->diagnostics[0].details, 0);
+       }
     }
     nmem = odr_extract_mem(c->odr_in);
     nmem_transfer(resultset->odr->mem, nmem);
@@ -2758,9 +3051,9 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
     if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
     {
         Z_SOAP *soap_package = 0;
-        ODR o = odr_createmem(ODR_DECODE);
+        ODR o = c->odr_in;
         Z_SOAP_Handler soap_handlers[2] = {
-            {"http://www.loc.gov/zing/srw/v1.0/", 0,
+            {"http://www.loc.gov/zing/srw/", 0,
              (Z_SOAP_fun) yaz_srw_codec},
             {0, 0, 0}
         };
@@ -2785,7 +3078,6 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         }
         else
             ret = -1;
-        odr_destroy(o);
     }
     if (ret)
     {
@@ -2819,7 +3111,6 @@ static int do_read (ZOOM_connection c)
     event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
     ZOOM_connection_put_event (c, event);
     
-
     r = cs_get (c->cs, &c->buf_in, &c->len_in);
     more = cs_more(c->cs);
     yaz_log (LOG_DEBUG, "do_read len=%d more=%d", r, more);
@@ -2853,7 +3144,13 @@ static int do_read (ZOOM_connection c)
 
        if (!z_GDU (c->odr_in, &gdu, 0, 0))
        {
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
+           int x;
+           int err = odr_geterrorx(c->odr_in, &x);
+           char msg[60];
+           const char *element = odr_getelement(c->odr_in);
+           sprintf (msg, "ODR code %d:%d element=%-20s",
+                    err, x, element ? element : "<unknown>");
+           set_ZOOM_error(c, ZOOM_ERROR_DECODE, msg);
            do_close (c);
        }
        else if (gdu->which == Z_GDU_Z3950)
@@ -2998,6 +3295,8 @@ ZOOM_diag_str (int error)
        return "Unsupported protocol";
     case ZOOM_ERROR_UNSUPPORTED_QUERY:
        return "Unsupported query type";
+    case ZOOM_ERROR_INVALID_QUERY:
+       return "Invalid query";
     default:
        return diagbib1_str (error);
     }
@@ -3016,7 +3315,7 @@ ZOOM_connection_error_x (ZOOM_connection c, const char **cp,
             *cp = z_HTTP_errmsg(c->error);
         else if (!strcmp(c->diagset, "Bib-1"))
             *cp = ZOOM_diag_str(error);
-        else if (!strcmp(c->diagset, "SRW"))
+        else if (!strcmp(c->diagset, "info:srw/diagnostic/1"))
             *cp = yaz_diag_srw_str(c->error);
         else
             *cp = "Unknown error and diagnostic set";