Add directory name as new arg to EXIM_DBOPEN
[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_catn(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_catn(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_catn(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_catn(result, &ssize, &offset, US "\"\"", 2);
384                 }
385
386                 else if (buffer[0] == 0 || Ustrchr(buffer, ' ') != NULL) {
387                     int j;
388                     result =
389                         string_catn(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_catn(result, &ssize, &offset, US "\"", 1);
401                 } else {
402                     result =
403                         string_catn(result, &ssize, &offset, US buffer, len);
404                 }
405                 result = string_catn(result, &ssize, &offset, US " ", 1);
406             }
407     }
408
409 /* If result is NULL then no data has been found and so we return FAIL.
410 Otherwise, we must terminate the string which has been built; string_cat()
411 always leaves enough room for a terminating zero. */
412
413     if (result == NULL) {
414         yield = FAIL;
415         *errmsg = US "Interbase: no data found";
416     } else {
417         result[offset] = 0;
418         store_reset(result + offset + 1);
419     }
420
421
422 /* Get here by goto from various error checks. */
423
424   IBASE_EXIT:
425
426     if (stmth != NULL)
427         isc_dsql_free_statement(status, &stmth, DSQL_drop);
428
429 /* Non-NULL result indicates a successful result */
430
431     if (result != NULL) {
432         *resultptr = result;
433         return OK;
434     } else {
435         DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
436         return yield;           /* FAIL or DEFER */
437     }
438 }
439
440
441
442
443 /*************************************************
444 *               Find entry point                 *
445 *************************************************/
446
447 /* See local README for interface description. The handle and filename
448 arguments are not used. Loop through a list of servers while the query is
449 deferred with a retryable error. */
450
451 static int
452 ibase_find(void *handle, uschar * filename, uschar * query, int length,
453            uschar ** result, uschar ** errmsg, uint *do_cache)
454 {
455     int sep = 0;
456     uschar *server;
457     uschar *list = ibase_servers;
458     uschar buffer[512];
459
460     /* Keep picky compilers happy */
461     do_cache = do_cache;
462
463     DEBUG(D_lookup) debug_printf("Interbase query: %s\n", query);
464
465     while ((server =
466             string_nextinlist(&list, &sep, buffer,
467                               sizeof(buffer))) != NULL) {
468         BOOL defer_break = FALSE;
469         int rc = perform_ibase_search(query, server, result, errmsg,
470                                       &defer_break);
471         if (rc != DEFER || defer_break)
472             return rc;
473     }
474
475     if (ibase_servers == NULL)
476         *errmsg = US "no Interbase servers defined (ibase_servers option)";
477
478     return DEFER;
479 }
480
481
482
483 /*************************************************
484 *               Quote entry point                *
485 *************************************************/
486
487 /* The only characters that need to be quoted (with backslash) are newline,
488 tab, carriage return, backspace, backslash itself, and the quote characters.
489 Percent, and underscore and not escaped. They are only special in contexts
490 where they can be wild cards, and this isn't usually the case for data inserted
491 from messages, since that isn't likely to be treated as a pattern of any kind.
492 Sadly, MySQL doesn't seem to behave like other programs. If you use something
493 like "where id="ab\%cd" it does not treat the string as "ab%cd". So you really
494 can't quote "on spec".
495
496 Arguments:
497   s          the string to be quoted
498   opt        additional option text or NULL if none
499
500 Returns:     the processed string or NULL for a bad option
501 */
502
503 static uschar *ibase_quote(uschar * s, uschar * opt)
504 {
505     register int c;
506     int count = 0;
507     uschar *t = s;
508     uschar *quoted;
509
510     if (opt != NULL)
511         return NULL;            /* No options recognized */
512
513     while ((c = *t++) != 0)
514         if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL)
515             count++;
516
517     if (count == 0)
518         return s;
519     t = quoted = store_get(Ustrlen(s) + count + 1);
520
521     while ((c = *s++) != 0) {
522         if (Ustrchr("'", c) != NULL) {
523             *t++ = '\'';
524             *t++ = '\'';
525 /*    switch(c)
526       {
527       case '\n': *t++ = 'n';
528       break;
529       case '\t': *t++ = 't';
530       break;
531       case '\r': *t++ = 'r';
532       break;
533       case '\b': *t++ = 'b';
534       break;
535       default:   *t++ = c;
536       break;
537       }*/
538         } else
539             *t++ = c;
540     }
541
542     *t = 0;
543     return quoted;
544 }
545
546
547 /*************************************************
548 *         Version reporting entry point          *
549 *************************************************/
550
551 /* See local README for interface description. */
552
553 #include "../version.h"
554
555 void
556 ibase_version_report(FILE *f)
557 {
558 #ifdef DYNLOOKUP
559 fprintf(f, "Library version: ibase: Exim version %s\n", EXIM_VERSION_STR);
560 #endif
561 }
562
563
564 static lookup_info _lookup_info = {
565   US"ibase",                     /* lookup name */
566   lookup_querystyle,             /* query-style lookup */
567   ibase_open,                    /* open function */
568   NULL,                          /* no check function */
569   ibase_find,                    /* find function */
570   NULL,                          /* no close function */
571   ibase_tidy,                    /* tidy function */
572   ibase_quote,                   /* quoting function */
573   ibase_version_report           /* version reporting */
574 };
575
576 #ifdef DYNLOOKUP
577 #define ibase_lookup_module_info _lookup_module_info
578 #endif
579
580 static lookup_info *_lookup_list[] = { &_lookup_info };
581 lookup_module_info ibase_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
582
583 /* End of lookups/ibase.c */