c19bb039a50a20a94d6551f133099ecda101f4f2
[exim.git] / src / src / hintsdb.h
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2020 - 2024 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
9
10 /* This header file contains macro definitions so that a variety of DBM
11 libraries can be used by Exim. Nigel Metheringham provided the original set for
12 Berkeley DB 1.x in native mode and ndbm. Subsequently, versions for Berkeley DB
13 2.x and 3.x were added. Later still, support for tdb was added, courtesy of
14 James Antill. Most recently, support for native mode gdbm was added, with code
15 from Pierre A. Humblet, so Exim could be made to work with Cygwin.
16
17 For convenience, the definitions of the structures used in the various hints
18 databases are also kept in this file, which is used by the maintenance
19 utilities as well as the main Exim binary.
20
21 A key/value store is supported (only).  Keys are strings; values arbitrary
22 binary blobs.
23
24 The API is:
25   Functions:
26     exim_lockfile_needed        API semantics predicate
27     exim_dbopen
28     exim_dbclose
29     exim_dbget
30     exim_dbput
31     exim_dbputb                 non-overwriting put
32     exim_dbdel
33     exim_dbcreate_cursor
34     exim_dbscan                 get, and bump cursor
35     exim_dbdelete_cursor
36     exim_datum_init
37     exim_datum_size_get/set
38     exim_datum_data_get/set
39     exim_datum_free
40   Defines:
41     EXIM_DB             access handle
42     EXIM_CURSOR         datatype for cursor
43     EXIM_DATUM          datatype for "value"
44     EXIM_DBTYPE         text for logging & debuug
45
46 Selection of the shim layer implementation, and backend, is by #defines.
47
48 The users of this API are:
49   hintsdb interface     dbfn.c
50   hintsdb utilities     exim_dbutil.c and exim_dbmvuild.c
51   dbmdb lookup          lookups/dbmdb,c
52   autoreply transport   transports/autoreply.c
53
54 Note that the dbmdb lookup use, bypassing the dbfn.c layer,
55 means that no file-locking is done.
56 XXX This feels like a layering violation; I don't see it commented on
57 anywhere.
58
59 Future: consider re-architecting to support caching of the open-handle
60 for hintsdb uses (the dbmdb use gets that already).  This would need APIs
61 for transaction locks.  Perhaps merge the implementation with the lookups
62 layer, in some way, for the open-handle caching (since that manages closes
63 required by Exim's process transisitions)?
64 */
65
66 #ifndef HINTSDB_H
67 #define HINTSDB_H
68
69
70 #ifdef USE_SQLITE
71 # if defined(USE_DB) || defined(USE_GDBM) || defined(USE_TDB)
72 #  error USE_SQLITE conflict with alternate definition
73 # endif
74
75 /* ********************* sqlite3 interface ************************ */
76
77 # include <sqlite3.h>
78
79 /* Basic DB type */
80 # define EXIM_DB sqlite3
81
82 # define EXIM_CURSOR int
83
84 # /* The datum type used for queries */
85 # define EXIM_DATUM blob
86
87 /* Some text for messages */
88 # define EXIM_DBTYPE "sqlite3"
89
90 # /* Access functions */
91
92 static inline BOOL
93 exim_lockfile_needed(void)
94 {
95 return FALSE;   /* We do transaction; no extra locking needed */
96 }
97
98 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
99 static inline EXIM_DB *
100 exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
101   unsigned mode)
102 {
103 EXIM_DB * dbp;
104 int ret, sflags = flags & O_RDWR ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
105 if (flags & O_CREAT) sflags |= SQLITE_OPEN_CREATE;
106 if ((ret = sqlite3_open_v2(CCS name, &dbp, sflags, NULL)) == SQLITE_OK)
107   {
108   sqlite3_busy_timeout(dbp, 5000);
109   if (flags & O_CREAT)
110     ret = sqlite3_exec(dbp,
111             "CREATE TABLE IF NOT EXISTS tbl (ky TEXT PRIMARY KEY, dat BLOB);",
112             NULL, NULL, NULL);
113   if (ret != SQLITE_OK)
114     sqlite3_close(dbp);
115   }
116 //else
117 //  fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errmsg(dbp));
118 return ret == SQLITE_OK ? dbp : NULL;
119 }
120
121 static inline BOOL
122 exim_dbtransaction_start(EXIM_DB * dbp)
123 {
124 return sqlite3_exec(dbp, "BEGIN TRANSACTION;", NULL, NULL, NULL) == SQLITE_OK;
125 }
126
127 static inline EXIM_DB *
128 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
129   unsigned mode)
130 {
131 EXIM_DB * dbp = exim_dbopen_multi(name, dirname, flags, mode);
132 if (!dbp || exim_dbtransaction_start(dbp))
133   return dbp;
134 sqlite3_close(dbp);
135 return NULL;
136 }
137
138 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
139 /* note we alloc'n'copy - the caller need not do so */
140 /* result has a NUL appended, but the length is as per the DB */
141
142 static inline BOOL
143 exim_dbget__(EXIM_DB * dbp, const uschar * s, EXIM_DATUM * res)
144 {
145 sqlite3_stmt * statement;
146 int ret;
147
148 res->len = (size_t) -1;
149 /* fprintf(stderr, "exim_dbget__(%s)\n", s); */
150 if ((ret = sqlite3_prepare_v2(dbp, CCS s, -1, &statement, NULL)) != SQLITE_OK)
151   {
152 /* fprintf(stderr, "prepare fail: %s\n", sqlite3_errmsg(dbp)); */
153   return FALSE;
154   }
155 if (sqlite3_step(statement) != SQLITE_ROW)
156   {
157 /* fprintf(stderr, "step fail: %s\n", sqlite3_errmsg(dbp)); */
158   sqlite3_finalize(statement);
159   return FALSE;
160   }
161
162 res->len = sqlite3_column_bytes(statement, 0);
163 # ifdef COMPILE_UTILITY
164 res->data = malloc(res->len);
165 # else
166 res->data = store_get(res->len, GET_TAINTED);
167 # endif
168 memcpy(res->data, sqlite3_column_blob(statement, 0), res->len);
169 res->data[res->len] = '\0';
170 /* fprintf(stderr, "res %d bytes: '%.*s'\n", (int)res->len, (int)res->len, res->data); */
171 sqlite3_finalize(statement);
172 return TRUE;
173 }
174
175 static inline BOOL
176 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
177 {
178 # define FMT "SELECT dat FROM tbl WHERE ky = '%.*s';"
179 uschar * qry;
180 int i;
181 BOOL ret;
182
183 # ifdef COMPILE_UTILITY
184 /* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */
185 qry = malloc(i = snprintf(NULL, 0, FMT, (int) key->len, key->data));
186 snprintf(CS qry, i, FMT, (int) key->len, key->data);
187 ret = exim_dbget__(dbp, qry, res);
188 free(qry);
189 # else
190 /* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */
191 qry = string_sprintf(FMT, (int) key->len, key->data);
192 ret = exim_dbget__(dbp, qry, res);
193 # endif
194
195 return ret;
196 # undef FMT
197 }
198
199 /**/
200 # define EXIM_DBPUTB_OK  0
201 # define EXIM_DBPUTB_DUP (-1)
202
203 static inline int
204 exim_s_dbp(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, const uschar * alt)
205 {
206 int hlen = data->len * 2, off = 0, res;
207 # define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%.*s', X'%.*s');"
208 # ifdef COMPILE_UTILITY
209 uschar * hex = malloc(hlen+1);
210 # else
211 uschar * hex = store_get(hlen+1, data->data);
212 # endif
213 uschar * qry;
214
215 for (const uschar * s = data->data, * t = s + data->len; s < t; s++, off += 2)
216   sprintf(CS hex + off, "%02X", *s);
217
218 # ifdef COMPILE_UTILITY
219 res = snprintf(CS hex, 0, FMT, alt, (int) key->len, key->data, hlen, hex);
220 qry = malloc(res);
221 snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, hlen, hex);
222 /* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
223 res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
224 free(qry);
225 free(hex);
226 # else
227 qry = string_sprintf(FMT, alt, (int) key->len, key->data, hlen, hex);
228 /* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
229 res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
230 /* fprintf(stderr, "exim_s_dbp res %d\n", res); */
231 # endif
232
233 if (res != SQLITE_OK)
234   fprintf(stderr, "sqlite3_exec: %s\n", sqlite3_errmsg(dbp));
235
236 return res == SQLITE_OK ? EXIM_DBPUTB_OK : EXIM_DBPUTB_DUP;
237 # undef FMT
238 }
239
240 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
241
242 static inline int
243 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
244 {
245 /* fprintf(stderr, "exim_dbput()\n"); */
246 (void) exim_s_dbp(dbp, key, data, US"REPLACE");
247 return 0;
248 }
249
250 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
251
252 /* Returns from EXIM_DBPUTB */
253
254 static inline int
255 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
256 {
257 return exim_s_dbp(dbp, key, data, US"ABORT");
258 }
259
260 /* EXIM_DBDEL */
261 static inline int
262 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
263 {
264 # define FMT "DELETE FROM tbl WHERE ky = '%.*s';"
265 uschar * qry;
266 int res;
267
268 # ifdef COMPILE_UTILITY
269 res = snprintf(NULL, 0, FMT, (int) key->len, key->data); /* res excludes nul */
270 qry = malloc(res);
271 snprintf(CS qry, res, FMT, (int) key->len, key->data);
272 res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
273 free(qry);
274 # else
275 qry = string_sprintf(FMT, (int) key->len, key->data);
276 res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
277 # endif
278
279 return res;
280 # undef FMT
281 }
282
283
284 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
285 /* Cursors are inefficiently emulated by repeating searches */
286
287 static inline EXIM_CURSOR *
288 exim_dbcreate_cursor(EXIM_DB * dbp)
289 {
290 # ifdef COMPILE_UTILITY
291 EXIM_CURSOR * c = malloc(sizeof(int));
292 # else
293 EXIM_CURSOR * c = store_malloc(sizeof(int));
294 # endif
295 *c = 0;
296 return c;
297 }
298
299 /* EXIM_DBSCAN */
300 /* Note that we return the (next) key, not the record value */
301 static inline BOOL
302 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first,
303   EXIM_CURSOR * cursor)
304 {
305 # define FMT "SELECT ky FROM tbl ORDER BY ky LIMIT 1 OFFSET %d;"
306 uschar * qry;
307 int i;
308 BOOL ret;
309
310 # ifdef COMPILE_UTILITY
311 qry = malloc((i = snprintf(NULL, 0, FMT, *cursor)));
312 snprintf(CS qry, i-1, FMT, *cursor);
313 /* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
314 ret = exim_dbget__(dbp, qry, key);
315 free(qry);
316 /* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
317 # else
318 qry = string_sprintf(FMT, *cursor);
319 /* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
320 ret = exim_dbget__(dbp, qry, key);
321 /* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
322 # endif
323 if (ret) *cursor = *cursor + 1;
324 return ret;
325 # undef FMT
326 }
327
328 /* EXIM_DBDELETE_CURSOR - terminate scanning operation. */
329 static inline void
330 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
331 {
332 # ifdef COMPILE_UTILITY
333 free(cursor);
334 # else
335 store_free(cursor);
336 # endif
337 }
338
339
340 /* EXIM_DBCLOSE */
341 static inline void
342 exim_dbclose_multi(EXIM_DB * dbp)
343 {
344 sqlite3_close(dbp);
345 }
346 static inline void
347 exim_dbtransaction_commit(EXIM_DB * dbp)
348 {
349 (void) sqlite3_exec(dbp, "COMMIT TRANSACTION;", NULL, NULL, NULL);
350 }
351 static inline void
352 exim_dbclose__(EXIM_DB * dbp)
353 {
354 exim_dbtransaction_commit(dbp);
355 exim_dbclose_multi(dbp);
356 }
357
358
359 /* Datum access */
360
361 static uschar *
362 exim_datum_data_get(EXIM_DATUM * dp)
363 { return US dp->data; }
364 static void
365 exim_datum_data_set(EXIM_DATUM * dp, void * s)
366 { dp->data = s; }
367  
368 static unsigned
369 exim_datum_size_get(EXIM_DATUM * dp)
370 { return dp->len; }
371 static void
372 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
373 { dp->len = n; }
374
375
376
377 static inline void
378 exim_datum_init(EXIM_DATUM * dp)
379 { dp->data = NULL; }                    /* compiler quietening */
380
381 /* No free needed for a datum */
382
383 static inline void
384 exim_datum_free(EXIM_DATUM * dp)
385 { }
386
387 /* size limit */
388
389 # define EXIM_DB_RLIMIT 150
390
391
392
393
394
395
396 #elif defined(USE_TDB)
397
398 # if defined(USE_DB) || defined(USE_GDBM) || defined(USE_SQLITE)
399 #  error USE_TDB conflict with alternate definition
400 # endif
401
402 /* ************************* tdb interface ************************ */
403 /*XXX https://manpages.org/tdb/3 mentions concurrent writes.
404 Could we lose the file lock? */
405
406 # include <tdb.h>
407
408 /* Basic DB type */
409 # define EXIM_DB TDB_CONTEXT
410
411 /* Cursor type: tdb uses the previous "key" in _nextkey() (really it wants
412 tdb_traverse to be called) */
413 # define EXIM_CURSOR TDB_DATA
414
415 /* The datum type used for queries */
416 # define EXIM_DATUM TDB_DATA
417
418 /* Some text for messages */
419 # define EXIM_DBTYPE "tdb"
420
421 /* Access functions */
422
423 static inline BOOL
424 exim_lockfile_needed(void)
425 {
426 return TRUE;
427 }
428
429 static inline EXIM_DB *
430 exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
431   unsigned mode) { return NULL; }
432 static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
433 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
434 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
435
436 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
437 static inline EXIM_DB *
438 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
439   unsigned mode)
440 {
441 return tdb_open(CS name, 0, TDB_DEFAULT, flags, mode);
442 }
443
444 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
445 static inline BOOL
446 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
447 {
448 *res = tdb_fetch(dbp, *key);    /* A struct arg and return!! */
449 return res->dptr != NULL;
450 }
451
452 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
453 static inline int
454 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
455 { return tdb_store(dbp, *key, *data, TDB_REPLACE); }
456
457 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
458 static inline int
459 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
460 { return tdb_store(dbp, *key, *data, TDB_INSERT); }
461
462 /* Returns from EXIM_DBPUTB */
463
464 # define EXIM_DBPUTB_OK  0
465 # define EXIM_DBPUTB_DUP (-1)
466
467 /* EXIM_DBDEL */
468 static inline int
469 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
470 { return tdb_delete(dbp, *key); }
471
472 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
473 static inline EXIM_CURSOR *
474 exim_dbcreate_cursor(EXIM_DB * dbp)
475 {
476 # ifdef COMPILE_UTILITY
477 EXIM_CURSOR * c = malloc(sizeof(TDB_DATA));
478 # else
479 EXIM_CURSOR * c = store_malloc(sizeof(TDB_DATA));
480 # endif
481 c->dptr = NULL;
482 return c;
483 }
484
485 /* EXIM_DBSCAN - This is complicated because we have to free the last datum
486 free() must not die when passed NULL */
487
488 static inline BOOL
489 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first,
490   EXIM_CURSOR * cursor)
491 {
492 *key = first ? tdb_firstkey(dbp) : tdb_nextkey(dbp, *cursor);
493 free(cursor->dptr);
494 *cursor = *key;
495 return key->dptr != NULL;
496 }
497
498 /* EXIM_DBDELETE_CURSOR - terminate scanning operation. */
499 static inline void
500 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
501 { store_free(cursor); }
502
503 /* EXIM_DBCLOSE */
504 static inline void
505 exim_dbclose__(EXIM_DB * db)
506 { tdb_close(db); }
507
508 /* Datum access */
509
510 static inline uschar *
511 exim_datum_data_get(EXIM_DATUM * dp)
512 { return US dp->dptr; }
513 static inline void
514 exim_datum_data_set(EXIM_DATUM * dp, void * s)
515 { dp->dptr = s; }
516
517 static inline unsigned
518 exim_datum_size_get(EXIM_DATUM * dp)
519 { return dp->dsize; }
520 static inline void
521 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
522 { dp->dsize = n; }
523
524 /* No initialization is needed. */
525
526 static inline void
527 exim_datum_init(EXIM_DATUM * d)
528 { }
529
530 /* Free the stuff inside the datum. */
531
532 static inline void
533 exim_datum_free(EXIM_DATUM * d)
534 {
535 free(d->dptr);
536 d->dptr = NULL;
537 }
538
539 /* size limit */
540
541 # define EXIM_DB_RLIMIT 150
542
543
544
545
546
547
548 /********************* Berkeley db native definitions **********************/
549
550 #elif defined USE_DB
551
552 # if defined(USE_TDB) || defined(USE_GDBM) || defined(USE_SQLITE)
553 #  error USE_DB conflict with alternate definition
554 # endif
555
556 # include <db.h>
557
558 /* 1.x did no locking
559    2.x had facilities, but exim does it's own
560    3.x+ unknown
561 */
562
563 /* We can distinguish between versions 1.x and 2.x/3.x by looking for a
564 definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */
565
566 # ifdef DB_VERSION_STRING
567
568 #  if DB_VERSION_MAJOR >= 6
569 #   error Version 6 and later BDB API is not supported
570 #  endif
571
572 /* The API changed (again!) between the 2.x and 3.x versions */
573
574 # if DB_VERSION_MAJOR >= 3
575
576 /***************** Berkeley db 3.x/4.x native definitions ******************/
577
578 /* Basic DB type */
579 #  if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
580 #   define EXIM_DB       DB_ENV
581 /* Cursor type, for scanning */
582 #   define EXIM_CURSOR   DBC
583
584 /* The datum type used for queries */
585 #   define EXIM_DATUM    DBT
586
587 /* Some text for messages */
588 #   define EXIM_DBTYPE   "db (v4.1+)"
589
590 /* Only more-recent versions.  5+ ? */
591 #   ifndef DB_FORCESYNC
592 #    define DB_FORCESYNC 0
593 #   endif
594
595 /* Error callback */
596 /* For Berkeley DB >= 2, we can define a function to be called in case of DB
597 errors. This should help with debugging strange DB problems, e.g. getting "File
598 exists" when you try to open a db file. The API for this function was changed
599 at DB release 4.3. */
600
601 static inline void
602 dbfn_bdb_error_callback(const DB_ENV * dbenv, const char * pfx, const char * msg)
603 {
604 #ifndef MACRO_PREDEF 
605 log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg);
606 #endif
607 }
608
609
610
611 /* Access functions (BDB 4.1+) */
612
613 static inline BOOL
614 exim_lockfile_needed(void)
615 {
616 return TRUE;
617 }
618
619 static inline EXIM_DB *
620 exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
621   unsigned mode) { return NULL; }
622 static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
623 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
624 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
625
626 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
627 /* The API changed for DB 4.1. - and we also starting using the "env" with a
628 specified working dir, to avoid the DBCONFIG file trap. */
629
630 #   define ENV_TO_DB(env) ((DB *)(((EXIM_DB *)env)->app_private))
631
632 static inline EXIM_DB *
633 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
634   unsigned mode)
635 {
636 EXIM_DB * dbp;
637 DB * b;
638 if (  db_env_create(&dbp, 0) != 0
639    || (dbp->set_errcall(dbp, dbfn_bdb_error_callback), 0)
640    || dbp->open(dbp, CS dirname, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0) != 0
641    )
642   return NULL;
643 if (db_create(&b, dbp, 0) == 0)
644   {
645   dbp->app_private = b;
646   if (b->open(b, NULL, CS name, NULL,
647               flags & O_CREAT ? DB_HASH : DB_UNKNOWN,
648               flags & O_CREAT ? DB_CREATE
649               : flags & (O_WRONLY|O_RDWR) ? 0 : DB_RDONLY,
650               mode) == 0
651           )
652     return dbp;
653
654   b->close(b, 0);
655   }
656 dbp->close(dbp, 0);
657 return NULL;
658 }
659
660 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
661 static inline BOOL
662 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
663 {
664 DB * b = ENV_TO_DB(dbp);
665 return b->get(b, NULL, key, res, 0) == 0;
666 }
667
668 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
669 static inline int
670 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
671 {
672 DB * b = ENV_TO_DB(dbp);
673 return b->put(b, NULL, key, data, 0);
674 }
675
676 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
677 static inline int
678 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
679 {
680 DB * b = ENV_TO_DB(dbp);
681 return b->put(b, NULL, key, data, DB_NOOVERWRITE);
682 }
683
684 /* Return values from EXIM_DBPUTB */
685
686 #   define EXIM_DBPUTB_OK  0
687 #   define EXIM_DBPUTB_DUP DB_KEYEXIST
688
689 /* EXIM_DBDEL */
690 static inline int
691 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
692 {
693 DB * b = ENV_TO_DB(dbp);
694 return b->del(b, NULL, key, 0);
695 }
696
697 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
698
699 static inline EXIM_CURSOR *
700 exim_dbcreate_cursor(EXIM_DB * dbp)
701 {
702 DB * b = ENV_TO_DB(dbp);
703 EXIM_CURSOR * c;
704 b->cursor(b, NULL, &c, 0);
705 return c;
706 }
707
708 /* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */
709 static inline BOOL
710 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first,
711   EXIM_CURSOR * cursor)
712 {
713 return cursor->c_get(cursor, key, data, first ? DB_FIRST : DB_NEXT) == 0;
714 }
715
716 /* EXIM_DBDELETE_CURSOR - terminate scanning operation */
717 static inline void
718 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
719 { cursor->c_close(cursor); }
720
721 /* EXIM_DBCLOSE */
722 static inline void
723 exim_dbclose__(EXIM_DB * dbp_o)
724 {
725 DB_ENV * dbp = dbp_o;
726 DB * b = ENV_TO_DB(dbp);
727 b->close(b, 0);
728 dbp->close(dbp, DB_FORCESYNC);
729 }
730
731 /* Datum access */
732
733 static inline uschar *
734 exim_datum_data_get(EXIM_DATUM * dp)
735 { return dp->data; }
736 static inline void
737 exim_datum_data_set(EXIM_DATUM * dp, void * s)
738 { dp->data = s; }
739
740 static inline unsigned
741 exim_datum_size_get(EXIM_DATUM * dp)
742 { return dp->size; }
743 static inline void
744 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
745 { dp->size = n; }
746
747 /* The whole datum structure contains other fields that must be cleared
748 before use, but we don't have to free anything after reading data. */
749
750 static inline void
751 exim_datum_init(EXIM_DATUM * d)
752 { memset(d, 0, sizeof(*d)); }
753
754 static inline void
755 exim_datum_free(EXIM_DATUM * d)
756 { }
757
758 #  else /* pre- 4.1 */
759
760 #   define EXIM_DB       DB
761
762 /* Cursor type, for scanning */
763 #   define EXIM_CURSOR   DBC
764
765 /* The datum type used for queries */
766 #   define EXIM_DATUM    DBT
767
768 /* Some text for messages */
769 #   define EXIM_DBTYPE   "db (v3/4)"
770
771 /* Access functions (BDB 3/4) */
772
773 static inline BOOL
774 exim_lockfile_needed(void)
775 {
776 return TRUE;
777 }
778
779 static inline EXIM_DB *
780 exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
781   unsigned mode) { return NULL; }
782 static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
783 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
784 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
785
786 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
787 static inline EXIM_DB *
788 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
789   unsigned mode)
790 {
791 EXIM_DB * dbp;
792 return db_create(&dbp, NULL, 0) == 0
793   && (  dbp->set_errcall(dbp, dbfn_bdb_error_callback),
794         dbp->open(dbp, CS name, NULL,
795           flags & O_CREAT ? DB_HASH : DB_UNKNOWN,
796           flags & O_CREAT ? DB_CREATE
797           : flags & (O_WRONLY|O_RDWR) ? 0 : DB_RDONLY,
798           mode)
799      ) == 0
800   ? dbp : NULL;
801 }
802
803 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
804 static inline BOOL
805 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
806 { return dbp->get(dbp, NULL, key, res, 0) == 0; }
807
808 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
809 static inline int
810 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
811 { return dbp->put(dbp, NULL, key, data, 0); }
812
813 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
814 static inline int
815 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
816 { return dbp->put(dbp, NULL, key, data, DB_NOOVERWRITE); }
817
818 /* Return values from EXIM_DBPUTB */
819
820 #   define EXIM_DBPUTB_OK  0
821 #   define EXIM_DBPUTB_DUP DB_KEYEXIST
822
823 /* EXIM_DBDEL */
824 static inline int
825 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
826 { return dbp->del(dbp, NULL, key, 0); }
827
828 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
829
830 static inline EXIM_CURSOR *
831 exim_dbcreate_cursor(EXIM_DB * dbp)
832 {
833 EXIM_CURSOR * c;
834 dbp->cursor(dbp, NULL, &c, 0);
835 return c;
836 }
837
838 /* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */
839 static inline BOOL
840 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first,
841   EXIM_CURSOR * cursor)
842 {
843 return cursor->c_get(cursor, key, data, first ? DB_FIRST : DB_NEXT) == 0;
844 }
845
846 /* EXIM_DBDELETE_CURSOR - terminate scanning operation */
847 static inline void
848 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
849 { cursor->c_close(cursor); }
850
851 /* EXIM_DBCLOSE */
852 static inline void
853 exim_dbclose__(EXIM_DB * dbp)
854 { dbp->close(dbp, 0); }
855
856 /* Datum access */
857
858 static inline uschar *
859 exim_datum_data_get(EXIM_DATUM * dp)
860 { return US dp->dptr; }
861 static inline void
862 exim_datum_data_set(EXIM_DATUM * dp, void * s)
863 { dp->dptr = s; }
864
865 static inline uschar *
866 exim_datum_size_get(EXIM_DATUM * dp)
867 { return US dp->size; }
868 static inline void
869 exim_datum_size_set(EXIM_DATUM * dp, uschar * s)
870 { dp->size = CS s; }
871
872 /* The whole datum structure contains other fields that must be cleared
873 before use, but we don't have to free anything after reading data. */
874
875 static inline void
876 exim_datum_init(EXIM_DATUM * d)
877 { memset(d, 0, sizeof(*d)); }
878
879 static inline void
880 exim_datum_free(EXIM_DATUM * d)
881 { }
882
883 #  endif
884
885
886 #  else /* DB_VERSION_MAJOR >= 3 */
887 #   error Berkeley DB versions earlier than 3 are not supported */
888 #  endif /* DB_VERSION_MAJOR */
889 # else
890 #  error Berkeley DB version 1 is no longer supported
891 # endif /* DB_VERSION_STRING */
892
893
894 /* all BDB versions */
895 /* size limit */
896
897 # define EXIM_DB_RLIMIT 150
898
899
900
901
902
903
904 /********************* gdbm interface definitions **********************/
905
906 #elif defined USE_GDBM
907 /*XXX TODO: exim's lockfile not needed? */
908
909 # if defined(USE_TDB) || defined(USE_DB) || defined(USE_SQLITE)
910 #  error USE_GDBM conflict with alternate definition
911 # endif
912
913 # include <gdbm.h>
914
915 /* Basic DB type */
916 typedef struct {
917        GDBM_FILE gdbm;  /* Database */
918        datum lkey;      /* Last key, for scans */
919 } EXIM_DB;
920
921 /* Cursor type, not used with gdbm: just set up a dummy */
922 # define EXIM_CURSOR int
923
924 /* The datum type used for queries */
925 # define EXIM_DATUM datum
926
927 /* Some text for messages */
928
929 # define EXIM_DBTYPE "gdbm"
930
931 /* Access functions (gdbm) */
932
933 static inline BOOL
934 exim_lockfile_needed(void)
935 {
936 return TRUE;
937 }
938
939 static inline EXIM_DB *
940 exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
941   unsigned mode) { return NULL; }
942 static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
943 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
944 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
945
946 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
947 static inline EXIM_DB *
948 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
949   unsigned mode)
950 {
951 EXIM_DB * dbp = malloc(sizeof(EXIM_DB));        /*XXX why not exim mem-mgmt? */
952 if (dbp)
953   {
954   dbp->lkey.dptr = NULL;
955   dbp->gdbm = gdbm_open(CS name, 0,
956     flags & O_CREAT ? GDBM_WRCREAT
957     : flags & (O_RDWR|O_WRONLY) ? GDBM_WRITER : GDBM_READER,
958     mode, 0);
959   if (dbp->gdbm) return dbp;
960   free(dbp);
961   }
962 return NULL;
963 }
964
965 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
966 static inline BOOL
967 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
968 {
969 *res = gdbm_fetch(dbp->gdbm, *key);     /* A struct arg & return! */
970 return res->dptr != NULL;
971 }
972
973 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
974 static inline int
975 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
976 { return gdbm_store(dbp->gdbm, *key, *data, GDBM_REPLACE); }
977
978 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
979 static inline int
980 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
981 { return gdbm_store(dbp->gdbm, *key, *data, GDBM_INSERT); }
982
983 /* Returns from EXIM_DBPUTB */
984
985 # define EXIM_DBPUTB_OK  0
986 # define EXIM_DBPUTB_DUP 1
987
988 /* EXIM_DBDEL */
989 static inline int
990 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
991 { return gdbm_delete(dbp->gdbm, *key); }
992
993 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */
994 static inline EXIM_CURSOR *
995 exim_dbcreate_cursor(EXIM_DB * dbp)
996 { return NULL; }
997
998 /* EXIM_DBSCAN */
999 static inline BOOL
1000 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first,
1001   EXIM_CURSOR * cursor)
1002 {
1003 char * s;
1004 *key = first ? gdbm_firstkey(dbp->gdbm) : gdbm_nextkey(dbp->gdbm, dbp->lkey);
1005 if ((s = dbp->lkey.dptr)) free(s);
1006 dbp->lkey = *key;
1007 return key->dptr != NULL;
1008 }
1009
1010 /* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */
1011 static inline void
1012 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
1013 { }
1014
1015 /* EXIM_DBCLOSE */
1016 static inline void
1017 exim_dbclose__(EXIM_DB * dbp)
1018 {
1019 char * s;
1020 gdbm_close(dbp->gdbm);
1021 if ((s = dbp->lkey.dptr)) free(s);
1022 free(dbp);
1023 }
1024
1025 /* Datum access types */
1026
1027 static inline uschar *
1028 exim_datum_data_get(EXIM_DATUM * dp)
1029 { return US dp->dptr; }
1030 static inline void
1031 exim_datum_data_set(EXIM_DATUM * dp, void * s)
1032 { dp->dptr = s; }
1033
1034 static inline unsigned
1035 exim_datum_size_get(EXIM_DATUM * dp)
1036 { return dp->dsize; }
1037 static inline void
1038 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
1039 { dp->dsize = n; }
1040
1041 /* There's no clearing required before use, but we have to free the dptr
1042 after reading data. */
1043
1044 static inline void
1045 exim_datum_init(EXIM_DATUM * d)
1046 { }
1047
1048 static inline void
1049 exim_datum_free(EXIM_DATUM * d)
1050 { free(d->dptr); }
1051
1052 /* size limit. GDBM is int-max limited, but we want to be less silly */
1053
1054 # define EXIM_DB_RLIMIT 150
1055
1056 #else  /* USE_GDBM */
1057
1058
1059
1060
1061
1062
1063 /* If none of USE_DB, USG_GDBM, USE_SQLITE or USE_TDB are set,
1064 the default is the NDBM interface (which seems to be a wrapper for GDBM) */
1065
1066
1067 /********************* ndbm interface definitions **********************/
1068
1069 # include <ndbm.h>
1070
1071 /* Basic DB type */
1072 # define EXIM_DB DBM
1073
1074 /* Cursor type, not used with ndbm: just set up a dummy */
1075 # define EXIM_CURSOR int
1076
1077 /* The datum type used for queries */
1078 # define EXIM_DATUM datum
1079
1080 /* Some text for messages */
1081
1082 # define EXIM_DBTYPE "ndbm"
1083
1084 /* Access functions (ndbm) */
1085
1086 static inline BOOL
1087 exim_lockfile_needed(void)
1088 {
1089 return TRUE;
1090 }
1091
1092 static inline EXIM_DB *
1093 exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
1094   unsigned mode) { return NULL; }
1095 static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
1096 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
1097 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
1098
1099 /* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */
1100 /* Check that the name given is not present. This catches
1101 a directory name; otherwise we would create the name.pag and
1102 name.dir files in the directory's parent. */
1103
1104 static inline EXIM_DB *
1105 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
1106   unsigned mode)
1107 {
1108 struct stat st;
1109 if (!(flags & O_CREAT) || lstat(CCS name, &st) != 0 && errno == ENOENT)
1110   return dbm_open(CS name, flags, mode);
1111 #ifndef COMPILE_UTILITY
1112 debug_printf("%s %d errno %s\n", __FUNCTION__, __LINE__, strerror(errno));
1113 #endif
1114 errno = (st.st_mode & S_IFMT) == S_IFDIR ? EISDIR : EEXIST;
1115 return NULL;
1116 }
1117
1118 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
1119 static inline BOOL
1120 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
1121 {
1122 *res = dbm_fetch(dbp, *key);    /* A struct arg & return! */
1123 return res->dptr != NULL;
1124 }
1125
1126 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
1127 static inline int
1128 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
1129 { return dbm_store(dbp, *key, *data, DBM_REPLACE); }
1130
1131 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
1132 static inline int
1133 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
1134 { return dbm_store(dbp, *key, *data, DBM_INSERT); }
1135
1136 /* Returns from EXIM_DBPUTB */
1137
1138 # define EXIM_DBPUTB_OK  0
1139 # define EXIM_DBPUTB_DUP 1
1140
1141 /* EXIM_DBDEL */
1142 static inline int
1143 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
1144 { return dbm_delete(dbp, *key); }
1145
1146 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */
1147 static inline EXIM_CURSOR *
1148 exim_dbcreate_cursor(EXIM_DB * dbp)
1149 { return NULL; }
1150
1151 /* EXIM_DBSCAN */
1152 static inline BOOL
1153 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first,
1154   EXIM_CURSOR * cursor)
1155 {
1156 *key = first ? dbm_firstkey(dbp) : dbm_nextkey(dbp);
1157 return key->dptr != NULL;
1158 }
1159
1160 /* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */
1161 static inline void
1162 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
1163 { }
1164
1165 /* EXIM_DBCLOSE */
1166 static inline void
1167 exim_dbclose__(EXIM_DB * dbp)
1168 { dbm_close(dbp); }
1169
1170 /* Datum access types */
1171
1172 static inline uschar *
1173 exim_datum_data_get(EXIM_DATUM * dp)
1174 { return US dp->dptr; }
1175 static inline void
1176 exim_datum_data_set(EXIM_DATUM * dp, void * s)
1177 { dp->dptr = s; }
1178
1179 static inline unsigned
1180 exim_datum_size_get(EXIM_DATUM * dp)
1181 { return dp->dsize; }
1182 static inline void
1183 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
1184 { dp->dsize = n; }
1185
1186 /* There's no clearing required before use, and we don't have to free anything
1187 after reading data. */
1188
1189 static inline void
1190 exim_datum_init(EXIM_DATUM * d)
1191 { }
1192
1193 static inline void
1194 exim_datum_free(EXIM_DATUM * d)
1195 { }
1196
1197 /* size limit */
1198
1199 # define EXIM_DB_RLIMIT 150
1200
1201 #endif /* !USE_GDBM */
1202
1203
1204
1205
1206
1207 #if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF)
1208
1209 static inline EXIM_DB *
1210 exim_dbopen(const uschar * name, const uschar * dirname, int flags,
1211   unsigned mode)
1212 {
1213 return exim_dbopen__(name, dirname, flags, mode);
1214 }
1215
1216 static inline void
1217 exim_dbclose(EXIM_DB * dbp)
1218 { exim_dbclose__(dbp); }
1219
1220 #else   /*  exim mainline code */
1221
1222 /* Wrappers for open/close with debug tracing */
1223
1224 extern void debug_printf_indent(const char *, ...);
1225 static inline BOOL is_tainted(const void *);
1226
1227 static inline EXIM_DB *
1228 exim_dbopen(const uschar * name, const uschar * dirname, int flags,
1229   unsigned mode)
1230 {
1231 void * dbp;
1232 DEBUG(D_hints_lookup)
1233   debug_printf_indent("EXIM_DBOPEN: file <%s> dir <%s> flags=%s\n",
1234     name, dirname,
1235     flags == O_RDONLY ? "O_RDONLY"
1236     : flags == O_RDWR ? "O_RDWR"
1237     : flags == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT"
1238     : "??");
1239 if (is_tainted(name) || is_tainted(dirname))
1240   {
1241   log_write(0, LOG_MAIN|LOG_PANIC, "Tainted name for DB file not permitted");
1242   dbp = NULL;
1243   }
1244 else
1245   dbp = exim_dbopen__(name, dirname, flags, mode);
1246
1247 DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", dbp);
1248 return dbp;
1249 }
1250
1251 static inline void
1252 exim_dbclose(EXIM_DB * dbp)
1253 {
1254 DEBUG(D_hints_lookup) debug_printf_indent("EXIM_DBCLOSE(%p)\n", dbp);
1255 exim_dbclose__(dbp);
1256 }
1257
1258 # endif         /* defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) */
1259
1260 /********************* End of dbm library definitions **********************/
1261
1262
1263 #endif  /* whole file */
1264 /* End of hintsdb.h */
1265 /* vi: aw ai sw=2
1266 */