Report compiler in -d -bV. Clang compat.
[exim.git] / src / src / lookups / ibase.c
1 /* $Cambridge: exim/src/src/lookups/ibase.c,v 1.5 2009/11/16 19:50:38 nm4 Exp $ */
2
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge 1995 - 2009 */
8 /* See the file NOTICE for conditions of use and distribution. */
9
10 /* The code in this module was contributed by Ard Biesheuvel. */
11
12 #include "../exim.h"
13 #include "lf_functions.h"
14
15 #include <ibase.h> /* The system header */
16
17 /* Structure and anchor for caching connections. */
18
19 typedef struct ibase_connection {
20 struct ibase_connection *next;
21 uschar *server;
22 isc_db_handle dbh;
23 isc_tr_handle transh;
24 } ibase_connection;
25
26 static ibase_connection *ibase_connections = NULL;
27
28
29
30 /*************************************************
31 * Open entry point *
32 *************************************************/
33
34 /* See local README for interface description. */
35
36 static void *ibase_open(uschar * filename, uschar ** errmsg)
37 {
38 return (void *) (1); /* Just return something non-null */
39 }
40
41
42
43 /*************************************************
44 * Tidy entry point *
45 *************************************************/
46
47 /* See local README for interface description. */
48
49 static void ibase_tidy(void)
50 {
51 ibase_connection *cn;
52 ISC_STATUS status[20];
53
54 while ((cn = ibase_connections) != NULL) {
55 ibase_connections = cn->next;
56 DEBUG(D_lookup) debug_printf("close Interbase connection: %s\n",
57 cn->server);
58 isc_commit_transaction(status, &cn->transh);
59 isc_detach_database(status, &cn->dbh);
60 }
61 }
62
63 static int fetch_field(char *buffer, int buffer_size, XSQLVAR * var)
64 {
65 if (buffer_size < var->sqllen)
66 return 0;
67
68 switch (var->sqltype & ~1) {
69 case SQL_VARYING:
70 strncpy(buffer, &var->sqldata[2], *(short *) var->sqldata);
71 return *(short *) var->sqldata;
72 case SQL_TEXT:
73 strncpy(buffer, var->sqldata, var->sqllen);
74 return var->sqllen;
75 case SQL_SHORT:
76 return sprintf(buffer, "%d", *(short *) var->sqldata);
77 case SQL_LONG:
78 return sprintf(buffer, "%ld", *(ISC_LONG *) var->sqldata);
79 #ifdef SQL_INT64
80 case SQL_INT64:
81 return sprintf(buffer, "%lld", *(ISC_INT64 *) var->sqldata);
82 #endif
83 default:
84 /* not implemented */
85 return 0;
86 }
87 }
88
89 /*************************************************
90 * Internal search function *
91 *************************************************/
92
93 /* This function is called from the find entry point to do the search for a
94 single server.
95
96 Arguments:
97 query the query string
98 server the server string
99 resultptr where to store the result
100 errmsg where to point an error message
101 defer_break TRUE if no more servers are to be tried after DEFER
102
103 The server string is of the form "host:dbname|user|password". The host can be
104 host:port. This string is in a nextinlist temporary buffer, so can be
105 overwritten.
106
107 Returns: OK, FAIL, or DEFER
108 */
109
110 static int
111 perform_ibase_search(uschar * query, uschar * server, uschar ** resultptr,
112 uschar ** errmsg, BOOL * defer_break)
113 {
114 isc_stmt_handle stmth = NULL;
115 XSQLDA *out_sqlda;
116 XSQLVAR *var;
117
118 char buffer[256];
119 ISC_STATUS status[20], *statusp = status;
120
121 int i;
122 int ssize = 0;
123 int offset = 0;
124 int yield = DEFER;
125 uschar *result = NULL;
126 ibase_connection *cn;
127 uschar *server_copy = NULL;
128 uschar *sdata[3];
129
130 /* Disaggregate the parameters from the server argument. The order is host,
131 database, user, password. We can write to the string, since it is in a
132 nextinlist temporary buffer. The copy of the string that is used for caching
133 has the password removed. This copy is also used for debugging output. */
134
135 for (i = 2; i > 0; i--) {
136 uschar *pp = Ustrrchr(server, '|');
137 if (pp == NULL) {
138 *errmsg =
139 string_sprintf("incomplete Interbase server data: %s",
140 (i == 3) ? server : server_copy);
141 *defer_break = TRUE;
142 return DEFER;
143 }
144 *pp++ = 0;
145 sdata[i] = pp;
146 if (i == 2)
147 server_copy = string_copy(server); /* sans password */
148 }
149 sdata[0] = server; /* What's left at the start */
150
151 /* See if we have a cached connection to the server */
152
153 for (cn = ibase_connections; cn != NULL; cn = cn->next) {
154 if (Ustrcmp(cn->server, server_copy) == 0) {
155 break;
156 }
157 }
158
159 /* Use a previously cached connection ? */
160
161 if (cn != NULL) {
162 static char db_info_options[] = { isc_info_base_level };
163
164 /* test if the connection is alive */
165 if (isc_database_info
166 (status, &cn->dbh, sizeof(db_info_options), db_info_options,
167 sizeof(buffer), buffer)) {
168 /* error occurred: assume connection is down */
169 DEBUG(D_lookup)
170 debug_printf
171 ("Interbase cleaning up cached connection: %s\n",
172 cn->server);
173 isc_detach_database(status, &cn->dbh);
174 } else {
175 DEBUG(D_lookup)
176 debug_printf("Interbase using cached connection for %s\n",
177 server_copy);
178 }
179 } else {
180 cn = store_get(sizeof(ibase_connection));
181 cn->server = server_copy;
182 cn->dbh = NULL;
183 cn->transh = NULL;
184 cn->next = ibase_connections;
185 ibase_connections = cn;
186 }
187
188 /* If no cached connection, we must set one up. */
189
190 if (cn->dbh == NULL || cn->transh == NULL) {
191
192 char *dpb, *p;
193 short dpb_length;
194 static char trans_options[] =
195 { isc_tpb_version3, isc_tpb_read, isc_tpb_read_committed,
196 isc_tpb_rec_version
197 };
198
199 /* Construct the database parameter buffer. */
200 dpb = buffer;
201 *dpb++ = isc_dpb_version1;
202 *dpb++ = isc_dpb_user_name;
203 *dpb++ = strlen(sdata[1]);
204 for (p = sdata[1]; *p;)
205 *dpb++ = *p++;
206 *dpb++ = isc_dpb_password;
207 *dpb++ = strlen(sdata[2]);
208 for (p = sdata[2]; *p;)
209 *dpb++ = *p++;
210 dpb_length = dpb - buffer;
211
212 DEBUG(D_lookup)
213 debug_printf("new Interbase connection: database=%s user=%s\n",
214 sdata[0], sdata[1]);
215
216 /* Connect to the database */
217 if (isc_attach_database
218 (status, 0, sdata[0], &cn->dbh, dpb_length, buffer)) {
219 isc_interprete(buffer, &statusp);
220 *errmsg =
221 string_sprintf("Interbase attach() failed: %s", buffer);
222 *defer_break = FALSE;
223 goto IBASE_EXIT;
224 }
225
226 /* Now start a read-only read-committed transaction */
227 if (isc_start_transaction
228 (status, &cn->transh, 1, &cn->dbh, sizeof(trans_options),
229 trans_options)) {
230 isc_interprete(buffer, &statusp);
231 isc_detach_database(status, &cn->dbh);
232 *errmsg =
233 string_sprintf("Interbase start_transaction() failed: %s",
234 buffer);
235 *defer_break = FALSE;
236 goto IBASE_EXIT;
237 }
238 }
239
240 /* Run the query */
241 if (isc_dsql_allocate_statement(status, &cn->dbh, &stmth)) {
242 isc_interprete(buffer, &statusp);
243 *errmsg =
244 string_sprintf("Interbase alloc_statement() failed: %s",
245 buffer);
246 *defer_break = FALSE;
247 goto IBASE_EXIT;
248 }
249
250 out_sqlda = store_get(XSQLDA_LENGTH(1));
251 out_sqlda->version = SQLDA_VERSION1;
252 out_sqlda->sqln = 1;
253
254 if (isc_dsql_prepare
255 (status, &cn->transh, &stmth, 0, query, 1, out_sqlda)) {
256 isc_interprete(buffer, &statusp);
257 store_reset(out_sqlda);
258 out_sqlda = NULL;
259 *errmsg =
260 string_sprintf("Interbase prepare_statement() failed: %s",
261 buffer);
262 *defer_break = FALSE;
263 goto IBASE_EXIT;
264 }
265
266 /* re-allocate the output structure if there's more than one field */
267 if (out_sqlda->sqln < out_sqlda->sqld) {
268 XSQLDA *new_sqlda = store_get(XSQLDA_LENGTH(out_sqlda->sqld));
269 if (isc_dsql_describe
270 (status, &stmth, out_sqlda->version, new_sqlda)) {
271 isc_interprete(buffer, &statusp);
272 isc_dsql_free_statement(status, &stmth, DSQL_drop);
273 store_reset(out_sqlda);
274 out_sqlda = NULL;
275 *errmsg =
276 string_sprintf("Interbase describe_statement() failed: %s",
277 buffer);
278 *defer_break = FALSE;
279 goto IBASE_EXIT;
280 }
281 out_sqlda = new_sqlda;
282 }
283
284 /* allocate storage for every returned field */
285 for (i = 0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++) {
286 switch (var->sqltype & ~1) {
287 case SQL_VARYING:
288 var->sqldata =
289 (char *) store_get(sizeof(char) * var->sqllen + 2);
290 break;
291 case SQL_TEXT:
292 var->sqldata =
293 (char *) store_get(sizeof(char) * var->sqllen);
294 break;
295 case SQL_SHORT:
296 var->sqldata = (char *) store_get(sizeof(short));
297 break;
298 case SQL_LONG:
299 var->sqldata = (char *) store_get(sizeof(ISC_LONG));
300 break;
301 #ifdef SQL_INT64
302 case SQL_INT64:
303 var->sqldata = (char *) store_get(sizeof(ISC_INT64));
304 break;
305 #endif
306 case SQL_FLOAT:
307 var->sqldata = (char *) store_get(sizeof(float));
308 break;
309 case SQL_DOUBLE:
310 var->sqldata = (char *) store_get(sizeof(double));
311 break;
312 #ifdef SQL_TIMESTAMP
313 case SQL_DATE:
314 var->sqldata = (char *) store_get(sizeof(ISC_QUAD));
315 break;
316 #else
317 case SQL_TIMESTAMP:
318 var->sqldata = (char *) store_get(sizeof(ISC_TIMESTAMP));
319 break;
320 case SQL_TYPE_DATE:
321 var->sqldata = (char *) store_get(sizeof(ISC_DATE));
322 break;
323 case SQL_TYPE_TIME:
324 var->sqldata = (char *) store_get(sizeof(ISC_TIME));
325 break;
326 #endif
327 }
328 if (var->sqltype & 1) {
329 var->sqlind = (short *) store_get(sizeof(short));
330 }
331 }
332
333 /* finally, we're ready to execute the statement */
334 if (isc_dsql_execute
335 (status, &cn->transh, &stmth, out_sqlda->version, NULL)) {
336 isc_interprete(buffer, &statusp);
337 *errmsg =
338 string_sprintf("Interbase describe_statement() failed: %s",
339 buffer);
340 isc_dsql_free_statement(status, &stmth, DSQL_drop);
341 *defer_break = FALSE;
342 goto IBASE_EXIT;
343 }
344
345 while (isc_dsql_fetch(status, &stmth, out_sqlda->version, out_sqlda) !=
346 100L) {
347 /* check if an error occurred */
348 if (status[0] & status[1]) {
349 isc_interprete(buffer, &statusp);
350 *errmsg =
351 string_sprintf("Interbase fetch() failed: %s", buffer);
352 isc_dsql_free_statement(status, &stmth, DSQL_drop);
353 *defer_break = FALSE;
354 goto IBASE_EXIT;
355 }
356
357 if (result != NULL)
358 result = string_cat(result, &ssize, &offset, US "\n", 1);
359
360 /* Find the number of fields returned. If this is one, we don't add field
361 names to the data. Otherwise we do. */
362 if (out_sqlda->sqld == 1) {
363 if (out_sqlda->sqlvar[0].sqlind == NULL || *out_sqlda->sqlvar[0].sqlind != -1) /* NULL value yields nothing */
364 result =
365 string_cat(result, &ssize, &offset, US buffer,
366 fetch_field(buffer, sizeof(buffer),
367 &out_sqlda->sqlvar[0]));
368 }
369
370 else
371 for (i = 0; i < out_sqlda->sqld; i++) {
372 int len = fetch_field(buffer, sizeof(buffer),
373 &out_sqlda->sqlvar[i]);
374
375 result =
376 string_cat(result, &ssize, &offset,
377 US out_sqlda->sqlvar[i].aliasname,
378 out_sqlda->sqlvar[i].aliasname_length);
379 result = string_cat(result, &ssize, &offset, US "=", 1);
380
381 /* Quote the value if it contains spaces or is empty */
382
383 if (*out_sqlda->sqlvar[i].sqlind == -1) { /* NULL value */
384 result =
385 string_cat(result, &ssize, &offset, US "\"\"", 2);
386 }
387
388 else if (buffer[0] == 0 || Ustrchr(buffer, ' ') != NULL) {
389 int j;
390 result =
391 string_cat(result, &ssize, &offset, US "\"", 1);
392 for (j = 0; j < len; j++) {
393 if (buffer[j] == '\"' || buffer[j] == '\\')
394 result =
395 string_cat(result, &ssize, &offset,
396 US "\\", 1);
397 result =
398 string_cat(result, &ssize, &offset,
399 US buffer + j, 1);
400 }
401 result =
402 string_cat(result, &ssize, &offset, US "\"", 1);
403 } else {
404 result =
405 string_cat(result, &ssize, &offset, US buffer,
406 len);
407 }
408 result = string_cat(result, &ssize, &offset, US " ", 1);
409 }
410 }
411
412 /* If result is NULL then no data has been found and so we return FAIL.
413 Otherwise, we must terminate the string which has been built; string_cat()
414 always leaves enough room for a terminating zero. */
415
416 if (result == NULL) {
417 yield = FAIL;
418 *errmsg = US "Interbase: no data found";
419 } else {
420 result[offset] = 0;
421 store_reset(result + offset + 1);
422 }
423
424
425 /* Get here by goto from various error checks. */
426
427 IBASE_EXIT:
428
429 if (stmth != NULL)
430 isc_dsql_free_statement(status, &stmth, DSQL_drop);
431
432 /* Non-NULL result indicates a sucessful result */
433
434 if (result != NULL) {
435 *resultptr = result;
436 return OK;
437 } else {
438 DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
439 return yield; /* FAIL or DEFER */
440 }
441 }
442
443
444
445
446 /*************************************************
447 * Find entry point *
448 *************************************************/
449
450 /* See local README for interface description. The handle and filename
451 arguments are not used. Loop through a list of servers while the query is
452 deferred with a retryable error. */
453
454 static int
455 ibase_find(void *handle, uschar * filename, uschar * query, int length,
456 uschar ** result, uschar ** errmsg, BOOL *do_cache)
457 {
458 int sep = 0;
459 uschar *server;
460 uschar *list = ibase_servers;
461 uschar buffer[512];
462
463 /* Keep picky compilers happy */
464 do_cache = do_cache;
465
466 DEBUG(D_lookup) debug_printf("Interbase query: %s\n", query);
467
468 while ((server =
469 string_nextinlist(&list, &sep, buffer,
470 sizeof(buffer))) != NULL) {
471 BOOL defer_break = FALSE;
472 int rc = perform_ibase_search(query, server, result, errmsg,
473 &defer_break);
474 if (rc != DEFER || defer_break)
475 return rc;
476 }
477
478 if (ibase_servers == NULL)
479 *errmsg = US "no Interbase servers defined (ibase_servers option)";
480
481 return DEFER;
482 }
483
484
485
486 /*************************************************
487 * Quote entry point *
488 *************************************************/
489
490 /* The only characters that need to be quoted (with backslash) are newline,
491 tab, carriage return, backspace, backslash itself, and the quote characters.
492 Percent, and underscore and not escaped. They are only special in contexts
493 where they can be wild cards, and this isn't usually the case for data inserted
494 from messages, since that isn't likely to be treated as a pattern of any kind.
495 Sadly, MySQL doesn't seem to behave like other programs. If you use something
496 like "where id="ab\%cd" it does not treat the string as "ab%cd". So you really
497 can't quote "on spec".
498
499 Arguments:
500 s the string to be quoted
501 opt additional option text or NULL if none
502
503 Returns: the processed string or NULL for a bad option
504 */
505
506 static uschar *ibase_quote(uschar * s, uschar * opt)
507 {
508 register int c;
509 int count = 0;
510 uschar *t = s;
511 uschar *quoted;
512
513 if (opt != NULL)
514 return NULL; /* No options recognized */
515
516 while ((c = *t++) != 0)
517 if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL)
518 count++;
519
520 if (count == 0)
521 return s;
522 t = quoted = store_get(Ustrlen(s) + count + 1);
523
524 while ((c = *s++) != 0) {
525 if (Ustrchr("'", c) != NULL) {
526 *t++ = '\'';
527 *t++ = '\'';
528 /* switch(c)
529 {
530 case '\n': *t++ = 'n';
531 break;
532 case '\t': *t++ = 't';
533 break;
534 case '\r': *t++ = 'r';
535 break;
536 case '\b': *t++ = 'b';
537 break;
538 default: *t++ = c;
539 break;
540 }*/
541 } else
542 *t++ = c;
543 }
544
545 *t = 0;
546 return quoted;
547 }
548
549
550 /*************************************************
551 * Version reporting entry point *
552 *************************************************/
553
554 /* See local README for interface description. */
555
556 #include "../version.h"
557
558 void
559 ibase_version_report(FILE *f)
560 {
561 #ifdef DYNLOOKUP
562 fprintf(f, "Library version: ibase: Exim version %s\n", EXIM_VERSION_STR);
563 #endif
564 }
565
566
567 static lookup_info _lookup_info = {
568 US"ibase", /* lookup name */
569 lookup_querystyle, /* query-style lookup */
570 ibase_open, /* open function */
571 NULL, /* no check function */
572 ibase_find, /* find function */
573 NULL, /* no close function */
574 ibase_tidy, /* tidy function */
575 ibase_quote, /* quoting function */
576 ibase_version_report /* version reporting */
577 };
578
579 #ifdef DYNLOOKUP
580 #define ibase_lookup_module_info _lookup_module_info
581 #endif
582
583 static lookup_info *_lookup_list[] = { &_lookup_info };
584 lookup_module_info ibase_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
585
586 /* End of lookups/ibase.c */