Fixed memory leak (setname in ZOOM_resultset)
[yaz-moved-to-github.git] / src / zoom-c.c
index 34f4c60..2c12c6a 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * Copyright (C) 1995-2007, Index Data ApS
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2008 Index Data
  * See the file LICENSE for details.
- *
- * $Id: zoom-c.c,v 1.154 2008-01-28 09:51:02 adam Exp $
  */
 /**
  * \file zoom-c.c
@@ -598,6 +596,13 @@ ZOOM_API(void)
         ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
 
     c->async = ZOOM_options_get_bool(c->options, "async", 0);
+    if (ZOOM_options_get_bool(c->options, "apdulog", 0))
+    {
+        c->odr_print = odr_createmem(ODR_PRINT);
+        odr_setprint(c->odr_print, yaz_log_file());
+    }
+    else
+        c->odr_print = 0;
     yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
  
     task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
@@ -776,6 +781,11 @@ ZOOM_API(void)
     xfree(c->diagset);
     odr_destroy(c->odr_in);
     odr_destroy(c->odr_out);
+    if (c->odr_print)
+    {
+        odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
+        odr_destroy(c->odr_print);
+    }
     ZOOM_options_destroy(c->options);
     ZOOM_connection_remove_tasks(c);
     ZOOM_connection_remove_events(c);
@@ -1303,6 +1313,8 @@ static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
         odr_reset(out);
         return -1;
     }
+    if (c->odr_print)
+        z_APDU(c->odr_print, &a, 0, 0);
     yaz_log(log_details, "%p encoding_APDU encoding OK", c);
     return 0;
 }
@@ -1431,6 +1443,8 @@ static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
     }
     if (!z_GDU(c->odr_out, &gdu, 0, 0))
         return zoom_complete;
+    if (c->odr_print)
+        z_GDU(c->odr_print, &gdu, 0, 0);
     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
         
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
@@ -1440,13 +1454,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) {
+#if YAZ_HAVE_XML2
+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;
 }
+#endif
 
 #if YAZ_HAVE_XML2
 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
@@ -1464,7 +1480,8 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
     {
     case ZOOM_TASK_SEARCH:
         resultset = c->tasks->u.search.resultset;
-        resultset->setname = xstrdup("default");
+        if (!resultset->setname)
+            resultset->setname = xstrdup("default");
         ZOOM_options_set(resultset->options, "setname", resultset->setname);
         start = &c->tasks->u.search.start;
         count = &c->tasks->u.search.count;
@@ -1474,7 +1491,7 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
 
         start = &c->tasks->u.retrieve.start;
         count = &c->tasks->u.retrieve.count;
-        
+
         if (*start >= resultset->size)
             return zoom_complete;
         if (*start + *count > resultset->size)
@@ -1525,7 +1542,8 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
     }
     sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
     sr->u.request->maximumRecords = odr_intdup(
-        c->odr_out, resultset->step>0 ? resultset->step : *count);
+        c->odr_out, (resultset->step > 0 && resultset->step < *count) ? 
+        resultset->step : *count);
     sr->u.request->recordSchema = resultset->schema;
     
     option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
@@ -2638,8 +2656,12 @@ static zoom_ret send_present(ZOOM_connection c)
         yaz_log(log_details, "%p send_present skip=%d", c, i);
 
     *req->resultSetStartPoint = *start + 1;
-    *req->numberOfRecordsRequested = resultset->step>0 ?
-        resultset->step : *count;
+
+    if (resultset->step > 0 && resultset->step < *count)
+        *req->numberOfRecordsRequested = resultset->step;
+    else
+        *req->numberOfRecordsRequested = *count;
+    
     if (*req->numberOfRecordsRequested + *start > resultset->size)
         *req->numberOfRecordsRequested = resultset->size - *start;
     assert(*req->numberOfRecordsRequested > 0);
@@ -3271,6 +3293,9 @@ static Z_APDU *create_update_package(ZOOM_package p)
     int record_len;
     const char *record_buf = ZOOM_options_getl(p->options, "record",
         &record_len);
+    int recordOpaque_len;
+    const char *recordOpaque_buf = ZOOM_options_getl(p->options, "recordOpaque",
+        &recordOpaque_len);
     const char *syntax_str = ZOOM_options_get(p->options, "syntax");
     const char *version = ZOOM_options_get(p->options, "updateVersion");
 
@@ -3286,7 +3311,7 @@ static Z_APDU *create_update_package(ZOOM_package p)
         version = "3";
     if (!syntax_str)
         syntax_str = "xml";
-    if (!record_buf)
+    if (!record_buf && !recordOpaque_buf)
     {
         record_buf = "void";
         record_len = 4;
@@ -3411,9 +3436,18 @@ static Z_APDU *create_update_package(ZOOM_package p)
         }
         else
             notToKeep->elements[0]->correlationInfo = 0;
-        notToKeep->elements[0]->record =
-            z_ext_record_oid(p->odr_out, syntax_oid,
-                             record_buf, record_len);
+        if (recordOpaque_buf)
+        {
+            notToKeep->elements[0]->record =
+                z_ext_record_oid_any(p->odr_out, syntax_oid,
+                                 recordOpaque_buf, recordOpaque_len);
+        }
+        else
+        {
+            notToKeep->elements[0]->record =
+                z_ext_record_oid(p->odr_out, syntax_oid,
+                                 record_buf, record_len);
+        }
     }
     if (0 && apdu)
     {
@@ -3642,22 +3676,90 @@ static zoom_ret send_sort_present(ZOOM_connection c)
     return r;
 }
 
+static int es_response_taskpackage_update(ZOOM_connection c,
+               Z_IUUpdateTaskPackage *utp)
+{
+       if (utp && utp->targetPart)
+       {
+               Z_IUTargetPart *targetPart = utp->targetPart;
+               switch ( *targetPart->updateStatus ) {
+                       case Z_IUTargetPart_success:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "success");
+                               break;
+                       case Z_IUTargetPart_partial:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "partial");
+                               break;
+                       case Z_IUTargetPart_failure:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "failure");
+                               if (targetPart->globalDiagnostics && targetPart->num_globalDiagnostics > 0)
+                                       response_diag(c, targetPart->globalDiagnostics[0]);
+                               break;
+               }
+               // NOTE: Individual record status, surrogate diagnostics, and supplemental diagnostics ARE NOT REPORTED.
+       }
+    return 1;
+}
+
+static int es_response_taskpackage(ZOOM_connection c,
+                                   Z_TaskPackage *taskPackage)
+{
+       // targetReference
+       Odr_oct *id = taskPackage->targetReference;
+       if (id)
+               ZOOM_options_setl(c->tasks->u.package->options,
+                                                       "targetReference", (char*) id->buf, id->len);
+       
+       // taskStatus
+       switch ( *taskPackage->taskStatus ) {
+               case Z_TaskPackage_pending:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "pending");
+                       break;
+               case Z_TaskPackage_active:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "active");
+                       break;
+               case Z_TaskPackage_complete:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "complete");
+                       break;
+               case Z_TaskPackage_aborted:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "aborted");
+                       if ( taskPackage->num_packageDiagnostics && taskPackage->packageDiagnostics )
+                               response_diag(c, taskPackage->packageDiagnostics[0]);
+                       break;
+       }
+       
+       // taskSpecificParameters
+       // NOTE: Only Update implemented, no others.
+       if ( taskPackage->taskSpecificParameters->which == Z_External_update ) {
+                       Z_IUUpdateTaskPackage *utp = taskPackage->taskSpecificParameters->u.update->u.taskPackage;
+                       es_response_taskpackage_update(c, utp);
+       }
+       return 1;
+}
+
+
 static int es_response(ZOOM_connection c,
                        Z_ExtendedServicesResponse *res)
 {
     if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
         return 0;
-    if (res->diagnostics && res->num_diagnostics > 0)
-        response_diag(c, res->diagnostics[0]);
+    switch (*res->operationStatus) {
+        case Z_ExtendedServicesResponse_done:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "done");
+            break;
+        case Z_ExtendedServicesResponse_accepted:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "accepted");
+            break;
+        case Z_ExtendedServicesResponse_failure:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "failure");
+            if (res->diagnostics && res->num_diagnostics > 0)
+                response_diag(c, res->diagnostics[0]);
+            break;
+    }
     if (res->taskPackage &&
         res->taskPackage->which == Z_External_extendedService)
     {
         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
-        Odr_oct *id = taskPackage->targetReference;
-        
-        if (id)
-            ZOOM_options_setl(c->tasks->u.package->options,
-                              "targetReference", (char*) id->buf, id->len);
+        es_response_taskpackage(c, taskPackage);
     }
     if (res->taskPackage && 
         res->taskPackage->which == Z_External_octet)
@@ -3844,35 +3946,37 @@ static void recv_apdu(ZOOM_connection c, Z_APDU *apdu)
 }
 
 #if YAZ_HAVE_XML2
-static void handle_srw_response(ZOOM_connection c,
-                                Z_SRW_searchRetrieveResponse *res)
+static zoom_ret handle_srw_response(ZOOM_connection c,
+                                    Z_SRW_searchRetrieveResponse *res)
 {
     ZOOM_resultset resultset = 0;
     int i;
     NMEM nmem;
     ZOOM_Event event;
-    int *start;
+    int *start, *count;
     const char *syntax, *elementSetName;
 
     if (!c->tasks)
-        return;
+        return zoom_complete;
 
     switch(c->tasks->which)
     {
     case ZOOM_TASK_SEARCH:
         resultset = c->tasks->u.search.resultset;
         start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
         syntax = c->tasks->u.search.syntax;
         elementSetName = c->tasks->u.search.elementSetName;        
         break;
     case ZOOM_TASK_RETRIEVE:
         resultset = c->tasks->u.retrieve.resultset;
         start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
         syntax = c->tasks->u.retrieve.syntax;
         elementSetName = c->tasks->u.retrieve.elementSetName;
         break;
     default:
-        return;
+        return zoom_complete;
     }
     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
     ZOOM_connection_put_event(c, event);
@@ -3934,11 +4038,22 @@ static void handle_srw_response(ZOOM_connection c,
         record_cache_add(resultset, npr, pos, syntax, elementSetName,
                          sru_rec->recordSchema, diag);
     }
-    if (res->num_diagnostics > 0)
-        set_SRU_error(c, &res->diagnostics[0]);
+    *count -= i;
+    *start += i;
+    if (*count + *start > resultset->size)
+        *count = resultset->size - *start;
+    if (*count < 0)
+        *count = 0;
+
     nmem = odr_extract_mem(c->odr_in);
     nmem_transfer(odr_getmem(resultset->odr), nmem);
     nmem_destroy(nmem);
+    
+    if (res->num_diagnostics > 0)
+        set_SRU_error(c, &res->diagnostics[0]);
+    else if (*count > 0)
+        return ZOOM_connection_srw_send_search(c);
+    return zoom_complete;
 }
 #endif
 
@@ -3968,6 +4083,7 @@ static void handle_srw_scan_response(ZOOM_connection c,
 #if YAZ_HAVE_XML2
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 {
+    zoom_ret cret = zoom_complete;
     int ret = -1;
     const char *addinfo = 0;
     const char *connection_head = z_HTTP_header_lookup(hres->headers,
@@ -3995,7 +4111,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 
             ZOOM_options_set(c->options, "sru_version", sr->srw_version);
             if (sr->which == Z_SRW_searchRetrieve_response)
-                handle_srw_response(c, sr->u.response);
+                cret = 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
@@ -4019,7 +4135,8 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
             set_ZOOM_error(c, ZOOM_ERROR_DECODE, addinfo);
         do_close(c);
     }
-    ZOOM_connection_remove_task(c);
+    if (cret == zoom_complete)
+        ZOOM_connection_remove_task(c);
     if (!strcmp(hres->version, "1.0"))
     {
         /* HTTP 1.0: only if Keep-Alive we stay alive.. */
@@ -4085,16 +4202,21 @@ static int do_read(ZOOM_connection c)
             }
             do_close(c);
         }
-        else if (gdu->which == Z_GDU_Z3950)
-            recv_apdu(c, gdu->u.z3950);
-        else if (gdu->which == Z_GDU_HTTP_Response)
+        else
         {
+            if (c->odr_print)
+                z_GDU(c->odr_print, &gdu, 0, 0);
+            if (gdu->which == Z_GDU_Z3950)
+                recv_apdu(c, gdu->u.z3950);
+            else if (gdu->which == Z_GDU_HTTP_Response)
+            {
 #if YAZ_HAVE_XML2
-            handle_http(c, gdu->u.HTTP_Response);
+                handle_http(c, gdu->u.HTTP_Response);
 #else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-            do_close(c);
+                set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
+                do_close(c);
 #endif
+            }
         }
         c->reconnect_ok = 0;
     }