New ZOOM events ZOOM_EVENT_RECV_{RECORD,SEARCH} for receiving a record
[yaz-moved-to-github.git] / zutil / zoom-c.c
index 9a9e9da..7ce62e7 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 2000-2003, Index Data
  * See the file LICENSE for details.
  *
- * $Id: zoom-c.c,v 1.20 2003-02-17 14:35:42 adam Exp $
+ * $Id: zoom-c.c,v 1.32 2003-04-28 11:04:52 adam Exp $
  *
  * ZOOM layer for C, connections, result sets, queries.
  */
@@ -100,7 +100,7 @@ static void set_dset_error (ZOOM_connection c, int error,
     else if (addinfo)
         c->addinfo = xstrdup(addinfo);
     if (error)
-        yaz_log(LOG_LOG, "Error %s %s:%d %s %s",
+        yaz_log(LOG_DEBUG, "Error %s %s:%d %s %s",
                 c->host_port ? c->host_port : "<>", dset, error,
                 addinfo ? addinfo : "",
                 addinfo2 ? addinfo2 : "");
@@ -109,13 +109,13 @@ static void set_dset_error (ZOOM_connection c, int error,
 static void set_HTTP_error (ZOOM_connection c, int error,
                             const char *addinfo, const char *addinfo2)
 {
-    return set_dset_error(c, error, "HTTP", addinfo, addinfo2);
+    set_dset_error(c, error, "HTTP", addinfo, addinfo2);
 }
 
 static void set_ZOOM_error (ZOOM_connection c, int error,
                            const char *addinfo)
 {
-    return set_dset_error(c, error, "ZOOM", addinfo, 0);
+    set_dset_error(c, error, "ZOOM", addinfo, 0);
 }
 
 static void clear_error (ZOOM_connection c)
@@ -172,6 +172,7 @@ void ZOOM_connection_remove_task (ZOOM_connection c)
        switch (task->which)
        {
        case ZOOM_TASK_SEARCH:
+
            ZOOM_resultset_destroy (task->u.search.resultset);
            break;
        case ZOOM_TASK_RETRIEVE:
@@ -347,7 +348,6 @@ ZOOM_connection_connect(ZOOM_connection c,
        c->lang = 0;
 
     xfree (c->host_port);
-    xfree (c->path);
     if (portnum)
     {
        char hostn[128];
@@ -389,6 +389,7 @@ ZOOM_query_create(void)
     s->z_query = 0;
     s->sort_spec = 0;
     s->odr = odr_createmem (ODR_ENCODE);
+    s->query_string = 0;
 
     return s;
 }
@@ -411,6 +412,7 @@ ZOOM_query_destroy(ZOOM_query s)
 ZOOM_API(int)
 ZOOM_query_prefix(ZOOM_query s, const char *str)
 {
+    s->query_string = odr_strdup(s->odr, str);
     s->z_query = (Z_Query *) odr_malloc (s->odr, sizeof(*s->z_query));
     s->z_query->which = Z_Query_type_1;
     s->z_query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
@@ -424,16 +426,19 @@ ZOOM_query_cql(ZOOM_query s, const char *str)
 {
     Z_External *ext;
 
+    s->query_string = odr_strdup(s->odr, str);
+
     ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
     ext->direct_reference = odr_getoidbystr(s->odr, "1.2.840.10003.16.2");
     ext->indirect_reference = 0;
     ext->descriptor = 0;
     ext->which = Z_External_CQL;
-    ext->u.cql = odr_strdup(s->odr, str);
+    ext->u.cql = s->query_string;
     
     s->z_query = (Z_Query *) odr_malloc (s->odr, sizeof(*s->z_query));
     s->z_query->which = Z_Query_type_104;
     s->z_query->u.type_104 =  ext;
+
     return 0;
 }
 
@@ -466,6 +471,7 @@ ZOOM_connection_destroy(ZOOM_connection c)
     ZOOM_options_destroy (c->options);
     ZOOM_connection_remove_tasks (c);
     xfree (c->host_port);
+    xfree (c->path);
     xfree (c->proxy);
     xfree (c->charset);
     xfree (c->lang);
@@ -499,8 +505,7 @@ ZOOM_resultset ZOOM_resultset_create ()
     r->count = 0;
     r->record_cache = 0;
     r->r_sort_spec = 0;
-    r->z_query = 0;
-    r->search = 0;
+    r->query = 0;
     r->connection = 0;
     r->next = 0;
     return r;
@@ -527,8 +532,7 @@ ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
     const char *cp;
 
     r->r_sort_spec = q->sort_spec;
-    r->z_query = q->z_query;
-    r->search = q;
+    r->query = q;
 
     r->options = ZOOM_options_create_with_parent(c->options);
 
@@ -547,7 +551,7 @@ ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
     r->next = c->resultsets;
     c->resultsets = r;
 
-    if (c->host_port && c->proto == PROTO_SRW)
+    if (c->host_port && c->proto == PROTO_HTTP)
     {
         if (!c->cs)
         {
@@ -605,7 +609,7 @@ ZOOM_resultset_destroy(ZOOM_resultset r)
                rp = &(*rp)->next;
            }
        }
-       ZOOM_query_destroy (r->search);
+       ZOOM_query_destroy (r->query);
        ZOOM_options_destroy (r->options);
        odr_destroy (r->odr);
         xfree (r->setname);
@@ -641,6 +645,20 @@ static void ZOOM_resultset_retrieve (ZOOM_resultset r,
     c = r->connection;
     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;
+        }
+    }
     task = ZOOM_connection_add_task (c, ZOOM_TASK_RETRIEVE);
     task->u.retrieve.resultset = r;
     task->u.retrieve.start = start;
@@ -695,29 +713,25 @@ static zoom_ret do_connect (ZOOM_connection c)
     yaz_log (LOG_DEBUG, "do_connect host=%s", effective_host);
 
     assert (!c->cs);
+    c->cs = cs_create_host (effective_host, 0, &add);
+
+    if (c->cs && c->cs->protocol == PROTO_HTTP)
+    {
+#if HAVE_XML2
+        const char *path = 0;
 
-    if (memcmp(c->host_port, "http:", 5) == 0)
-    {
-#if HAVE_XSLT
-        const char *path;
-        c->proto = PROTO_SRW;
-        effective_host = c->host_port + 5;
-        if (*effective_host == '/')
-            effective_host++;
-        if (*effective_host == '/')
-            effective_host++;
-        if (!(path = strchr(effective_host, '/')))
-            path = "/";
+        c->proto = PROTO_HTTP;
+        cs_get_host_args(c->host_port, &path);
         xfree(c->path);
-        c->path = xstrdup(path);
+        c->path = xmalloc(strlen(path)+2);
+        c->path[0] = '/';
+        strcpy (c->path+1, path);
 #else
-        c->state = STATE_IDLE;
         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
+        do_close(c);
         return zoom_complete;
 #endif
     }
-    c->cs = cs_create_host (effective_host, 0, &add);
-    
     if (c->cs)
     {
         int ret = cs_connect (c->cs, add);
@@ -840,7 +854,6 @@ static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
 static zoom_ret send_APDU (ZOOM_connection c, Z_APDU *a)
 {
     ZOOM_Event event;
-    yaz_log(LOG_LOG, "sending Z39.50 APDU");
     assert (a);
     if (encode_APDU(c, a, c->odr_out))
        return zoom_complete;
@@ -962,9 +975,10 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
     return send_APDU (c, apdu);
 }
 
-#if HAVE_XSLT
-static zoom_ret send_srw (ZOOM_connection c, Z_SRW_searchRetrieve *sr)
+#if HAVE_XML2
+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},
         {0, 0, 0}
@@ -977,8 +991,38 @@ static zoom_ret send_srw (ZOOM_connection c, Z_SRW_searchRetrieve *sr)
 
     gdu = z_get_HTTP_Request(c->odr_out);
     gdu->u.HTTP_Request->path = c->path;
+
+    if (c->host_port)
+    {
+        const char *cp0 = strstr(c->host_port, "://");
+        const char *cp1 = 0;
+        if (cp0)
+            cp0 = cp0+3;
+        else
+            cp0 = c->host_port;
+
+        cp1 = strchr(cp0, '/');
+        if (!cp1)
+            cp1 = cp0+strlen(cp0);
+
+        if (cp0 && cp1)
+        {
+            char *h = odr_malloc(c->odr_out, cp1 - cp0 + 1);
+            memcpy (h, cp0, cp1 - cp0);
+            h[cp1-cp0] = '\0';
+            z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
+                              "host", h);
+        }
+    }
+
+    strcpy(ctype, "text/xml");
+    if (c->charset && strlen(c->charset) < 20)
+    {
+        strcat(ctype, "; charset=");
+        strcat(ctype, c->charset);
+    }
     z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
-                      "Content-Type", "text/xml");
+                      "Content-Type", ctype);
     z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
                       "SOAPAction", "\"\"");
     p->which = Z_SOAP_generic;
@@ -988,9 +1032,10 @@ static zoom_ret send_srw (ZOOM_connection c, Z_SRW_searchRetrieve *sr)
     p->u.generic->p = sr;
     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
 
-    ret = z_soap_codec(o, &p,
-                       &gdu->u.HTTP_Request->content_buf,
-                       &gdu->u.HTTP_Request->content_len, h);
+    ret = z_soap_codec_enc(o, &p,
+                           &gdu->u.HTTP_Request->content_buf,
+                           &gdu->u.HTTP_Request->content_len, h,
+                           c->charset);
 
     if (!z_GDU(c->odr_out, &gdu, 0, 0))
         return zoom_complete;
@@ -1005,18 +1050,23 @@ static zoom_ret send_srw (ZOOM_connection c, Z_SRW_searchRetrieve *sr)
 }
 #endif
 
-#if HAVE_XSLT
+#if HAVE_XML2
 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
 {
     int i;
     ZOOM_resultset resultset = 0;
-    Z_SRW_searchRetrieve *sr = 0;
+    Z_SRW_PDU *sr = 0;
+    const char *recordPacking = 0;
 
     if (c->error)                  /* don't continue on error */
        return zoom_complete;
     assert (c->tasks);
     if (c->tasks->which == ZOOM_TASK_SEARCH)
+    {
         resultset = c->tasks->u.search.resultset;
+        resultset->setname = xstrdup ("default");
+        ZOOM_options_set (resultset->options, "setname", resultset->setname);
+    }
     else if(c->tasks->which == ZOOM_TASK_RETRIEVE)
     {
         resultset = c->tasks->u.retrieve.resultset;
@@ -1039,17 +1089,22 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
         if (i == resultset->count)
             return zoom_complete;
     }
-    assert(resultset->z_query);
+    assert(resultset->query);
         
     sr = yaz_srw_get(c->odr_out, Z_SRW_searchRetrieve_request);
 
-    if (resultset->z_query->which == Z_Query_type_104
-        && resultset->z_query->u.type_104->which == Z_External_CQL)
-        sr->u.request->query = resultset->z_query->u.type_104->u.cql;
-    else if (resultset->z_query->which == Z_Query_type_1)
-    { 
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, "Type-1");
-        return zoom_complete;
+    if (resultset->query->z_query->which == Z_Query_type_104
+        && resultset->query->z_query->u.type_104->which == Z_External_CQL)
+    {
+
+        sr->u.request->query_type = Z_SRW_query_type_cql;
+        sr->u.request->query.cql =resultset->query->z_query->u.type_104->u.cql;
+    }
+    else if (resultset->query->z_query->which == Z_Query_type_1 &&
+             resultset->query->z_query->u.type_1)
+    {
+        sr->u.request->query_type = Z_SRW_query_type_pqf;
+        sr->u.request->query.pqf = resultset->query->query_string;
     }
     else
     {
@@ -1060,9 +1115,11 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
     sr->u.request->maximumRecords = odr_intdup (c->odr_out, resultset->count);
     sr->u.request->recordSchema = resultset->schema;
 
-    resultset->setname = xstrdup ("default");
-    ZOOM_options_set (resultset->options, "setname", resultset->setname);
+    recordPacking = ZOOM_resultset_option_get (resultset, "recordPacking");
+
+    if (recordPacking)
+        sr->u.request->recordPacking = odr_strdup(c->odr_out, recordPacking);
+    
     return send_srw(c, sr);
 }
 #else
@@ -1102,10 +1159,10 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
        mediumSetElementSetName = elementSetName;
 
     assert (r);
-    assert (r->z_query);
+    assert (r->query);
 
     /* prepare query for the search request */
-    search_req->query = r->z_query;
+    search_req->query = r->query->z_query;
 
     search_req->databaseNames =
        set_DatabaseNames (c, r->options, &search_req->num_databaseNames);
@@ -1251,8 +1308,14 @@ ZOOM_resultset_record_immediate (ZOOM_resultset s,size_t pos)
 ZOOM_API(ZOOM_record)
 ZOOM_resultset_record (ZOOM_resultset r, size_t pos)
 {
-    ZOOM_resultset_retrieve (r, 1, pos, 1);
-    return ZOOM_resultset_record_immediate (r, pos);
+    ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
+
+    if (!rec)
+    {
+        ZOOM_resultset_retrieve (r, 1, pos, 1);
+        rec = ZOOM_resultset_record_immediate (r, pos);
+    }
+    return rec;
 }
 
 ZOOM_API(void)
@@ -1412,6 +1475,11 @@ ZOOM_record_get (ZOOM_record rec, const char *type, int *len)
             if (len) *len = 5;
             return "GRS-1";
         }
+       else if (r->which == Z_External_OPAC)
+        {
+            if (len) *len = 4;
+            return "OPAC";
+        }
        return 0;
     }
     else if (!strcmp (type, "raw"))
@@ -1465,6 +1533,9 @@ static void record_cache_add (ZOOM_resultset r, Z_NamePlusRecord *npr,
     const char *syntax = 
         ZOOM_resultset_option_get (r, "preferredRecordSyntax");
     
+    ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+    ZOOM_connection_put_event(r->connection, event);
+
     for (rc = r->record_cache; rc; rc = rc->next)
     {
        if (pos == rc->pos)
@@ -1604,11 +1675,15 @@ static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
 {
     ZOOM_resultset resultset;
-
+    ZOOM_Event event;
+    
     yaz_log (LOG_DEBUG, "got search response");
-
+    
     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
        return ;
+    
+    event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
+    ZOOM_connection_put_event(c, event);
 
     resultset = c->tasks->u.search.resultset;
 
@@ -1757,15 +1832,15 @@ static zoom_ret send_present (ZOOM_connection c)
         compo->u.complex->generic = (Z_Specification *)
             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
 
-        compo->u.complex->generic->which = Z_Specification_oid;
-        compo->u.complex->generic->u.oid = (Odr_oid *)
+        compo->u.complex->generic->which = Z_Schema_oid;
+        compo->u.complex->generic->schema.oid = (Odr_oid *)
             yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, resultset->schema);
 
-        if (!compo->u.complex->generic->u.oid)
+        if (!compo->u.complex->generic->schema.oid)
         {
             /* OID wasn't a schema! Try record syntax instead. */
 
-            compo->u.complex->generic->u.oid = (Odr_oid *)
+            compo->u.complex->generic->schema.oid = (Odr_oid *)
                 yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, resultset->schema);
         }
         if (elementSetName && *elementSetName)
@@ -2055,21 +2130,26 @@ static Z_ItemOrder *encode_item_order(ZOOM_package p)
     req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
        
-    req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
-        odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
-
     str = ZOOM_options_get(p->options, "itemorder-setname");
     if (!str)
         str = "default";
-    req->u.esRequest->notToKeep->resultSetItem->resultSetId =
-        nmem_strdup (p->odr_out->mem, str);
-    req->u.esRequest->notToKeep->resultSetItem->item =
-        (int *) odr_malloc(p->odr_out, sizeof(int));
+
+    if (!*str) 
+        req->u.esRequest->notToKeep->resultSetItem = 0;
+    else
+    {
+        req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
+           odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
+
+        req->u.esRequest->notToKeep->resultSetItem->resultSetId =
+           nmem_strdup (p->odr_out->mem, str);
+        req->u.esRequest->notToKeep->resultSetItem->item =
+            (int *) odr_malloc(p->odr_out, sizeof(int));
        
-    str = ZOOM_options_get(p->options, "itemorder-item");
-    *req->u.esRequest->notToKeep->resultSetItem->item =
-        (str ? atoi(str) : 1);
-    
+        str = ZOOM_options_get(p->options, "itemorder-item");
+        *req->u.esRequest->notToKeep->resultSetItem->item =
+            (str ? atoi(str) : 1);
+    }
     req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
     
     return req;
@@ -2201,13 +2281,13 @@ static int ZOOM_connection_exec_task (ZOOM_connection c)
         switch (task->which)
         {
         case ZOOM_TASK_SEARCH:
-            if (c->proto == PROTO_SRW)
+            if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_search(c);
             else
                 ret = ZOOM_connection_send_search(c);
             break;
         case ZOOM_TASK_RETRIEVE:
-            if (c->proto == PROTO_SRW)
+            if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_search(c);
             else
                 ret = send_present (c);
@@ -2376,13 +2456,14 @@ static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
     }
 }
 
-#if HAVE_XSLT
+#if HAVE_XML2
 static void handle_srw_response(ZOOM_connection c,
                                 Z_SRW_searchRetrieveResponse *res)
 {
     ZOOM_resultset resultset = 0;
     int i;
     NMEM nmem;
+    ZOOM_Event event;
 
     if (!c->tasks)
         return;
@@ -2394,9 +2475,12 @@ static void handle_srw_response(ZOOM_connection c,
     else
        return ;
 
+    event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
+    ZOOM_connection_put_event(c, event);
+
     resultset->size = 0;
 
-    yaz_log(LOG_LOG, "got SRW response OK");
+    yaz_log(LOG_DEBUG, "got SRW response OK");
     
     if (res->numberOfRecords)
         resultset->size = *res->numberOfRecords;
@@ -2430,7 +2514,6 @@ static void handle_srw_response(ZOOM_connection c,
             npr->u.databaseRecord->u.octet_aligned->size = 
             res->records[i].recordData_len;
         record_cache_add (resultset, npr, pos);
-        yaz_log(LOG_LOG, "add SRW record to cache to pos %d", pos);
     }
     if (res->num_diagnostics > 0)
     {
@@ -2443,7 +2526,7 @@ static void handle_srw_response(ZOOM_connection c,
 }
 #endif
 
-#if HAVE_XSLT
+#if HAVE_XML2
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 {
     int ret = -1;
@@ -2469,7 +2552,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         if (!ret && soap_package->which == Z_SOAP_generic &&
             soap_package->u.generic->no == 0)
         {
-            Z_SRW_searchRetrieve *sr = soap_package->u.generic->p;
+            Z_SRW_PDU *sr = soap_package->u.generic->p;
             if (sr->which == Z_SRW_searchRetrieve_response)
                 handle_srw_response(c, sr->u.response);
             else
@@ -2543,6 +2626,7 @@ static int do_read (ZOOM_connection c)
     {
         Z_GDU *gdu;
         ZOOM_Event event;
+
        odr_reset (c->odr_in);
        odr_setbuf (c->odr_in, c->buf_in, r, 0);
         event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
@@ -2557,7 +2641,7 @@ static int do_read (ZOOM_connection c)
            handle_apdu (c, gdu->u.z3950);
         else if (gdu->which == Z_GDU_HTTP_Response)
         {
-#if HAVE_XSLT
+#if HAVE_XML2
             handle_http (c, gdu->u.HTTP_Response);
 #else
             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
@@ -2587,7 +2671,7 @@ static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
             yaz_log (LOG_DEBUG, "reconnect write");
             c->tasks->running = 0;
             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
-            return zoom_complete;
+            return zoom_pending;
         }
        if (c->state == STATE_CONNECTING)
            set_ZOOM_error(c, ZOOM_ERROR_CONNECT, 0);
@@ -2714,7 +2798,7 @@ ZOOM_connection_error_x (ZOOM_connection c, const char **cp,
         else if (!strcmp(c->diagset, "Bib-1"))
             *cp = ZOOM_diag_str(error);
         else if (!strcmp(c->diagset, "SRW"))
-            *cp = yaz_srw_error_str(c->error);
+            *cp = yaz_diag_srw_str(c->error);
         else
             *cp = "Unknown error and diagnostic set";
     }