Handle surrogate diagnostics for SRU
[yaz-moved-to-github.git] / src / zoom-c.c
index 41766a2..d724047 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1995-2007, Index Data ApS
  * See the file LICENSE for details.
  *
- * $Id: zoom-c.c,v 1.137 2007-06-25 18:34:27 adam Exp $
+ * $Id: zoom-c.c,v 1.147 2007-09-09 05:54:45 adam Exp $
  */
 /**
  * \file zoom-c.c
@@ -28,6 +28,7 @@
 #include <yaz/ccl.h>
 #include <yaz/query-charset.h>
 #include <yaz/copy_types.h>
+#include <yaz/snprintf.h>
 
 static int log_api = 0;
 static int log_details = 0;
@@ -179,6 +180,23 @@ static void set_HTTP_error(ZOOM_connection c, int error,
 {
     set_dset_error(c, error, "HTTP", addinfo, addinfo2);
 }
+
+static int uri_to_code(const char *uri)
+{
+    int code = 0;       
+    const char *cp;
+    if ((cp = strrchr(uri, '/')))
+        code = atoi(cp+1);
+    return code;
+}
+
+static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
+{
+    const char *uri = d->uri;
+    if (uri)
+        set_dset_error(c, uri_to_code(uri), uri, d->details, 0);
+}
+
 #endif
 
 
@@ -380,6 +398,8 @@ ZOOM_API(ZOOM_connection)
 
     c->m_queue_front = 0;
     c->m_queue_back = 0;
+
+    c->sru_version = xstrdup("1.1");
     return c;
 }
 
@@ -526,6 +546,10 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "sru");
     c->sru_mode = get_sru_mode_from_string(val);
 
+    xfree(c->sru_version);
+    val = ZOOM_options_get(c->options, "sru_version");
+    c->sru_version = xstrdup(val ? val : "1.1");
+
     ZOOM_options_set(c->options, "host", c->host_port);
 
     xfree(c->cookie_out);
@@ -640,7 +664,7 @@ ZOOM_API(int)
     s->query_string = odr_strdup(s->odr, str);
 
     ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
-    ext->direct_reference = odr_getoidbystr(s->odr, "1.2.840.10003.16.2");
+    ext->direct_reference = odr_oiddup(s->odr, yaz_oid_userinfo_cql);
     ext->indirect_reference = 0;
     ext->descriptor = 0;
     ext->which = Z_External_CQL;
@@ -766,6 +790,7 @@ ZOOM_API(void)
     xfree(c->user);
     xfree(c->group);
     xfree(c->password);
+    xfree(c->sru_version);
     xfree(c);
 }
 
@@ -802,7 +827,6 @@ ZOOM_resultset ZOOM_resultset_create(void)
     r->next = 0;
     r->databaseNames = 0;
     r->num_databaseNames = 0;
-    r->rpn_iconv = 0;
     return r;
 }
 
@@ -834,13 +858,6 @@ ZOOM_API(ZOOM_resultset)
 
     r->options = ZOOM_options_create_with_parent(c->options);
     
-    {
-        const char *cp = ZOOM_options_get(r->options, "rpnCharset");
-        if (cp)
-            r->rpn_iconv = yaz_iconv_open(cp, "UTF-8");
-    }
-
-
     start = ZOOM_options_get_int(r->options, "start", 0);
     count = ZOOM_options_get_int(r->options, "count", 0);
     {
@@ -1013,8 +1030,6 @@ static void resultset_destroy(ZOOM_resultset r)
         }
         ZOOM_query_destroy(r->query);
         ZOOM_options_destroy(r->options);
-        if (r->rpn_iconv)
-            yaz_iconv_close(r->rpn_iconv);
         odr_destroy(r->odr);
         xfree(r->setname);
         xfree(r->schema);
@@ -1341,7 +1356,7 @@ static zoom_ret ZOOM_connection_send_init(ZOOM_connection c)
                     odr_prepend(c->odr_out, "ZOOM-C",
                                 ireq->implementationName));
     
-    version = odr_strdup(c->odr_out, "$Revision: 1.137 $");
+    version = odr_strdup(c->odr_out, "$Revision: 1.147 $");
     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
         version[strlen(version)-2] = '\0';
     ireq->implementationVersion = 
@@ -1431,6 +1446,15 @@ static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
 }
 #endif
 
+
+static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type) {
+    Z_SRW_PDU *sr = yaz_srw_get_pdu(c->odr_out, type, c->sru_version);
+    sr->username = c->user;
+    sr->password = c->password;
+    return sr;
+}
+
+
 #if YAZ_HAVE_XML2
 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
 {
@@ -1488,8 +1512,7 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
     }
     assert(resultset->query);
         
-    sr = yaz_srw_get(c->odr_out, Z_SRW_searchRetrieve_request);
-
+    sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
     if (resultset->query->z_query->which == Z_Query_type_104
         && resultset->query->z_query->u.type_104->which == Z_External_CQL)
     {
@@ -1569,12 +1592,23 @@ static zoom_ret ZOOM_connection_send_search(ZOOM_connection c)
         set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
         return zoom_complete;
     }
-    if (r->query->z_query->which == Z_Query_type_1 && r->rpn_iconv)
+    if (r->query->z_query->which == Z_Query_type_1 || 
+        r->query->z_query->which == Z_Query_type_101)
     {
-        search_req->query = yaz_copy_Z_Query(r->query->z_query, c->odr_out);
-        
-        yaz_query_charset_convert_rpnquery(search_req->query->u.type_1,
-                                           c->odr_out, r->rpn_iconv);
+        const char *cp = ZOOM_options_get(r->options, "rpnCharset");
+        if (cp)
+        {
+            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
+            if (cd)
+            {
+                search_req->query = yaz_copy_Z_Query(search_req->query,
+                                                     c->odr_out);
+                
+                yaz_query_charset_convert_rpnquery(search_req->query->u.type_1,
+                                                   c->odr_out, cd);
+                yaz_iconv_close(cd);
+            }
+        }
     }
     search_req->databaseNames = r->databaseNames;
     search_req->num_databaseNames = r->num_databaseNames;
@@ -1870,6 +1904,7 @@ static const char *record_iconv_return(ZOOM_record rec, int *len,
     return buf;
 }
 
+
 ZOOM_API(int)
     ZOOM_record_error(ZOOM_record rec, const char **cp,
                       const char **addinfo, const char **diagset)
@@ -1878,7 +1913,18 @@ ZOOM_API(int)
     
     if (!rec)
         return 0;
+
     npr = rec->npr;
+    if (rec->diag)
+    {
+        if (cp)
+            *cp = rec->diag[0].message;
+        if (addinfo)
+            *addinfo = rec->diag[0].details;
+        if (diagset)
+            *diagset = rec->diagset;
+        return uri_to_code(rec->diag[0].uri);
+    }
     if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
     {
         Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
@@ -1981,6 +2027,12 @@ ZOOM_API(const char *)
             *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
         return npr->databaseName;
     }
+    else if (!strcmp(type, "schema"))
+    {
+        if (len)
+            *len = rec->schema ? strlen(rec->schema) : 0;
+        return rec->schema;
+    }
     else if (!strcmp(type, "syntax"))
     {
         const char *desc = 0;   
@@ -2154,7 +2206,9 @@ static size_t record_hash(int pos)
 
 static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
                              int pos,
-                             const char *syntax, const char *elementSetName)
+                             const char *syntax, const char *elementSetName,
+                             const char *schema,
+                             Z_SRW_diagnostic *diag, int num_diag)
 {
     ZOOM_record_cache rc;
     
@@ -2178,11 +2232,22 @@ static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr,
         }
     }
     rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
-    rc->rec.npr = npr; 
+    rc->rec.npr = npr;
+    rc->rec.schema = schema ? odr_strdup(r->odr, schema) : 0;
     rc->rec.odr = 0;
     rc->rec.wrbuf_marc = 0;
     rc->rec.wrbuf_iconv = 0;
     rc->rec.wrbuf_opac = 0;
+    rc->rec.diag = diag;
+    rc->rec.diagset = 0;
+    if (diag && diag[0].uri)
+    {
+        char *cp;
+        rc->rec.diagset = odr_strdup(r->odr, diag[0].uri);
+        if ((cp = strrchr(rc->rec.diagset, '/')))
+            *cp = '\0';
+    }
+
     if (elementSetName)
         rc->elementSetName = odr_strdup(r->odr, elementSetName);
     else
@@ -2198,6 +2263,7 @@ static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr,
     else
         rc->schema = 0;
 
+
     rc->pos = pos;
     rc->next = r->record_hash[record_hash(pos)];
     r->record_hash[record_hash(pos)] = rc;
@@ -2277,7 +2343,8 @@ static void handle_records(ZOOM_connection c, Z_Records *sr,
             for (i = 0; i<p->num_records; i++)
             {
                 record_cache_add(resultset, p->records[i], i + *start,
-                                 syntax, elementSetName);
+                                 syntax, elementSetName,
+                                 elementSetName, 0, 0);
             }
             *count -= i;
             if (*count < 0)
@@ -2296,7 +2363,7 @@ static void handle_records(ZOOM_connection c, Z_Records *sr,
                 Z_NamePlusRecord *myrec = 
                     zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
                 record_cache_add(resultset, myrec, *start,
-                                 syntax, elementSetName);
+                                 syntax, elementSetName, 0, 0, 0);
             }
         }
         else if (present_phase)
@@ -2304,7 +2371,8 @@ static void handle_records(ZOOM_connection c, Z_Records *sr,
             /* present response and we didn't get any records! */
             Z_NamePlusRecord *myrec = 
                 zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
-            record_cache_add(resultset, myrec, *start, syntax, elementSetName);
+            record_cache_add(resultset, myrec, *start, syntax, elementSetName,
+                             0, 0, 0);
         }
     }
 }
@@ -2467,6 +2535,7 @@ static int scan_response(ZOOM_connection c, Z_ScanResponse *res)
     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);
@@ -2667,68 +2736,25 @@ ZOOM_API(ZOOM_scanset)
 ZOOM_API(ZOOM_scanset)
     ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
 {
-    char *start;
-    char *freeme = 0;
     ZOOM_scanset scan = 0;
 
-    /*
-     * We need to check the query-type, so we can recognise CQL and
-     * CCL and compile them into a form that we can use here.  The
-     * ZOOM_query structure has no explicit `type' member, but
-     * inspection of the ZOOM_query_prefix() and ZOOM_query_cql()
-     * functions shows how the structure is set up in each case.
-     */
     if (!q->z_query)
         return 0;
-    else if (q->z_query->which == Z_Query_type_1) 
-    {
-        yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p PQF '%s'",
-                c, q, q->query_string);
-        start = q->query_string;
-    } 
-    else if (q->z_query->which == Z_Query_type_104)
-    {
-        yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'",
-                c, q, q->query_string);
-        start = freeme = cql2pqf(c, q->query_string);
-        if (start == 0)
-            return 0;
-    } 
-    else
-    {
-        yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'",
-                c, q, q->query_string);
-        abort();
-    }
-    
     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->termListAndStartPoint =
-        p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, start);
-    xfree(freeme);
-
-    {
-        const char *cp = ZOOM_options_get(scan->options, "rpnCharset");
-        if (cp)
-        {
-            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
-            if (cd)
-            {
-                yaz_query_charset_convert_apt(scan->termListAndStartPoint,
-                                              scan->odr, cd);
-            }
-        }
-    }
-        
+    scan->srw_scan_response = 0;
 
+    scan->query = q;
+    (q->refcount)++;
     scan->databaseNames = set_DatabaseNames(c, c->options,
                                             &scan->num_databaseNames,
                                             scan->odr);
-    if (scan->termListAndStartPoint != 0)
+
+    if (1)
     {
         ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
         task->u.scan.scan = scan;
@@ -2751,6 +2777,8 @@ ZOOM_API(void)
     (scan->refcount)--;
     if (scan->refcount == 0)
     {
+        ZOOM_query_destroy(scan->query);
+
         odr_destroy(scan->odr);
         
         ZOOM_options_destroy(scan->options);
@@ -2776,7 +2804,7 @@ static zoom_ret send_package(ZOOM_connection c)
     return do_write(c);
 }
 
-static zoom_ret send_scan(ZOOM_connection c)
+static zoom_ret ZOOM_connection_send_scan(ZOOM_connection c)
 {
     ZOOM_scanset scan;
     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
@@ -2788,8 +2816,44 @@ static zoom_ret send_scan(ZOOM_connection c)
     assert (c->tasks->which == ZOOM_TASK_SCAN);
     scan = c->tasks->u.scan.scan;
 
-    req->termListAndStartPoint = scan->termListAndStartPoint;
-    req->attributeSet = scan->attributeSet;
+    /* 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", 10);
@@ -2808,69 +2872,157 @@ static zoom_ret send_scan(ZOOM_connection c)
     return send_APDU(c, apdu);
 }
 
+#if YAZ_HAVE_XML2
+static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
+{
+    ZOOM_scanset scan;
+    Z_SRW_PDU *sr = 0;
+    const char *option_val = 0;
+
+    if (!c->tasks)
+        return zoom_complete;
+    assert (c->tasks->which == ZOOM_TASK_SCAN);
+    scan = c->tasks->u.scan.scan;
+        
+    sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
+
+    /* SRU scan can only carry CQL and PQF */
+    if (scan->query->z_query->which == Z_Query_type_104)
+    {
+        sr->u.scan_request->query_type = Z_SRW_query_type_cql;
+        sr->u.scan_request->scanClause.cql = scan->query->query_string;
+    }
+    else if (scan->query->z_query->which == Z_Query_type_1
+             || scan->query->z_query->which == Z_Query_type_101)
+    {
+        sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
+        sr->u.scan_request->scanClause.pqf = scan->query->query_string;
+    }
+    else
+    {
+        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        return zoom_complete;
+    }
+
+    sr->u.scan_request->maximumTerms = odr_intdup(
+        c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
+    
+    sr->u.scan_request->responsePosition = odr_intdup(
+        c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
+    
+    option_val = ZOOM_options_get(scan->options, "extraArgs");
+    if (option_val)
+        sr->extra_args = odr_strdup(c->odr_out, option_val);
+    return send_srw(c, sr);
+}
+#else
+static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
+{
+    return zoom_complete;
+}
+#endif
+
+
 ZOOM_API(size_t)
     ZOOM_scanset_size(ZOOM_scanset scan)
 {
-    if (!scan || !scan->scan_response || !scan->scan_response->entries)
+    if (!scan)
         return 0;
-    return scan->scan_response->entries->num_entries;
+
+    if (scan->scan_response && scan->scan_response->entries)
+        return scan->scan_response->entries->num_entries;
+    else if (scan->srw_scan_response)
+        return scan->srw_scan_response->num_terms;
+    return 0;
 }
 
-ZOOM_API(const char *)
-    ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
-                      int *occ, int *len)
+static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
+                                int *occ,
+                                const char **value_term, size_t *value_len,
+                                const char **disp_term, size_t *disp_len)
 {
-    const char *term = 0;
     size_t noent = ZOOM_scanset_size(scan);
-    Z_ScanResponse *res = scan->scan_response;
     
-    *len = 0;
+    *value_term = 0;
+    *value_len = 0;
+
+    *disp_term = 0;
+    *disp_len = 0;
+
     *occ = 0;
-    if (pos >= noent)
-        return 0;
-    if (res->entries->entries[pos]->which == Z_Entry_termInfo)
+    if (pos >= noent || pos < 0)
+        return;
+    if (scan->scan_response)
     {
-        Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
-        
-        if (t->term->which == Z_Term_general)
+        Z_ScanResponse *res = scan->scan_response;
+        if (res->entries->entries[pos]->which == Z_Entry_termInfo)
         {
-            term = (const char *) t->term->u.general->buf;
-            *len = t->term->u.general->len;
+            Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
+            
+            *value_term = (const char *) t->term->u.general->buf;
+            *value_len = t->term->u.general->len;
+            if (t->displayTerm)
+            {
+                *disp_term = t->displayTerm;
+                *disp_len = strlen(*disp_term);
+            }
+            else if (t->term->which == Z_Term_general)
+            {
+                *disp_term = (const char *) t->term->u.general->buf;
+                *disp_len = t->term->u.general->len;
+            }
+            *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
         }
-        *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
     }
-    return term;
+    if (scan->srw_scan_response)
+    {
+        Z_SRW_scanResponse *res = scan->srw_scan_response;
+        Z_SRW_scanTerm *t = res->terms + pos;
+        if (t)
+        {
+            *value_term = t->value;
+            *value_len = strlen(*value_term);
+
+            if (t->displayTerm)
+                *disp_term = t->displayTerm;
+            else
+                *disp_term = t->value;
+            *disp_len = strlen(*disp_term);
+            *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
+        }
+    }
+}
+
+ZOOM_API(const char *)
+    ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
+                      int *occ, int *len)
+{
+    const char *value_term = 0;
+    size_t value_len = 0;
+    const char *disp_term = 0;
+    size_t disp_len = 0;
+
+    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
+                        &disp_term, &disp_len);
+    
+    *len = value_len;
+    return value_term;
 }
 
 ZOOM_API(const char *)
     ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
                               int *occ, int *len)
 {
-    const char *term = 0;
-    size_t noent = ZOOM_scanset_size(scan);
-    Z_ScanResponse *res = scan->scan_response;
-    
-    *len = 0;
-    *occ = 0;
-    if (pos >= noent)
-        return 0;
-    if (res->entries->entries[pos]->which == Z_Entry_termInfo)
-    {
-        Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
+    const char *value_term = 0;
+    size_t value_len = 0;
+    const char *disp_term = 0;
+    size_t disp_len = 0;
 
-        if (t->displayTerm)
-        {
-            term = t->displayTerm;
-            *len = strlen(term);
-        }
-        else if (t->term->which == Z_Term_general)
-        {
-            term = (const char *) t->term->u.general->buf;
-            *len = t->term->u.general->len;
-        }
-        *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
-    }
-    return term;
+    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
+                        &disp_term, &disp_len);
+    
+    *len = disp_len;
+    return disp_term;
 }
 
 ZOOM_API(const char *)
@@ -3124,6 +3276,7 @@ static Z_APDU *create_update_package(ZOOM_package p)
     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");
+    const char *version = ZOOM_options_get(p->options, "updateVersion");
 
     const char *correlationInfo_note =
         ZOOM_options_get(p->options, "correlationInfo.note");
@@ -3131,7 +3284,10 @@ static Z_APDU *create_update_package(ZOOM_package p)
         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)
@@ -3152,8 +3308,27 @@ static Z_APDU *create_update_package(ZOOM_package p)
     if (num_db > 0)
         first_db = db[0];
     
-    if (!action)
-        action = "specialUpdate";
+    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;
@@ -3168,7 +3343,7 @@ static Z_APDU *create_update_package(ZOOM_package p)
     else
         return 0;
 
-    apdu = create_es_package(p, yaz_oid_extserv_database_update);
+    apdu = create_es_package(p, package_oid);
     if (apdu)
     {
         Z_IUOriginPartToKeep *toKeep;
@@ -3179,10 +3354,8 @@ static Z_APDU *create_update_package(ZOOM_package p)
             ZOOM_options_get(p->options, "elementSetName");
         
         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-
         
-        r->direct_reference = odr_oiddup(p->odr_out, 
-                                         yaz_oid_extserv_database_update);
+        r->direct_reference = odr_oiddup(p->odr_out, package_oid);
         r->descriptor = 0;
         r->which = Z_External_update;
         r->indirect_reference = 0;
@@ -3419,7 +3592,10 @@ static int ZOOM_connection_exec_task(ZOOM_connection c)
             ret = do_connect(c);
             break;
         case ZOOM_TASK_SCAN:
-            ret = send_scan(c);
+            if (c->proto == PROTO_HTTP)
+                ret = ZOOM_connection_srw_send_scan(c);
+            else
+                ret = ZOOM_connection_send_scan(c);
             break;
         case ZOOM_TASK_PACKAGE:
             ret = send_package(c);
@@ -3697,6 +3873,9 @@ static void handle_srw_response(ZOOM_connection c,
 
     resultset->size = 0;
 
+    if (res->resultSetId)
+        ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
+
     yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
     
     if (res->numberOfRecords)
@@ -3705,7 +3884,10 @@ static void handle_srw_response(ZOOM_connection c,
     for (i = 0; i<res->num_records; i++)
     {
         int pos;
-
+        Z_SRW_record *sru_rec;
+        Z_SRW_diagnostic *diag = 0;
+        int num_diag;
         Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
             odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
 
@@ -3715,6 +3897,8 @@ static void handle_srw_response(ZOOM_connection c,
         else
             pos = *start + i;
         
+        sru_rec = &res->records[i];
+
         npr->databaseName = 0;
         npr->which = Z_NamePlusRecord_databaseRecord;
         npr->u.databaseRecord = (Z_External *)
@@ -3727,25 +3911,25 @@ static void handle_srw_response(ZOOM_connection c,
         npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
             odr_malloc(c->odr_in, sizeof(Odr_oct));
         npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
-            res->records[i].recordData_buf;
+            sru_rec->recordData_buf;
         npr->u.databaseRecord->u.octet_aligned->len = 
             npr->u.databaseRecord->u.octet_aligned->size = 
-            res->records[i].recordData_len;
-        record_cache_add(resultset, npr, pos, syntax, elementSetName);
-    }
-    if (res->num_diagnostics > 0)
-    {
-        const char *uri = res->diagnostics[0].uri;
-        if (uri)
+            sru_rec->recordData_len;
+        
+        if (sru_rec->recordSchema 
+            && !strcmp(sru_rec->recordSchema,
+                       "info:srw/schema/1/diagnostics-v1.1"))
         {
-            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);
+            sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
+                                             sru_rec->recordData_len,
+                                             &diag, &num_diag,
+                                             resultset->odr);
         }
+        record_cache_add(resultset, npr, pos, syntax, elementSetName,
+                         sru_rec->recordSchema, diag, num_diag);
     }
+    if (res->num_diagnostics > 0)
+        set_SRU_error(c, &res->diagnostics[0]);
     nmem = odr_extract_mem(c->odr_in);
     nmem_transfer(odr_getmem(resultset->odr), nmem);
     nmem_destroy(nmem);
@@ -3753,6 +3937,29 @@ static void handle_srw_response(ZOOM_connection c,
 #endif
 
 #if YAZ_HAVE_XML2
+static void handle_srw_scan_response(ZOOM_connection c,
+                                     Z_SRW_scanResponse *res)
+{
+    NMEM nmem = odr_extract_mem(c->odr_in);
+    ZOOM_scanset scan;
+
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
+        return;
+    scan = c->tasks->u.scan.scan;
+
+    if (res->num_diagnostics > 0)
+        set_SRU_error(c, &res->diagnostics[0]);
+
+    scan->scan_response = 0;
+    scan->srw_scan_response = res;
+    nmem_transfer(odr_getmem(scan->odr), nmem);
+
+    ZOOM_options_set_int(scan->options, "number", res->num_terms);
+    nmem_destroy(nmem);
+}
+#endif
+
+#if YAZ_HAVE_XML2
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 {
     int ret = -1;
@@ -3779,8 +3986,12 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
             soap_package->u.generic->no == 0)
         {
             Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
+
+            ZOOM_options_set(c->options, "sru_version", sr->srw_version);
             if (sr->which == Z_SRW_searchRetrieve_response)
                 handle_srw_response(c, sr->u.response);
+            else if (sr->which == Z_SRW_scan_response)
+                handle_srw_scan_response(c, sr->u.scan_response);
             else
                 ret = -1;
         }
@@ -3853,11 +4064,19 @@ static int do_read(ZOOM_connection c)
         {
             int x;
             int err = odr_geterrorx(c->odr_in, &x);
-            char msg[60];
+            char msg[100];
             const char *element = odr_getelement(c->odr_in);
-            sprintf(msg, "ODR code %d:%d element=%-20s",
-                    err, x, element ? element : "<unknown>");
+            yaz_snprintf(msg, sizeof(msg),
+                    "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)
+            {
+                FILE *ber_file = yaz_log_file();
+                if (ber_file)
+                    odr_dumpBER(ber_file, c->buf_in, r);
+            }
             do_close(c);
         }
         else if (gdu->which == Z_GDU_Z3950)