consification
[exim.git] / src / src / lookups / ibase.c
index b29fccca78cbcb8d790fe3b6b6843feca3269d7d..72a27c0109388c5e5a0b34391730ca18a58309f2 100644 (file)
@@ -2,7 +2,8 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* The code in this module was contributed by Ard Biesheuvel. */
@@ -31,9 +32,9 @@ static ibase_connection *ibase_connections = NULL;
 
 /* See local README for interface description. */
 
-static void *ibase_open(uschar * filename, uschar ** errmsg)
+static void *ibase_open(const uschar * filename, uschar ** errmsg)
 {
-    return (void *) (1);        /* Just return something non-null */
+return (void *) (1);        /* Just return something non-null */
 }
 
 
@@ -51,7 +52,7 @@ static void ibase_tidy(void)
 
     while ((cn = ibase_connections) != NULL) {
         ibase_connections = cn->next;
-        DEBUG(D_lookup) debug_printf("close Interbase connection: %s\n",
+        DEBUG(D_lookup) debug_printf_indent("close Interbase connection: %s\n",
                                      cn->server);
         isc_commit_transaction(status, &cn->transh);
         isc_detach_database(status, &cn->dbh);
@@ -109,332 +110,333 @@ static int
 perform_ibase_search(uschar * query, uschar * server, uschar ** resultptr,
                      uschar ** errmsg, BOOL * defer_break)
 {
-    isc_stmt_handle stmth = NULL;
-    XSQLDA *out_sqlda;
-    XSQLVAR *var;
-
-    char buffer[256];
-    ISC_STATUS status[20], *statusp = status;
-
-    int i;
-    int ssize = 0;
-    int offset = 0;
-    int yield = DEFER;
-    uschar *result = NULL;
-    ibase_connection *cn;
-    uschar *server_copy = NULL;
-    uschar *sdata[3];
+isc_stmt_handle stmth = NULL;
+XSQLDA *out_sqlda;
+XSQLVAR *var;
+int i;
+rmark reset_point;
+
+char buffer[256];
+ISC_STATUS status[20], *statusp = status;
+
+gstring * result;
+int yield = DEFER;
+ibase_connection *cn;
+uschar *server_copy = NULL;
+uschar *sdata[3];
 
 /* Disaggregate the parameters from the server argument. The order is host,
 database, user, password. We can write to the string, since it is in a
 nextinlist temporary buffer. The copy of the string that is used for caching
 has the password removed. This copy is also used for debugging output. */
 
-    for (i = 2; i > 0; i--) {
-        uschar *pp = Ustrrchr(server, '|');
-        if (pp == NULL) {
-            *errmsg =
-                string_sprintf("incomplete Interbase server data: %s",
-                               (i == 3) ? server : server_copy);
-            *defer_break = TRUE;
-            return DEFER;
-        }
-        *pp++ = 0;
-        sdata[i] = pp;
-        if (i == 2)
-            server_copy = string_copy(server);   /* sans password */
+for (int i = 2; i > 0; i--)
+  {
+  uschar *pp = Ustrrchr(server, '|');
+
+  if (pp == NULL)
+    {
+    *errmsg = string_sprintf("incomplete Interbase server data: %s",
+                      (i == 3) ? server : server_copy);
+    *defer_break = TRUE;
+    return DEFER;
     }
-    sdata[0] = server;          /* What's left at the start */
+  *pp++ = 0;
+  sdata[i] = pp;
+  if (i == 2)
+      server_copy = string_copy(server);   /* sans password */
+  }
+sdata[0] = server;          /* What's left at the start */
 
 /* See if we have a cached connection to the server */
 
-    for (cn = ibase_connections; cn != NULL; cn = cn->next) {
-        if (Ustrcmp(cn->server, server_copy) == 0) {
-            break;
-        }
-    }
+for (cn = ibase_connections; cn != NULL; cn = cn->next)
+  if (Ustrcmp(cn->server, server_copy) == 0)
+    break;
 
 /* Use a previously cached connection ? */
 
-    if (cn != NULL) {
-        static char db_info_options[] = { isc_info_base_level };
-
-        /* test if the connection is alive */
-        if (isc_database_info
-            (status, &cn->dbh, sizeof(db_info_options), db_info_options,
-             sizeof(buffer), buffer)) {
-            /* error occurred: assume connection is down */
-            DEBUG(D_lookup)
-                debug_printf
-                ("Interbase cleaning up cached connection: %s\n",
-                 cn->server);
-            isc_detach_database(status, &cn->dbh);
-        } else {
-            DEBUG(D_lookup)
-                debug_printf("Interbase using cached connection for %s\n",
-                             server_copy);
-        }
-    } else {
-        cn = store_get(sizeof(ibase_connection));
-        cn->server = server_copy;
-        cn->dbh = NULL;
-        cn->transh = NULL;
-        cn->next = ibase_connections;
-        ibase_connections = cn;
+if (cn)
+  {
+  static char db_info_options[] = { isc_info_base_level };
+
+  /* test if the connection is alive */
+  if (isc_database_info(status, &cn->dbh, sizeof(db_info_options),
+       db_info_options, sizeof(buffer), buffer))
+    {
+    /* error occurred: assume connection is down */
+    DEBUG(D_lookup)
+       debug_printf
+       ("Interbase cleaning up cached connection: %s\n",
+        cn->server);
+    isc_detach_database(status, &cn->dbh);
     }
+  else
+    DEBUG(D_lookup) debug_printf_indent("Interbase using cached connection for %s\n",
+                    server_copy);
+  }
+else
+  {
+  cn = store_get(sizeof(ibase_connection), FALSE);
+  cn->server = server_copy;
+  cn->dbh = NULL;
+  cn->transh = NULL;
+  cn->next = ibase_connections;
+  ibase_connections = cn;
+  }
 
 /* If no cached connection, we must set one up. */
 
-    if (cn->dbh == NULL || cn->transh == NULL) {
-
-        char *dpb, *p;
-        short dpb_length;
-        static char trans_options[] =
-            { isc_tpb_version3, isc_tpb_read, isc_tpb_read_committed,
-            isc_tpb_rec_version
-        };
-
-        /* Construct the database parameter buffer. */
-        dpb = buffer;
-        *dpb++ = isc_dpb_version1;
-        *dpb++ = isc_dpb_user_name;
-        *dpb++ = strlen(sdata[1]);
-        for (p = sdata[1]; *p;)
-            *dpb++ = *p++;
-        *dpb++ = isc_dpb_password;
-        *dpb++ = strlen(sdata[2]);
-        for (p = sdata[2]; *p;)
-            *dpb++ = *p++;
-        dpb_length = dpb - buffer;
-
-        DEBUG(D_lookup)
-            debug_printf("new Interbase connection: database=%s user=%s\n",
-                         sdata[0], sdata[1]);
-
-        /* Connect to the database */
-        if (isc_attach_database
-            (status, 0, sdata[0], &cn->dbh, dpb_length, buffer)) {
-            isc_interprete(buffer, &statusp);
-            *errmsg =
-                string_sprintf("Interbase attach() failed: %s", buffer);
-            *defer_break = FALSE;
-            goto IBASE_EXIT;
-        }
-
-        /* Now start a read-only read-committed transaction */
-        if (isc_start_transaction
-            (status, &cn->transh, 1, &cn->dbh, sizeof(trans_options),
-             trans_options)) {
-            isc_interprete(buffer, &statusp);
-            isc_detach_database(status, &cn->dbh);
-            *errmsg =
-                string_sprintf("Interbase start_transaction() failed: %s",
-                               buffer);
-            *defer_break = FALSE;
-            goto IBASE_EXIT;
-        }
+if (cn->dbh == NULL || cn->transh == NULL)
+  {
+  char *dpb;
+  short dpb_length;
+  static char trans_options[] =
+      { isc_tpb_version3, isc_tpb_read, isc_tpb_read_committed,
+      isc_tpb_rec_version
+  };
+
+  /* Construct the database parameter buffer. */
+  dpb = buffer;
+  *dpb++ = isc_dpb_version1;
+  *dpb++ = isc_dpb_user_name;
+  *dpb++ = strlen(sdata[1]);
+  for (char * p = sdata[1]; *p;)
+      *dpb++ = *p++;
+  *dpb++ = isc_dpb_password;
+  *dpb++ = strlen(sdata[2]);
+  for (char * p = sdata[2]; *p;)
+      *dpb++ = *p++;
+  dpb_length = dpb - buffer;
+
+  DEBUG(D_lookup)
+      debug_printf_indent("new Interbase connection: database=%s user=%s\n",
+                  sdata[0], sdata[1]);
+
+  /* Connect to the database */
+  if (isc_attach_database
+      (status, 0, sdata[0], &cn->dbh, dpb_length, buffer))
+    {
+    isc_interprete(buffer, &statusp);
+    *errmsg =
+       string_sprintf("Interbase attach() failed: %s", buffer);
+    *defer_break = FALSE;
+    goto IBASE_EXIT;
     }
 
-/* Run the query */
-    if (isc_dsql_allocate_statement(status, &cn->dbh, &stmth)) {
-        isc_interprete(buffer, &statusp);
-        *errmsg =
-            string_sprintf("Interbase alloc_statement() failed: %s",
-                           buffer);
-        *defer_break = FALSE;
-        goto IBASE_EXIT;
+  /* Now start a read-only read-committed transaction */
+  if (isc_start_transaction
+      (status, &cn->transh, 1, &cn->dbh, sizeof(trans_options),
+       trans_options))
+    {
+    isc_interprete(buffer, &statusp);
+    isc_detach_database(status, &cn->dbh);
+    *errmsg =
+       string_sprintf("Interbase start_transaction() failed: %s",
+                      buffer);
+    *defer_break = FALSE;
+    goto IBASE_EXIT;
     }
+  }
 
-    out_sqlda = store_get(XSQLDA_LENGTH(1));
-    out_sqlda->version = SQLDA_VERSION1;
-    out_sqlda->sqln = 1;
-
-    if (isc_dsql_prepare
-        (status, &cn->transh, &stmth, 0, query, 1, out_sqlda)) {
-        isc_interprete(buffer, &statusp);
-        store_reset(out_sqlda);
-        out_sqlda = NULL;
-        *errmsg =
-            string_sprintf("Interbase prepare_statement() failed: %s",
-                           buffer);
-        *defer_break = FALSE;
-        goto IBASE_EXIT;
-    }
+/* Run the query */
+if (isc_dsql_allocate_statement(status, &cn->dbh, &stmth))
+  {
+  isc_interprete(buffer, &statusp);
+  *errmsg =
+      string_sprintf("Interbase alloc_statement() failed: %s",
+                    buffer);
+  *defer_break = FALSE;
+  goto IBASE_EXIT;
+  }
+
+/* Lacking any information, assume that the data is untainted */
+reset_point = store_mark();
+out_sqlda = store_get(XSQLDA_LENGTH(1), FALSE);
+out_sqlda->version = SQLDA_VERSION1;
+out_sqlda->sqln = 1;
+
+if (isc_dsql_prepare
+    (status, &cn->transh, &stmth, 0, query, 1, out_sqlda))
+  {
+  isc_interprete(buffer, &statusp);
+  reset_point = store_reset(reset_point);
+  out_sqlda = NULL;
+  *errmsg =
+      string_sprintf("Interbase prepare_statement() failed: %s",
+                    buffer);
+  *defer_break = FALSE;
+  goto IBASE_EXIT;
+  }
 
 /* re-allocate the output structure if there's more than one field */
-    if (out_sqlda->sqln < out_sqlda->sqld) {
-        XSQLDA *new_sqlda = store_get(XSQLDA_LENGTH(out_sqlda->sqld));
-        if (isc_dsql_describe
-            (status, &stmth, out_sqlda->version, new_sqlda)) {
-            isc_interprete(buffer, &statusp);
-            isc_dsql_free_statement(status, &stmth, DSQL_drop);
-            store_reset(out_sqlda);
-            out_sqlda = NULL;
-            *errmsg =
-                string_sprintf("Interbase describe_statement() failed: %s",
-                               buffer);
-            *defer_break = FALSE;
-            goto IBASE_EXIT;
-        }
-        out_sqlda = new_sqlda;
+if (out_sqlda->sqln < out_sqlda->sqld)
+  {
+  XSQLDA *new_sqlda = store_get(XSQLDA_LENGTH(out_sqlda->sqld), FALSE);
+  if (isc_dsql_describe
+      (status, &stmth, out_sqlda->version, new_sqlda))
+    {
+    isc_interprete(buffer, &statusp);
+    isc_dsql_free_statement(status, &stmth, DSQL_drop);
+    reset_point = store_reset(reset_point);
+    out_sqlda = NULL;
+    *errmsg = string_sprintf("Interbase describe_statement() failed: %s",
+                      buffer);
+    *defer_break = FALSE;
+    goto IBASE_EXIT;
     }
+  out_sqlda = new_sqlda;
+  }
 
 /* allocate storage for every returned field */
-    for (i = 0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++) {
-        switch (var->sqltype & ~1) {
-        case SQL_VARYING:
-            var->sqldata =
-                (char *) store_get(sizeof(char) * var->sqllen + 2);
-            break;
-        case SQL_TEXT:
-            var->sqldata =
-                (char *) store_get(sizeof(char) * var->sqllen);
-            break;
-        case SQL_SHORT:
-            var->sqldata = (char *) store_get(sizeof(short));
-            break;
-        case SQL_LONG:
-            var->sqldata = (char *) store_get(sizeof(ISC_LONG));
-            break;
+for (i = 0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++)
+  {
+  switch (var->sqltype & ~1)
+    {
+    case SQL_VARYING:
+       var->sqldata = CS store_get(sizeof(char) * var->sqllen + 2, FALSE);
+       break;
+    case SQL_TEXT:
+       var->sqldata = CS store_get(sizeof(char) * var->sqllen, FALSE);
+       break;
+    case SQL_SHORT:
+       var->sqldata = CS  store_get(sizeof(short), FALSE);
+       break;
+    case SQL_LONG:
+       var->sqldata = CS  store_get(sizeof(ISC_LONG), FALSE);
+       break;
 #ifdef SQL_INT64
-        case SQL_INT64:
-            var->sqldata = (char *) store_get(sizeof(ISC_INT64));
-            break;
+    case SQL_INT64:
+       var->sqldata = CS  store_get(sizeof(ISC_INT64), FALSE);
+       break;
 #endif
-        case SQL_FLOAT:
-            var->sqldata = (char *) store_get(sizeof(float));
-            break;
-        case SQL_DOUBLE:
-            var->sqldata = (char *) store_get(sizeof(double));
-            break;
+    case SQL_FLOAT:
+       var->sqldata = CS  store_get(sizeof(float), FALSE);
+       break;
+    case SQL_DOUBLE:
+       var->sqldata = CS  store_get(sizeof(double), FALSE);
+       break;
 #ifdef SQL_TIMESTAMP
-        case SQL_DATE:
-            var->sqldata = (char *) store_get(sizeof(ISC_QUAD));
-            break;
+    case SQL_DATE:
+       var->sqldata = CS  store_get(sizeof(ISC_QUAD), FALSE);
+       break;
 #else
-        case SQL_TIMESTAMP:
-            var->sqldata = (char *) store_get(sizeof(ISC_TIMESTAMP));
-            break;
-        case SQL_TYPE_DATE:
-            var->sqldata = (char *) store_get(sizeof(ISC_DATE));
-            break;
-        case SQL_TYPE_TIME:
-            var->sqldata = (char *) store_get(sizeof(ISC_TIME));
-            break;
-#endif
-        }
-        if (var->sqltype & 1) {
-            var->sqlind = (short *) store_get(sizeof(short));
-        }
+    case SQL_TIMESTAMP:
+       var->sqldata = CS  store_get(sizeof(ISC_TIMESTAMP), FALSE);
+       break;
+    case SQL_TYPE_DATE:
+       var->sqldata = CS  store_get(sizeof(ISC_DATE), FALSE);
+       break;
+    case SQL_TYPE_TIME:
+       var->sqldata = CS  store_get(sizeof(ISC_TIME), FALSE);
+       break;
+  #endif
     }
-
-    /* finally, we're ready to execute the statement */
-    if (isc_dsql_execute
-        (status, &cn->transh, &stmth, out_sqlda->version, NULL)) {
-        isc_interprete(buffer, &statusp);
-        *errmsg =
-            string_sprintf("Interbase describe_statement() failed: %s",
-                           buffer);
-        isc_dsql_free_statement(status, &stmth, DSQL_drop);
-        *defer_break = FALSE;
-        goto IBASE_EXIT;
+  if (var->sqltype & 1)
+    var->sqlind = (short *) store_get(sizeof(short), FALSE);
+  }
+
+/* finally, we're ready to execute the statement */
+if (isc_dsql_execute
+    (status, &cn->transh, &stmth, out_sqlda->version, NULL))
+  {
+  isc_interprete(buffer, &statusp);
+  *errmsg = string_sprintf("Interbase describe_statement() failed: %s",
+                    buffer);
+  isc_dsql_free_statement(status, &stmth, DSQL_drop);
+  *defer_break = FALSE;
+  goto IBASE_EXIT;
+  }
+
+while (isc_dsql_fetch(status, &stmth, out_sqlda->version, out_sqlda) != 100L)
+  {
+  /* check if an error occurred */
+  if (status[0] & status[1])
+    {
+    isc_interprete(buffer, &statusp);
+    *errmsg =
+       string_sprintf("Interbase fetch() failed: %s", buffer);
+    isc_dsql_free_statement(status, &stmth, DSQL_drop);
+    *defer_break = FALSE;
+    goto IBASE_EXIT;
     }
 
-    while (isc_dsql_fetch(status, &stmth, out_sqlda->version, out_sqlda) !=
-           100L) {
-        /* check if an error occurred */
-        if (status[0] & status[1]) {
-            isc_interprete(buffer, &statusp);
-            *errmsg =
-                string_sprintf("Interbase fetch() failed: %s", buffer);
-            isc_dsql_free_statement(status, &stmth, DSQL_drop);
-            *defer_break = FALSE;
-            goto IBASE_EXIT;
-        }
-
-        if (result != NULL)
-            result = string_catn(result, &ssize, &offset, US "\n", 1);
-
-        /* Find the number of fields returned. If this is one, we don't add field
-           names to the data. Otherwise we do. */
-        if (out_sqlda->sqld == 1) {
-            if (out_sqlda->sqlvar[0].sqlind == NULL || *out_sqlda->sqlvar[0].sqlind != -1)     /* NULL value yields nothing */
-                result =
-                    string_catn(result, &ssize, &offset, US buffer,
-                               fetch_field(buffer, sizeof(buffer),
-                                           &out_sqlda->sqlvar[0]));
-        }
-
-        else
-            for (i = 0; i < out_sqlda->sqld; i++) {
-                int len = fetch_field(buffer, sizeof(buffer),
-                                      &out_sqlda->sqlvar[i]);
-
-                result =
-                    string_cat(result, &ssize, &offset,
-                               US out_sqlda->sqlvar[i].aliasname,
-                               out_sqlda->sqlvar[i].aliasname_length);
-                result = string_catn(result, &ssize, &offset, US "=", 1);
-
-                /* Quote the value if it contains spaces or is empty */
-
-                if (*out_sqlda->sqlvar[i].sqlind == -1) {       /* NULL value */
-                    result =
-                        string_catn(result, &ssize, &offset, US "\"\"", 2);
-                }
-
-                else if (buffer[0] == 0 || Ustrchr(buffer, ' ') != NULL) {
-                    int j;
-                    result =
-                        string_catn(result, &ssize, &offset, US "\"", 1);
-                    for (j = 0; j < len; j++) {
-                        if (buffer[j] == '\"' || buffer[j] == '\\')
-                            result =
-                                string_cat(result, &ssize, &offset,
-                                           US "\\", 1);
-                        result =
-                            string_cat(result, &ssize, &offset,
-                                       US buffer + j, 1);
-                    }
-                    result =
-                        string_catn(result, &ssize, &offset, US "\"", 1);
-                } else {
-                    result =
-                        string_catn(result, &ssize, &offset, US buffer, len);
-                }
-                result = string_catn(result, &ssize, &offset, US " ", 1);
-            }
+  if (result)
+    result = string_catn(result, US "\n", 1);
+
+  /* Find the number of fields returned. If this is one, we don't add field
+     names to the data. Otherwise we do. */
+  if (out_sqlda->sqld == 1)
+    {
+    if (out_sqlda->sqlvar[0].sqlind == NULL || *out_sqlda->sqlvar[0].sqlind != -1)     /* NULL value yields nothing */
+      result = string_catn(result, US buffer,
+                      fetch_field(buffer, sizeof(buffer),
+                                  &out_sqlda->sqlvar[0]));
     }
 
+  else
+    for (int i = 0; i < out_sqlda->sqld; i++)
+      {
+      int len = fetch_field(buffer, sizeof(buffer), &out_sqlda->sqlvar[i]);
+
+      result = string_catn(result, US out_sqlda->sqlvar[i].aliasname,
+                    out_sqlda->sqlvar[i].aliasname_length);
+      result = string_catn(result, US "=", 1);
+
+      /* Quote the value if it contains spaces or is empty */
+
+      if (*out_sqlda->sqlvar[i].sqlind == -1)       /* NULL value */
+       result = string_catn(result, US "\"\"", 2);
+
+      else if (buffer[0] == 0 || Ustrchr(buffer, ' ') != NULL)
+       {
+       result = string_catn(result, US "\"", 1);
+       for (int j = 0; j < len; j++)
+         {
+         if (buffer[j] == '\"' || buffer[j] == '\\')
+             result = string_cat(result, US "\\", 1);
+         result = string_cat(result, US buffer + j, 1);
+         }
+       result = string_catn(result, US "\"", 1);
+       }
+      else
+       result = string_catn(result, US buffer, len);
+      result = string_catn(result, US " ", 1);
+      }
+  }
+
 /* If result is NULL then no data has been found and so we return FAIL.
 Otherwise, we must terminate the string which has been built; string_cat()
 always leaves enough room for a terminating zero. */
 
-    if (result == NULL) {
-        yield = FAIL;
-        *errmsg = US "Interbase: no data found";
-    } else {
-        result[offset] = 0;
-        store_reset(result + offset + 1);
-    }
+if (!result)
+  {
+  yield = FAIL;
+  *errmsg = US "Interbase: no data found";
+  }
+else
+  gstring_release_unused(result);
 
 
 /* Get here by goto from various error checks. */
 
-  IBASE_EXIT:
+IBASE_EXIT:
 
-    if (stmth != NULL)
-        isc_dsql_free_statement(status, &stmth, DSQL_drop);
+if (stmth)
+  isc_dsql_free_statement(status, &stmth, DSQL_drop);
 
 /* Non-NULL result indicates a successful result */
 
-    if (result != NULL) {
-        *resultptr = result;
-        return OK;
-    } else {
-        DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
-        return yield;           /* FAIL or DEFER */
-    }
+if (result)
+  {
+  *resultptr = string_from_gstring(result);
+  return OK;
+  }
+else
+  {
+  DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
+  return yield;           /* FAIL or DEFER */
+  }
 }
 
 
@@ -449,33 +451,27 @@ arguments are not used. Loop through a list of servers while the query is
 deferred with a retryable error. */
 
 static int
-ibase_find(void *handle, uschar * filename, uschar * query, int length,
-           uschar ** result, uschar ** errmsg, uint *do_cache)
+ibase_find(void * handle, const uschar * filename, uschar * query, int length,
+  uschar ** result, uschar ** errmsg, uint * do_cache, const uschar * opts)
 {
-    int sep = 0;
-    uschar *server;
-    uschar *list = ibase_servers;
-    uschar buffer[512];
-
-    /* Keep picky compilers happy */
-    do_cache = do_cache;
-
-    DEBUG(D_lookup) debug_printf("Interbase query: %s\n", query);
-
-    while ((server =
-            string_nextinlist(&list, &sep, buffer,
-                              sizeof(buffer))) != NULL) {
-        BOOL defer_break = FALSE;
-        int rc = perform_ibase_search(query, server, result, errmsg,
-                                      &defer_break);
-        if (rc != DEFER || defer_break)
-            return rc;
-    }
+int sep = 0;
+uschar *server;
+uschar *list = ibase_servers;
 
-    if (ibase_servers == NULL)
-        *errmsg = US "no Interbase servers defined (ibase_servers option)";
+DEBUG(D_lookup) debug_printf_indent("Interbase query: %s\n", query);
 
-    return DEFER;
+while ((server = string_nextinlist(&list, &sep, NULL, 0)))
+  {
+  BOOL defer_break = FALSE;
+  int rc = perform_ibase_search(query, server, result, errmsg, &defer_break);
+  if (rc != DEFER || defer_break)
+    return rc;
+  }
+
+if (!ibase_servers)
+  *errmsg = US "no Interbase servers defined (ibase_servers option)";
+
+return DEFER;
 }
 
 
@@ -516,7 +512,7 @@ static uschar *ibase_quote(uschar * s, uschar * opt)
 
     if (count == 0)
         return s;
-    t = quoted = store_get(Ustrlen(s) + count + 1);
+    t = quoted = store_get(Ustrlen(s) + count + 1, FALSE);
 
     while ((c = *s++) != 0) {
         if (Ustrchr("'", c) != NULL) {
@@ -562,15 +558,15 @@ fprintf(f, "Library version: ibase: Exim version %s\n", EXIM_VERSION_STR);
 
 
 static lookup_info _lookup_info = {
-  US"ibase",                     /* lookup name */
-  lookup_querystyle,             /* query-style lookup */
-  ibase_open,                    /* open function */
-  NULL,                          /* no check function */
-  ibase_find,                    /* find function */
-  NULL,                          /* no close function */
-  ibase_tidy,                    /* tidy function */
-  ibase_quote,                   /* quoting function */
-  ibase_version_report           /* version reporting */
+  .name = US"ibase",                   /* lookup name */
+  .type = lookup_querystyle,           /* query-style lookup */
+  .open = ibase_open,                  /* open function */
+  .check NULL,                         /* no check function */
+  .find = ibase_find,                  /* find function */
+  .close = NULL,                       /* no close function */
+  .tidy = ibase_tidy,                  /* tidy function */
+  .quote = ibase_quote,                        /* quoting function */
+  .version_report = ibase_version_report           /* version reporting */
 };
 
 #ifdef DYNLOOKUP