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