* 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. */
/* 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 */
}
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);
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_cat(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_cat(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_cat(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_cat(result, &ssize, &offset, US "\"\"", 2);
- }
-
- else if (buffer[0] == 0 || Ustrchr(buffer, ' ') != NULL) {
- int j;
- result =
- string_cat(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_cat(result, &ssize, &offset, US "\"", 1);
- } else {
- result =
- string_cat(result, &ssize, &offset, US buffer,
- len);
- }
- result = string_cat(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 sucessful result */
+/* 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 */
+ }
}
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;
+uschar buffer[512];
- if (ibase_servers == NULL)
- *errmsg = US "no Interbase servers defined (ibase_servers option)";
+/* Keep picky compilers happy */
+do_cache = do_cache;
- return DEFER;
+DEBUG(D_lookup) debug_printf_indent("Interbase query: %s\n", query);
+
+while ((server = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+ {
+ 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;
}
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) {
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