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