Remove call to gnutls_global_init. tcpip_init already does it
[yaz-moved-to-github.git] / client / client.c
index c9351d2..596b76a 100644 (file)
@@ -1,16 +1,18 @@
 /* This file is part of the YAZ toolkit.
- * Copyright (C) 1995-2010 Index Data
+ * Copyright (C) Index Data
  * See the file LICENSE for details.
  */
 /** \file client.c
  *  \brief yaz-client program
  */
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <time.h>
-#include <ctype.h>
 #ifndef WIN32
 #include <signal.h>
 #endif
@@ -46,7 +48,6 @@
 #include <yaz/comstack.h>
 
 #include <yaz/oid_db.h>
-#define NO_OID 1
 #include <yaz/proto.h>
 #include <yaz/marcdisp.h>
 #include <yaz/diagbib1.h>
@@ -63,6 +64,7 @@
 #include <yaz/cql.h>
 #include <yaz/log.h>
 #include <yaz/facet.h>
+#include <yaz/cookie.h>
 
 #if HAVE_READLINE_READLINE_H
 #include <readline/readline.h>
@@ -82,6 +84,7 @@ static file_history_t file_history = 0;
 
 static char sru_method[10] = "soap";
 static char sru_version[10] = "1.2";
+static char sru_recordPacking[10] = "";
 static char *codeset = 0;               /* character set for output */
 static int hex_dump = 0;
 static char *dump_file_prefix = 0;
@@ -101,11 +104,12 @@ char *databaseNames[128];
 int num_databaseNames = 0;
 static Z_External *record_last = 0;
 static int setnumber = -1;              /* current result set number */
-static int smallSetUpperBound = 0;
-static int largeSetLowerBound = 1;
-static int mediumSetPresentNumber = 0;
+static Odr_int smallSetUpperBound = 0;
+static Odr_int largeSetLowerBound = 1;
+static Odr_int mediumSetPresentNumber = 0;
 static Z_ElementSetNames *elementSetNames = 0;
 static Z_FacetList *facet_list = 0;
+static ODR facet_odr = 0;
 static Odr_int setno = 1;                   /* current set offset */
 static enum oid_proto protocol = PROTO_Z3950;      /* current app protocol */
 #define RECORDSYNTAX_MAX 20
@@ -123,7 +127,7 @@ static char ccl_fields[512] = "default.bib";
 static char cql_fields[512] = "/usr/local/share/yaz/etc/pqf.properties";
 static char *esPackageName = 0;
 static char *yazProxy = 0;
-static int kilobytes = 1024;
+static int kilobytes = 64 * 1024;
 static char *negotiationCharset = 0;
 static int  negotiationCharsetRecords = 1;
 static int  negotiationCharsetVersion = 3;
@@ -140,10 +144,13 @@ static int auto_wait = 1;
 static Odr_bitmask z3950_options;
 static int z3950_version = 3;
 static int scan_stepSize = 0;
-static int scan_position = 1;
+static char scan_position[64];
 static int scan_size = 20;
-static char cur_host[200];
+static WRBUF cur_host = 0;
 static Odr_int last_hit_count = 0;
+static int pretty_xml = 0;
+static Odr_int sru_maximumRecords = 0;
+static yaz_cookies_t yaz_cookies = 0;
 
 typedef enum {
     QueryType_Prefix,
@@ -248,9 +255,12 @@ static void add_otherInfos(Z_APDU *a)
     Z_OtherInformation **oi;
     int i;
 
-    yaz_oi_APDU(a, &oi);
-    if (facet_list)
+    if (facet_list && a->which == Z_APDU_searchRequest)
+    {
+        oi = &a->u.searchRequest->additionalSearchInfo;
         yaz_oi_set_facetlist(oi, out, facet_list);
+    }
+    yaz_oi_APDU(a, &oi);
     for (i = 0; i < maxOtherInfosSupported; ++i)
     {
         if (oid_oidlen(extraOtherInfos[i].oid) > 0)
@@ -280,7 +290,6 @@ int send_apdu(Z_APDU *a)
     buf = odr_getbuf(out, &len, 0);
     if (ber_file)
         odr_dumpBER(ber_file, buf, len);
-    /* printf ("sending APDU of size %d\n", len); */
     do_hex_dump(buf, len);
     if (cs_put(conn, buf, len) < 0)
     {
@@ -314,14 +323,9 @@ static void print_refid(Z_ReferenceId *id)
 
 static Z_ReferenceId *set_refid(ODR out)
 {
-    Z_ReferenceId *id;
     if (!refid)
         return 0;
-    id = (Z_ReferenceId *) odr_malloc(out, sizeof(*id));
-    id->size = id->len = strlen(refid);
-    id->buf = (unsigned char *) odr_malloc(out, id->len);
-    memcpy(id->buf, refid, id->len);
-    return id;
+    return odr_create_Odr_oct(out, refid, strlen(refid));
 }
 
 /* INIT SERVICE ------------------------------- */
@@ -699,10 +703,7 @@ static int session_connect_base(const char *arg, const char **basep)
     strncpy(type_and_host, arg, sizeof(type_and_host)-1);
     type_and_host[sizeof(type_and_host)-1] = '\0';
 
-    if (yazProxy)
-        conn = cs_create_host(yazProxy, 1, &add);
-    else
-        conn = cs_create_host(arg, 1, &add);
+    conn = cs_create_host_proxy(arg, 1, &add, yazProxy);
     if (!conn)
     {
         printf("Could not resolve address %s\n", arg);
@@ -738,12 +739,15 @@ static int session_connect_base(const char *arg, const char **basep)
     return 0;
 }
 
-static int session_connect(const char *arg)
+static int session_connect(void)
 {
     int r;
     const char *basep = 0;
 
-    r = session_connect_base(arg, &basep);
+    yaz_cookies_destroy(yaz_cookies);
+    yaz_cookies = yaz_cookies_create();
+
+    r = session_connect_base(wrbuf_cstr(cur_host), &basep);
     if (basep && *basep)
         set_base(basep);
     else if (protocol == PROTO_Z3950)
@@ -756,19 +760,16 @@ static int cmd_open(const char *arg)
     int r;
     if (arg)
     {
-        strncpy(cur_host, arg, sizeof(cur_host)-1);
-        cur_host[sizeof(cur_host)-1] = 0;
+        wrbuf_rewind(cur_host);
+        if (!strstr(arg, "://") && strcmp(sru_method, "soap"))
+            wrbuf_puts(cur_host, "http://");
+        wrbuf_puts(cur_host, arg);
     }
-    /* TODO Make facet definition survive the open command without crashing */
-    /* TODO Fix deallocation */
-    facet_list = 0;
-
     set_base("");
-    r = session_connect(cur_host);
+    r = session_connect();
     if (conn && conn->protocol == PROTO_HTTP)
         queryType = QueryType_CQL;
 
-
     return r;
 }
 
@@ -846,6 +847,75 @@ static void print_record(const char *buf, size_t len)
         printf("\n");
 }
 
+static void print_mab_record(const char *buf, size_t len)
+{
+    size_t i;
+    size_t last_linebreak = 0;
+    size_t last_subfield  = 0;
+    for (i = 0; i < len; i++)
+    {
+        // line break after header
+        if ( i == 24 )
+        {
+            printf("\n");
+            last_linebreak = i - 1;
+        }
+
+        // space between field and content
+        if ( i > 24 && i - last_linebreak == 5 )
+            printf(" ");
+
+        // space after subfield
+        if ( last_subfield != 0 && i - last_subfield == 2 )
+            printf(" ");
+
+        if ((buf[i] <= 126 && buf[i] >= 32) || strchr("\n\r\t\f", buf[i]))
+            printf("%c", buf[i]);
+        else if ( buf[i] == 29 ) // record separator
+            printf("\n");
+        else if ( buf[i] == 30 ) // field separator
+        {
+            printf("\n");
+            last_linebreak = i;
+        }
+        else if ( buf[i] == 31 ) // subfield
+        {
+            // space before subfields; except first one
+            if ( i > 24 && i - last_linebreak > 5 )
+                printf(" ");
+            printf("$");
+            last_subfield = i;
+        }
+        else
+            printf("\\X%02X", ((const unsigned char *)buf)[i]);
+    }
+}
+
+static void print_xml_record(const char *buf, size_t len)
+{
+    int has_printed = 0;
+#if YAZ_HAVE_XML2
+    if (pretty_xml)
+    {
+        xmlDocPtr doc;
+        xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
+        doc = xmlParseMemory(buf, len);
+        if (doc)
+        {
+            xmlChar *xml_mem;
+            int xml_size;
+            xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
+            fwrite(xml_mem, 1, xml_size, stdout);
+            xmlFree(xml_mem);
+            xmlFreeDoc(doc);
+            has_printed = 1;
+        }
+    }
+#endif
+    if (!has_printed)
+        fwrite(buf, 1, len, stdout);
+}
+
 static void display_record(Z_External *r)
 {
     const Odr_oid *oid = r->direct_reference;
@@ -902,12 +972,16 @@ static void display_record(Z_External *r)
         const char *octet_buf = (const char*)r->u.octet_aligned->buf;
         size_t octet_len = r->u.octet_aligned->len;
         if (!oid_oidcmp(oid, yaz_oid_recsyn_xml)
-            || !oid_oidcmp(oid, yaz_oid_recsyn_xml)
+            || !oid_oidcmp(oid, yaz_oid_recsyn_application_xml)
             || !oid_oidcmp(oid, yaz_oid_recsyn_html))
         {
-            fwrite(octet_buf, 1, octet_len, stdout);
+            print_xml_record(octet_buf, octet_len);
         }
-        else if (yaz_oid_is_iso2709(oid))
+        else if (!oid_oidcmp(oid, yaz_oid_recsyn_mab))
+        {
+            print_mab_record(octet_buf, octet_len);
+        }
+        else
         {
             const char *result;
             size_t rlen;
@@ -953,17 +1027,14 @@ static void display_record(Z_External *r)
             }
             else
             {
-                printf("bad MARC. Dumping as it is:\n");
+                if (yaz_oid_is_iso2709(oid))
+                     printf("bad MARC. Dumping as it is:\n");
                 print_record(octet_buf, octet_len);
             }
             yaz_marc_destroy(mt);
             if (cd)
                 yaz_iconv_close(cd);
         }
-        else
-        {
-            print_record(octet_buf, octet_len);
-        }
         marc_file_write(octet_buf, r->u.octet_aligned->len);
     }
     else if (oid && !oid_oidcmp(oid, yaz_oid_recsyn_sutrs))
@@ -1293,46 +1364,20 @@ static int send_srw_host_path(Z_SRW_PDU *sr, const char *host_port,
 static int send_srw(Z_SRW_PDU *sr)
 {
     char *path = yaz_encode_sru_dbpath_odr(out, databaseNames[0]);
-    return send_srw_host_path(sr, cur_host, path);
+    return send_srw_host_path(sr, wrbuf_cstr(cur_host), path);
 }
 
-static int send_SRW_redirect(const char *uri, Z_HTTP_Response *cookie_hres)
+static int send_SRW_redirect(const char *uri)
 {
     const char *username = 0;
     const char *password = 0;
-    struct Z_HTTP_Header *h;
-    char *combined_cookies = 0;
-    int combined_cookies_len = 0;
     Z_GDU *gdu = get_HTTP_Request_url(out, uri);
 
     gdu->u.HTTP_Request->method = odr_strdup(out, "GET");
     z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers, "Accept",
                       "text/xml");
 
-    for (h = cookie_hres->headers; h; h = h->next)
-    {
-        if (!strcmp(h->name, "Set-Cookie"))
-        {
-            char *cp;
-
-            if (!(cp = strchr(h->value, ';')))
-                cp = h->value + strlen(h->value);
-            if (cp - h->value >= 1)
-            {
-                combined_cookies = xrealloc(combined_cookies, combined_cookies_len + cp - h->value + 3);
-                memcpy(combined_cookies+combined_cookies_len, h->value, cp - h->value);
-                combined_cookies[combined_cookies_len + cp - h->value] = '\0';
-                strcat(combined_cookies,"; ");
-                combined_cookies_len = strlen(combined_cookies);
-            }
-        }
-    }
-    if (combined_cookies_len)
-    {
-        z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers, "Cookie", combined_cookies);
-        xfree(combined_cookies);
-    }
-
+    yaz_cookies_request(yaz_cookies, out, gdu->u.HTTP_Request);
     if (auth)
     {
         if (auth->which == Z_IdAuthentication_open)
@@ -1388,7 +1433,7 @@ static char *encode_SRW_term(ODR o, const char *q)
 }
 
 
-static int send_SRW_scanRequest(const char *arg, int pos, int num)
+static int send_SRW_scanRequest(const char *arg, Odr_int *pos, int num)
 {
     Z_SRW_PDU *sr = 0;
 
@@ -1398,18 +1443,18 @@ static int send_SRW_scanRequest(const char *arg, int pos, int num)
     switch (queryType)
     {
     case QueryType_CQL:
-        sr->u.scan_request->query_type = Z_SRW_query_type_cql;
-        sr->u.scan_request->scanClause.cql = encode_SRW_term(out, arg);
+        sr->u.scan_request->queryType = "cql";
+        sr->u.scan_request->scanClause = encode_SRW_term(out, arg);
         break;
     case QueryType_Prefix:
-        sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
-        sr->u.scan_request->scanClause.pqf = encode_SRW_term(out, arg);
+        sr->u.scan_request->queryType = "pqf";
+        sr->u.scan_request->scanClause = encode_SRW_term(out, arg);
         break;
     default:
         printf("Only CQL and PQF supported in SRW\n");
         return 0;
     }
-    sr->u.scan_request->responsePosition = odr_intdup(out, pos);
+    sr->u.scan_request->responsePosition = pos;
     sr->u.scan_request->maximumTerms = odr_intdup(out, num);
     return send_srw(sr);
 }
@@ -1437,27 +1482,29 @@ static int send_SRW_searchRequest(const char *arg)
     switch (queryType)
     {
     case QueryType_CQL:
-        srw_sr->u.request->query_type = Z_SRW_query_type_cql;
-        srw_sr->u.request->query.cql = encode_SRW_term(srw_sr_odr_out, arg);
+        srw_sr->u.request->queryType = "cql";
+        srw_sr->u.request->query = encode_SRW_term(srw_sr_odr_out, arg);
 
-        sr->u.request->query_type = Z_SRW_query_type_cql;
-        sr->u.request->query.cql = encode_SRW_term(srw_sr_odr_out, arg);
+        sr->u.request->queryType = "cql";
+        sr->u.request->query = encode_SRW_term(srw_sr_odr_out, arg);
         break;
     case QueryType_Prefix:
-        srw_sr->u.request->query_type = Z_SRW_query_type_pqf;
-        srw_sr->u.request->query.pqf = encode_SRW_term(srw_sr_odr_out, arg);
+        srw_sr->u.request->queryType = "pqf";
+        srw_sr->u.request->query = encode_SRW_term(srw_sr_odr_out, arg);
 
-        sr->u.request->query_type = Z_SRW_query_type_pqf;
-        sr->u.request->query.pqf = encode_SRW_term(srw_sr_odr_out, arg);
+        sr->u.request->queryType = "pqf";
+        sr->u.request->query = encode_SRW_term(srw_sr_odr_out, arg);
         break;
     default:
         printf("Only CQL and PQF supported in SRW\n");
         return 0;
     }
+    if (*sru_recordPacking)
+        sr->u.request->recordPacking = sru_recordPacking;
+    sru_maximumRecords = 0;
     sr->u.request->maximumRecords = odr_intdup(out, 0);
     sr->u.request->facetList = facet_list;
-    if (record_schema)
-        sr->u.request->recordSchema = record_schema;
+    sr->u.request->recordSchema = record_schema;
     if (recordsyntax_size == 1 && !yaz_matchstr(recordsyntax_list[0], "xml"))
         sr->u.request->recordPacking = "xml";
     return send_srw(sr);
@@ -1503,7 +1550,7 @@ static int send_Z3950_searchRequest(const char *arg)
             printf("CCL ERROR: %s\n", ccl_err_msg(error));
             return 0;
         }
-    } 
+    }
     else if (myQueryType == QueryType_CQL2RPN)
     {
         /* ### All this code should be wrapped in a utility function */
@@ -1537,12 +1584,12 @@ static int send_Z3950_searchRequest(const char *arg)
     req->referenceId = set_refid(out);
     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
     {
-        static unsigned char big[2100];
+        static char big[2100];
         static Odr_oct bigo;
 
         /* send a very big referenceid to test transport stack etc. */
         memset(big, 'A', 2100);
-        bigo.len = bigo.size = 2100;
+        bigo.len = 2100;
         bigo.buf = big;
         req->referenceId = &bigo;
     }
@@ -1596,7 +1643,7 @@ static int send_Z3950_searchRequest(const char *arg)
     case QueryType_CCL:
         query.which = Z_Query_type_2;
         query.u.type_2 = &ccl_query;
-        ccl_query.buf = (unsigned char*) arg;
+        ccl_query.buf = (char *) arg;
         ccl_query.len = strlen(arg);
         break;
     case QueryType_CCL2RPN:
@@ -1636,16 +1683,16 @@ static void display_term(Z_Term *term)
     switch (term->which)
     {
     case Z_Term_general:
-        printf("    %.*s", term->u.general->len, term->u.general->buf);
+        printf("%.*s", term->u.general->len, term->u.general->buf);
         break;
     case Z_Term_characterString:
-        printf("    %s", term->u.characterString);
+        printf("%s", term->u.characterString);
         break;
     case Z_Term_numeric:
-        printf("    " ODR_INT_PRINTF, *term->u.numeric);
+        printf(ODR_INT_PRINTF, *term->u.numeric);
         break;
     case Z_Term_null:
-        printf("    null");
+        printf("null");
         break;
     }
 }
@@ -1672,19 +1719,16 @@ static void display_facet(Z_FacetField *facet)
     {
         Z_AttributeList *al = facet->attributes;
         struct yaz_facet_attr attr_values;
-        attr_values.errcode = 0;
-        attr_values.limit = -1;
-        attr_values.useattr = 0;
-        attr_values.relation = "default";
-
+        yaz_facet_attr_init(&attr_values);
         yaz_facet_attr_get_z_attributes(al, &attr_values);
         if (!attr_values.errcode)
         {
             int term_index;
-            printf("  %s (%d): \n", attr_values.useattr, /* attr_values.relation, attr_values.limit, */ facet->num_terms);
+            printf("  %s (%d): \n", attr_values.useattr, facet->num_terms);
             for (term_index = 0 ; term_index < facet->num_terms; term_index++)
             {
                 Z_FacetTerm *facetTerm = facet->terms[term_index];
+                printf("    ");
                 display_term(facetTerm->term);
                 printf(" (" NMEM_INT_PRINTF ")\n", *facetTerm->count);
             }
@@ -1847,7 +1891,7 @@ static void print_referenceId(int iLevel, Z_ReferenceId *referenceId)
         int i;
 
         print_level(iLevel);
-        printf("Ref Id (%d, %d): ", referenceId->len, referenceId->size);
+        printf("Ref Id (%d): ", referenceId->len);
         for (i = 0; i < referenceId->len; i++)
             printf("%c", referenceId->buf[i]);
         printf("\n");
@@ -2103,21 +2147,14 @@ static Z_External *create_external_itemRequest(void)
     }
     else
     {
+        item_request_buf = odr_getbuf (out, &item_request_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_oct *)
-            odr_malloc(out, sizeof(*r->u.single_ASN1_type));
-        r->u.single_ASN1_type->buf = (unsigned char *)
-        odr_malloc(out, item_request_size);
-        r->u.single_ASN1_type->len = item_request_size;
-        r->u.single_ASN1_type->size = item_request_size;
-        memcpy(r->u.single_ASN1_type->buf, item_request_buf,
-                item_request_size);
-
+        r->u.single_ASN1_type =
+            odr_create_Odr_oct(out, item_request_buf, item_request_size);
         do_hex_dump(item_request_buf,item_request_size);
     }
     return r;
@@ -2161,18 +2198,8 @@ static Z_External *create_external_ILL_APDU(void)
         r->indirect_reference = 0;
         r->descriptor = 0;
         r->which = Z_External_single;
-
-        r->u.single_ASN1_type = (Odr_oct *)
-            odr_malloc(out, sizeof(*r->u.single_ASN1_type));
-        r->u.single_ASN1_type->buf = (unsigned char *)
-        odr_malloc(out, ill_request_size);
-        r->u.single_ASN1_type->len = ill_request_size;
-        r->u.single_ASN1_type->size = ill_request_size;
-        memcpy(r->u.single_ASN1_type->buf, ill_request_buf, ill_request_size);
-/*         printf("len = %d\n", ill_request_size); */
-/*              do_hex_dump(ill_request_buf,ill_request_size); */
-/*              printf("--- end of extenal\n"); */
-
+        r->u.single_ASN1_type = odr_create_Odr_oct(out, ill_request_buf,
+                                                   ill_request_size);
     }
     return r;
 }
@@ -2280,22 +2307,6 @@ static int only_z3950(void)
     return 0;
 }
 
-static int is_SRW(void)
-{
-    if (!conn)
-    {
-        printf("Not connected yet\n");
-        return 1;
-    }
-    if (protocol == PROTO_HTTP && yaz_matchstr(sru_method, "solr"))
-    {
-        printf("Not supported by SRW\n");
-        return 1;
-    }
-    return 0;
-}
-
-
 static int cmd_update_common(const char *arg, int version);
 
 static int cmd_update(const char *arg)
@@ -2367,9 +2378,9 @@ static int cmd_update_common(const char *arg, int version)
 
 #if YAZ_HAVE_XML2
     if (protocol == PROTO_HTTP)
-        return send_SRW_update(action_no, recid_buf, rec_buf, rec_len);
+        return send_SRW_update(action_no, recid, rec_buf, rec_len);
 #endif
-    return send_Z3950_update(version, action_no, recid_buf, rec_buf, rec_len);
+    return send_Z3950_update(version, action_no, recid, rec_buf, rec_len);
 }
 
 #if YAZ_HAVE_XML2
@@ -2377,7 +2388,7 @@ static int send_SRW_update(int action_no, const char *recid,
                            char *rec_buf, int rec_len)
 {
     if (!conn)
-        session_connect(cur_host);
+        session_connect();
     if (!conn)
         return 0;
     else
@@ -2477,16 +2488,8 @@ static int send_Z3950_update(int version, int action_no, const char *recid,
         notToKeep->elements[0] = (Z_IU0SuppliedRecords_elem *)
             odr_malloc(out, sizeof(**notToKeep->elements));
         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
-        if (recid)
-        {
-            notToKeep->elements[0]->u.opaque = (Odr_oct *)
-                odr_malloc(out, sizeof(Odr_oct));
-            notToKeep->elements[0]->u.opaque->buf = (unsigned char *) recid;
-            notToKeep->elements[0]->u.opaque->size = strlen(recid);
-            notToKeep->elements[0]->u.opaque->len = strlen(recid);
-        }
-        else
-            notToKeep->elements[0]->u.opaque = 0;
+        notToKeep->elements[0]->u.opaque = recid ?
+            odr_create_Odr_oct(out, recid, strlen(recid)) : 0;
         notToKeep->elements[0]->supplementalId = 0;
         notToKeep->elements[0]->correlationInfo = 0;
         notToKeep->elements[0]->record = record_this;
@@ -2524,16 +2527,8 @@ static int send_Z3950_update(int version, int action_no, const char *recid,
         notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
             odr_malloc(out, sizeof(**notToKeep->elements));
         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
-        if (recid)
-        {
-            notToKeep->elements[0]->u.opaque = (Odr_oct *)
-                odr_malloc(out, sizeof(Odr_oct));
-            notToKeep->elements[0]->u.opaque->buf = (unsigned char *) recid;
-            notToKeep->elements[0]->u.opaque->size = strlen(recid);
-            notToKeep->elements[0]->u.opaque->len = strlen(recid);
-        }
-        else
-            notToKeep->elements[0]->u.opaque = 0;
+        notToKeep->elements[0]->u.opaque = recid ?
+            odr_create_Odr_oct(out, recid, strlen(recid)) : 0;
         notToKeep->elements[0]->supplementalId = 0;
         notToKeep->elements[0]->correlationInfo = 0;
         notToKeep->elements[0]->record = record_this;
@@ -2577,7 +2572,7 @@ static int cmd_xmles(const char *arg)
                           &ext->u.single_ASN1_type->len) == 0)
             return 0;
 
-        ext->u.single_ASN1_type->buf = (unsigned char *) asn_buf;
+        ext->u.single_ASN1_type->buf = asn_buf;
 
         oid = yaz_string_to_oid_odr(yaz_oid_std(),
                                     CLASS_EXTSERV, oid_str, out);
@@ -2655,7 +2650,7 @@ static int cmd_explain(const char *arg)
         return 0;
 #if YAZ_HAVE_XML2
     if (!conn)
-        session_connect(cur_host);
+        session_connect();
     if (conn)
     {
         Z_SRW_PDU *sr = 0;
@@ -2663,7 +2658,7 @@ static int cmd_explain(const char *arg)
         setno = 1;
 
         /* save this for later .. when fetching individual records */
-        sr = yaz_srw_get(out, Z_SRW_explain_request);
+        sr = yaz_srw_get_pdu(out, Z_SRW_explain_request, sru_version);
         if (recordsyntax_size == 1
             && !yaz_matchstr(recordsyntax_list[0], "xml"))
             sr->u.explain_request->recordPacking = "xml";
@@ -2678,12 +2673,14 @@ static int cmd_init(const char *arg)
 {
     if (*arg)
     {
-        strncpy(cur_host, arg, sizeof(cur_host)-1);
-        cur_host[sizeof(cur_host)-1] = 0;
+        wrbuf_rewind(cur_host);
+        if (!strstr(arg, "://") && strcmp(sru_method, "soap"))
+            wrbuf_puts(cur_host, "http://");
+        wrbuf_puts(cur_host, arg);
     }
     if (only_z3950())
         return 1;
-    send_Z3950_initRequest(cur_host);
+    send_Z3950_initRequest(wrbuf_cstr(cur_host));
     return 2;
 }
 
@@ -2820,7 +2817,8 @@ static int cmd_sru(const char *arg)
     }
     else
     {
-        int r = sscanf(arg, "%9s %9s", sru_method, sru_version);
+        int r = sscanf(arg, "%9s %9s %9s", sru_method, sru_version,
+            sru_recordPacking);
         if (r >= 1)
         {
             if (!yaz_matchstr(sru_method, "post"))
@@ -2853,7 +2851,7 @@ static int cmd_find(const char *arg)
     {
 #if YAZ_HAVE_XML2
         if (!conn)
-            session_connect(cur_host);
+            session_connect();
         if (!conn)
             return 0;
         if (!send_SRW_searchRequest(arg))
@@ -2864,7 +2862,7 @@ static int cmd_find(const char *arg)
     }
     else
     {
-        if (*cur_host && auto_reconnect)
+        if (wrbuf_len(cur_host) && auto_reconnect)
         {
             int i = 0;
             for (;;)
@@ -2882,7 +2880,7 @@ static int cmd_find(const char *arg)
                     printf("Unable to reconnect\n");
                     break;
                 }
-                session_connect(cur_host);
+                session_connect();
                 wait_and_handle_response(0);
             }
             return 0;
@@ -2903,21 +2901,17 @@ static int cmd_find(const char *arg)
 
 static int cmd_facets(const char *arg)
 {
-    /* TODO Wrong odr. Loosing memory */
-    ODR odr = odr_createmem(ODR_ENCODE);
-    int size = 0;
+    if (!facet_odr)
+        facet_odr = odr_createmem(ODR_ENCODE);
+    odr_reset(facet_odr);
+
     if (!*arg)
     {
         facet_list = 0;
         printf("Facets cleared.\n");
         return 0;
     }
-    size = strlen(arg);
-    if (is_SRW()) {
-        printf("WARN: No supported for SRW/SRU.\n");
-    }
-    facet_list = yaz_pqf_parse_facet_list(odr, arg);
-
+    facet_list = yaz_pqf_parse_facet_list(facet_odr, arg);
     if (!facet_list)
     {
         printf("Invalid facet list: %s", arg);
@@ -2926,7 +2920,6 @@ static int cmd_facets(const char *arg)
     return 1;
 }
 
-
 static int cmd_delete(const char *arg)
 {
     if (only_z3950())
@@ -2938,30 +2931,30 @@ static int cmd_delete(const char *arg)
 
 static int cmd_ssub(const char *arg)
 {
-    if (!(smallSetUpperBound = atoi(arg)))
-        return 0;
+    smallSetUpperBound = odr_strtol(arg, 0, 10);
     return 1;
 }
 
 static int cmd_lslb(const char *arg)
 {
-    if (!(largeSetLowerBound = atoi(arg)))
-        return 0;
+    largeSetLowerBound = odr_strtol(arg, 0, 10);
     return 1;
 }
 
 static int cmd_mspn(const char *arg)
 {
-    if (!(mediumSetPresentNumber = atoi(arg)))
-        return 0;
+    mediumSetPresentNumber = odr_strtol(arg, 0, 10);
     return 1;
 }
 
 static int cmd_status(const char *arg)
 {
-    printf("smallSetUpperBound: %d\n", smallSetUpperBound);
-    printf("largeSetLowerBound: %d\n", largeSetLowerBound);
-    printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
+    printf("smallSetUpperBound: " ODR_INT_PRINTF "\n",
+           smallSetUpperBound);
+    printf("largeSetLowerBound: " ODR_INT_PRINTF "\n",
+           largeSetLowerBound);
+    printf("mediumSetPresentNumber: " ODR_INT_PRINTF "\n",
+           mediumSetPresentNumber);
     return 1;
 }
 
@@ -2985,21 +2978,50 @@ static int cmd_setnames(const char *arg)
 
 /* PRESENT SERVICE ----------------------------- */
 
+size_t check_token(const char *haystack, const char *token)
+{
+    size_t len = strlen(token);
+    size_t extra;
+    if (strncmp(haystack, token, len))
+        return 0;
+    for (extra = 0; haystack[extra + len] != '\0'; extra++)
+        if (!strchr(" \r\n\t", haystack[extra + len]))
+        {
+            if (extra)
+                break;
+            else
+                return 0;  /* no whitespace after token */
+        }
+    return extra + len;
+}
+
 static int parse_show_args(const char *arg_c, char *setstring,
                            Odr_int *start, Odr_int *number)
 {
     char *end_ptr;
     Odr_int start_position;
+    size_t token_len;
 
     if (setnumber >= 0)
         sprintf(setstring, "%d", setnumber);
     else
         *setstring = '\0';
 
-    if (!strcmp(arg_c, "all"))
+    token_len = check_token(arg_c, "format");
+    if (token_len)
+    {
+        pretty_xml = 1;
+        arg_c += token_len;
+    }
+    else
+        pretty_xml = 0;
+
+    token_len = check_token(arg_c, "all");
+    if (token_len)
     {
         *number = last_hit_count;
         *start = 1;
+        return 1;
     }
     start_position = odr_strtol(arg_c, &end_ptr, 10);
     if (end_ptr == arg_c)
@@ -3007,7 +3029,7 @@ static int parse_show_args(const char *arg_c, char *setstring,
     *start = start_position;
     if (*end_ptr == '\0')
         return 1;
-    while (isspace(*(unsigned char *)end_ptr))
+    while (yaz_isspace(*end_ptr))
         end_ptr++;
     if (*end_ptr != '+')
     {
@@ -3024,7 +3046,7 @@ static int parse_show_args(const char *arg_c, char *setstring,
     }
     if (*end_ptr == '\0')
         return 1;
-    while (isspace(*(unsigned char *)end_ptr))
+    while (yaz_isspace(*end_ptr))
         end_ptr++;
     if (*end_ptr != '+')
     {
@@ -3139,10 +3161,12 @@ static int send_SRW_presentRequest(const char *arg)
         return 0;
     if (!parse_show_args(arg, setstring, &setno, &nos))
         return 0;
+    if (*sru_recordPacking)
+        sr->u.request->recordPacking = sru_recordPacking;
     sr->u.request->startRecord = odr_intdup(out, setno);
+    sru_maximumRecords = nos;
     sr->u.request->maximumRecords = odr_intdup(out, nos);
-    if (record_schema)
-        sr->u.request->recordSchema = record_schema;
+    sr->u.request->recordSchema = record_schema;
     if (recordsyntax_size == 1 && !yaz_matchstr(recordsyntax_list[0], "xml"))
         sr->u.request->recordPacking = "xml";
     return send_srw(sr);
@@ -3199,7 +3223,7 @@ static int cmd_show(const char *arg)
     {
 #if YAZ_HAVE_XML2
         if (!conn)
-            session_connect(cur_host);
+            session_connect();
         if (!conn)
             return 0;
         if (!send_SRW_presentRequest(arg))
@@ -3223,9 +3247,15 @@ static int cmd_show(const char *arg)
 
 static void exit_client(int code)
 {
+    odr_destroy(in);
+    odr_destroy(out);
+    odr_destroy(print);
+    ccl_qual_rm(&bibset);
+    yaz_cookies_destroy(yaz_cookies);
     file_history_save(file_history);
     file_history_destroy(&file_history);
     nmem_destroy(nmem_auth);
+    wrbuf_destroy(cur_host);
     exit(code);
 }
 
@@ -3248,10 +3278,10 @@ static int cmd_cancel(const char *arg)
             apdu->u.triggerResourceControlRequest;
         bool_t rfalse = 0;
         char command[16];
-        
+
         *command = '\0';
         sscanf(arg, "%15s", command);
-        
+
         if (only_z3950())
             return 0;
         if (session_initResponse &&
@@ -3264,7 +3294,7 @@ static int cmd_cancel(const char *arg)
         *req->requestedAction = Z_TriggerResourceControlRequest_cancel;
         req->resultSetWanted = &rfalse;
         req->referenceId = set_refid(out);
-        
+
         send_apdu(apdu);
         printf("Sent cancel request\n");
         if (!strcmp(command, "wait"))
@@ -3287,13 +3317,14 @@ static int cmd_cancel_find(const char *arg)
 }
 
 static int send_Z3950_scanrequest(const char *set,  const char *query,
-                                  Odr_int pp, Odr_int num, const char *term)
+                                  Odr_int *pos, Odr_int num, const char *term)
 {
     Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
     Z_ScanRequest *req = apdu->u.scanRequest;
 
     if (only_z3950())
         return 0;
+    printf("query: %s\n", query);
     if (queryType == QueryType_CCL2RPN)
     {
         int error, pos;
@@ -3353,17 +3384,15 @@ static int send_Z3950_scanrequest(const char *set,  const char *query,
             req->termListAndStartPoint->term->u.general)
         {
             req->termListAndStartPoint->term->u.general->buf =
-                (unsigned char *) odr_strdup(out, term);
-            req->termListAndStartPoint->term->u.general->len =
-                req->termListAndStartPoint->term->u.general->size =
-                strlen(term);
+                odr_strdup(out, term);
+            req->termListAndStartPoint->term->u.general->len = strlen(term);
         }
     }
     req->referenceId = set_refid(out);
     req->num_databaseNames = num_databaseNames;
     req->databaseNames = databaseNames;
     req->numberOfTermsRequested = &num;
-    req->preferredPositionInResponse = &pp;
+    req->preferredPositionInResponse = pos;
     req->stepSize = odr_intdup(out, scan_stepSize);
 
     if (set)
@@ -3538,9 +3567,17 @@ static int cmd_scanstep(const char *arg)
 
 static int cmd_scanpos(const char *arg)
 {
-    int r = sscanf(arg, "%d", &scan_position);
-    if (r == 0)
-        scan_position = 1;
+    if (!strcmp(arg, "none"))
+        strcpy(scan_position, "none");
+    else
+    {
+        int dummy;
+        int r = sscanf(arg, "%d", &dummy);
+        if (r == 1 && strlen(arg) < sizeof(scan_position)-1)
+            strcpy(scan_position, arg);
+        else
+            printf("specify number of none for scanpos\n");
+    }
     return 0;
 }
 
@@ -3554,23 +3591,37 @@ static int cmd_scansize(const char *arg)
 
 static int cmd_scan_common(const char *set, const char *arg)
 {
+    Odr_int pos, *pos_p = 0;
+    const char *scan_term = 0;
+    const char *scan_query = 0;
+
+    if (!*arg)
+    {
+        pos = 1;
+        pos_p = &pos;
+        scan_query = last_scan_query;
+        scan_term = last_scan_line;
+    }
+    else
+    {
+        strcpy(last_scan_query, arg);
+        scan_query = arg;
+        if (strcmp(scan_position, "none"))
+        {
+            pos = odr_atoi(scan_position);
+            pos_p = &pos;
+        }
+    }
+
     if (protocol == PROTO_HTTP)
     {
 #if YAZ_HAVE_XML2
         if (!conn)
-            session_connect(cur_host);
+            session_connect();
         if (!conn)
             return 0;
-        if (*arg)
-        {
-            if (send_SRW_scanRequest(arg, scan_position, scan_size) < 0)
-                return 0;
-        }
-        else
-        {
-            if (send_SRW_scanRequest(last_scan_line, 1, scan_size) < 0)
-                return 0;
-        }
+        if (send_SRW_scanRequest(scan_query, pos_p, scan_size) < 0)
+            return 0;
         return 2;
 #else
         return 0;
@@ -3578,9 +3629,9 @@ static int cmd_scan_common(const char *set, const char *arg)
     }
     else
     {
-        if (*cur_host && !conn && auto_reconnect)
+        if (wrbuf_len(cur_host) && !conn && auto_reconnect)
         {
-            session_connect(cur_host);
+            session_connect();
             wait_and_handle_response(0);
         }
         if (!conn)
@@ -3591,19 +3642,9 @@ static int cmd_scan_common(const char *set, const char *arg)
             printf("Target doesn't support scan\n");
             return 0;
         }
-        if (*arg)
-        {
-            strcpy(last_scan_query, arg);
-            if (send_Z3950_scanrequest(set, arg,
-                                       scan_position, scan_size, 0) < 0)
-                return 0;
-        }
-        else
-        {
-            if (send_Z3950_scanrequest(set, last_scan_query,
-                                      1, scan_size, last_scan_line) < 0)
-                return 0;
-        }
+        if (send_Z3950_scanrequest(set, scan_query, pos_p,
+                                   scan_size, scan_term) < 0)
+            return 0;
         return 2;
     }
 }
@@ -4035,7 +4076,7 @@ static int cmd_set_auto_reconnect(const char* arg)
         printf("Error use on or off\n");
         return 1;
     }
-    
+
     if (auto_reconnect)
         printf("Set auto reconnect enabled.\n");
     else
@@ -4221,6 +4262,8 @@ static void initialize(const char *rc_file)
     FILE *inf;
     int i;
 
+    cur_host = wrbuf_alloc();
+
     if (!(out = odr_createmem(ODR_ENCODE)) ||
         !(in = odr_createmem(ODR_DECODE)) ||
         !(print = odr_createmem(ODR_PRINT)))
@@ -4228,7 +4271,9 @@ static void initialize(const char *rc_file)
         fprintf(stderr, "failed to allocate ODR streams\n");
         exit(1);
     }
-    
+
+    strcpy(scan_position, "1");
+
     setvbuf(stdout, 0, _IONBF, 0);
     if (apdu_file)
         odr_setprint(print, apdu_file);
@@ -4273,16 +4318,13 @@ struct timeval tv_start;
 static void handle_srw_record(Z_SRW_record *rec)
 {
     if (rec->recordPosition)
-    {
         printf("pos=" ODR_INT_PRINTF, *rec->recordPosition);
-        setno = *rec->recordPosition + 1;
-    }
     if (rec->recordSchema)
         printf(" schema=%s", rec->recordSchema);
     printf("\n");
     if (rec->recordData_buf && rec->recordData_len)
     {
-        printf("%.*s", rec->recordData_len, rec->recordData_buf);
+        print_xml_record(rec->recordData_buf, rec->recordData_len);
         marc_file_write(rec->recordData_buf, rec->recordData_len);
     }
     else
@@ -4315,11 +4357,22 @@ static void handle_srw_response(Z_SRW_searchRetrieveResponse *res)
     }
     if (res->numberOfRecords)
         printf("Number of hits: " ODR_INT_PRINTF "\n", *res->numberOfRecords);
-    if (res->facetList) {
+    if (res->facetList)
         display_facets(res->facetList);
-    }
-    for (i = 0; i<res->num_records; i++)
+    if (res->suggestions)
+        printf("Suggestions:\n%s\n", res->suggestions);
+    for (i = 0; i < res->num_records; i++)
+    {
+        if (i >= sru_maximumRecords)
+        {
+            printf("SRU server returns extra records. Skipping "
+                   ODR_INT_PRINTF " records.\n",
+                   res->num_records - sru_maximumRecords);
+            break;
+        }
         handle_srw_record(res->records + i);
+    }
+    setno += res->num_records;
 }
 
 static void handle_srw_scan_term(Z_SRW_scanTerm *term)
@@ -4386,9 +4439,11 @@ static void http_response(Z_HTTP_Response *hres)
 
             if (ret == 0 && sr->which == Z_SRW_searchRetrieve_response)
                 handle_srw_response(sr->u.response);
+            else if (ret == 0 && sr->which == Z_SRW_scan_response)
+                handle_srw_scan_response(sr->u.scan_response);
             else
             {
-                printf("Decoding of SOLR package failed\n");
+                printf("Decoding of Solr package failed\n");
                 ret = -1;
             }
             odr_destroy(o);
@@ -4397,9 +4452,11 @@ static void http_response(Z_HTTP_Response *hres)
         {
             Z_SOAP *soap_package = 0;
             ODR o = odr_createmem(ODR_DECODE);
-            Z_SOAP_Handler soap_handlers[3] = {
-                {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
+            Z_SOAP_Handler soap_handlers[] = {
+                {YAZ_XMLNS_SRU_v2_mask, 0, (Z_SOAP_fun) yaz_srw_codec},
                 {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
+                {YAZ_XMLNS_SRU_v1_response, 0, (Z_SOAP_fun) yaz_srw_codec},
+                {"searchRetrieveResponse", 0, (Z_SOAP_fun) yaz_srw_codec},
                 {0, 0, 0}
             };
             ret = z_soap_codec(o, &soap_package,
@@ -4462,7 +4519,7 @@ static void http_response(Z_HTTP_Response *hres)
 }
 #endif
 
-#define max_HTTP_redirects 2
+#define max_HTTP_redirects 3
 
 static void wait_and_handle_response(int one_response_only)
 {
@@ -4484,7 +4541,7 @@ static void wait_and_handle_response(int one_response_only)
         {
             cs_close(conn);
             conn = 0;
-            session_connect(cur_host);
+            session_connect();
             reconnect_ok = 0;
             if (conn)
             {
@@ -4599,17 +4656,23 @@ static void wait_and_handle_response(int one_response_only)
             Z_HTTP_Response *hres = gdu->u.HTTP_Response;
             int code = hres->code;
             const char *location = 0;
+
+            yaz_cookies_response(yaz_cookies, hres);
             if ((code == 301 || code == 302)
                 && no_redirects < max_HTTP_redirects
                 && !yaz_matchstr(sru_method, "get")
                 && (location = z_HTTP_header_lookup(hres->headers, "Location")))
             {
                 const char *base_tmp;
-                session_connect_base(location, &base_tmp);
+                int host_change = 0;
+                location = yaz_check_location(in, wrbuf_cstr(cur_host),
+                                              location, &host_change);
+                if (host_change)
+                    session_connect_base(location, &base_tmp);
                 no_redirects++;
                 if (conn)
                 {
-                    if (send_SRW_redirect(location, hres) == 2)
+                    if (send_SRW_redirect(location) == 2)
                         continue;
                 }
                 printf("Redirect failed\n");
@@ -4783,9 +4846,9 @@ static int cmd_list_all(const char* args)
 
     /* connection options */
     if (conn)
-        printf("Connected to         : %s\n", cur_host);
-    else if (*cur_host)
-        printf("Not connected to     : %s\n", cur_host);
+        printf("Connected to         : %s\n", wrbuf_cstr(cur_host));
+    else if (cur_host && wrbuf_len(cur_host))
+        printf("Not connected to     : %s\n", wrbuf_cstr(cur_host));
     else
         printf("Not connected        : \n");
     if (yazProxy) printf("using proxy          : %s\n",yazProxy);
@@ -4829,7 +4892,9 @@ static int cmd_list_all(const char* args)
     printf("Named Result Sets    : %s\n",setnumber==-1?"off":"on");
 
     /* piggy back options */
-    printf("ssub/lslb/mspn       : %d/%d/%d\n",smallSetUpperBound,largeSetLowerBound,mediumSetPresentNumber);
+    printf("ssub/lslb/mspn       : " ODR_INT_PRINTF "/" ODR_INT_PRINTF "/"
+           ODR_INT_PRINTF "\n",
+           smallSetUpperBound, largeSetLowerBound, mediumSetPresentNumber);
 
     /* print present related options */
     if (recordsyntax_size > 0)
@@ -4889,7 +4954,7 @@ static int cmd_wait_response(const char *arg)
 {
     int i;
     int wait_for = atoi(arg);
-    if (wait_for < 1) 
+    if (wait_for < 1)
         wait_for = 1;
 
     for (i = 0 ; i < wait_for; ++i )
@@ -5120,7 +5185,7 @@ static void process_cmd_line(char* line)
 
         for (; *p; ++p)
         {
-            if (!isspace(*(unsigned char *) p))
+            if (!yaz_isspace(*p))
                 lastnonspace = p;
         }
         if (lastnonspace)