c99e948b59a532294e87a564ffe3874f3682e0bc
[exim.git] / src / src / lookups / dbmdb.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2020 - 2022 */
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-only */
9
10 #include "../exim.h"
11 #include "lf_functions.h"
12
13
14 /*************************************************
15 *              Open entry point                  *
16 *************************************************/
17
18 /* See local README for interface description */
19
20 static void *
21 dbmdb_open(const uschar * filename, uschar ** errmsg)
22 {
23 uschar * dirname = string_copy(filename);
24 uschar * s;
25 EXIM_DB * yield = NULL;
26
27 if ((s = Ustrrchr(dirname, '/'))) *s = '\0';
28 if (!(yield = exim_dbopen(filename, dirname, O_RDONLY, 0)))
29   *errmsg = string_open_failed("%s as a %s file", filename, EXIM_DBTYPE);
30 return yield;
31 }
32
33
34
35 /*************************************************
36 *             Check entry point                  *
37 *************************************************/
38
39 /* This needs to know more about the underlying files than is good for it!
40 We need to know what the real file names are in order to check the owners and
41 modes. If USE_DB is set, we know it is Berkeley DB, which uses an unmodified
42 file name. If USE_TDB or USE_GDBM is set, we know it is tdb or gdbm, which do
43 the same. Otherwise, for safety, we have to check for x.db or x.dir and x.pag.
44 */
45
46 static BOOL
47 dbmdb_check(void *handle, const uschar *filename, int modemask, uid_t *owners,
48   gid_t *owngroups, uschar **errmsg)
49 {
50 int rc;
51
52 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
53 rc = lf_check_file(-1, filename, S_IFREG, modemask, owners, owngroups,
54   "dbm", errmsg);
55 #else
56   {
57   uschar filebuffer[256];
58   (void)sprintf(CS filebuffer, "%.250s.db", filename);
59   rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
60     "dbm", errmsg);
61   if (rc < 0)        /* stat() failed */
62     {
63     (void)sprintf(CS filebuffer, "%.250s.dir", filename);
64     rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
65       "dbm", errmsg);
66     if (rc == 0)     /* x.dir was OK */
67       {
68       (void)sprintf(CS filebuffer, "%.250s.pag", filename);
69       rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
70         "dbm", errmsg);
71       }
72     }
73   }
74 #endif
75
76 return rc == 0;
77 }
78
79
80
81 /*************************************************
82 *              Find entry point                  *
83 *************************************************/
84
85 /* See local README for interface description. This function adds 1 to
86 the keylength in order to include the terminating zero. */
87
88 static int
89 dbmdb_find(void * handle, const uschar * filename, const uschar * keystring,
90   int length, uschar ** result, uschar ** errmsg, uint * do_cache,
91   const uschar * opts)
92 {
93 EXIM_DB *d = (EXIM_DB *)handle;
94 EXIM_DATUM key, data;
95
96 exim_datum_init(&key);               /* Some DBM libraries require datums to */
97 exim_datum_init(&data);              /* be cleared before use. */
98 length++;
99 exim_datum_data_set(&key,
100   memcpy(store_get(length, keystring), keystring, length)); /* key can have embedded NUL */
101 exim_datum_size_set(&key, length);
102
103 if (exim_dbget(d, &key, &data))
104   {
105   *result = string_copyn(exim_datum_data_get(&data), exim_datum_size_get(&data));
106   exim_datum_free(&data);            /* Some DBM libraries need a free() call */
107   return OK;
108   }
109 return FAIL;
110 }
111
112
113
114 /*************************************************
115 *      Find entry point - no zero on key         *
116 *************************************************/
117
118 /* See local README for interface description */
119
120 static int
121 dbmnz_find(void * handle, const uschar * filename, const uschar * keystring,
122   int length, uschar ** result, uschar ** errmsg, uint * do_cache,
123   const uschar * opts)
124 {
125 return dbmdb_find(handle, filename, keystring, length-1, result, errmsg,
126   do_cache, opts);
127 }
128
129
130
131 /*************************************************
132 *     Find entry point - zero-joined list key    *
133 *************************************************/
134
135 /*
136  * The parameter passed as a key is a list in normal Exim list syntax.
137  * The elements of that list are joined together on NUL, with no trailing
138  * NUL, to form the key.
139  */
140
141 static int
142 dbmjz_find(void * handle, const uschar * filename, const uschar * keystring,
143   int length, uschar ** result, uschar ** errmsg, uint * do_cache,
144   const uschar * opts)
145 {
146 uschar *key_item, *key_buffer, *key_p;
147 const uschar *key_elems = keystring;
148 int buflen, bufleft, key_item_len, sep = 0;
149
150 /* To a first approximation, the size of the lookup key needs to be about,
151 or less than, the length of the delimited list passed in + 1. */
152
153 buflen = length + 3;
154 key_buffer = store_get(buflen, keystring);
155
156 key_buffer[0] = '\0';
157
158 key_p = key_buffer;
159 bufleft = buflen;
160
161 /* In all cases of an empty list item, we can set 1 and advance by 1 and then
162 pick up the trailing NUL from the previous list item, EXCEPT when at the
163 beginning of the output string, in which case we need to supply that NUL
164 ourselves.  */
165 while ((key_item = string_nextinlist(&key_elems, &sep, key_p, bufleft)) != NULL)
166   {
167   key_item_len = Ustrlen(key_item) + 1;
168   if (key_item_len == 1)
169     {
170     key_p[0] = '\0';
171     if (key_p == key_buffer)
172       {
173       key_p[1] = '\0';
174       key_item_len += 1;
175       }
176     }
177
178   bufleft -= key_item_len;
179   if (bufleft <= 0)
180     {
181     /* The string_nextinlist() will stop at buffer size, but we should always
182     have at least 1 character extra, so some assumption has failed. */
183     *errmsg = string_copy(US"Ran out of buffer space for joining elements");
184     return DEFER;
185     }
186   key_p += key_item_len;
187   }
188
189 if (key_p == key_buffer)
190   {
191   *errmsg = string_copy(US"empty list key");
192   return FAIL;
193   }
194
195 /* We do not pass in the final NULL; if needed, the list should include an
196 empty element to put one in. Boundary: key length 1, is a NULL */
197 key_item_len = key_p - key_buffer - 1;
198
199 DEBUG(D_lookup) debug_printf_indent("NUL-joined key length: %d\n", key_item_len);
200
201 /* beware that dbmdb_find() adds 1 to length to get back terminating NUL, so
202 because we've calculated the real length, we need to subtract one more here */
203
204 return dbmdb_find(handle, filename, key_buffer, key_item_len - 1,
205     result, errmsg, do_cache, opts);
206 }
207
208
209
210 /*************************************************
211 *              Close entry point                 *
212 *************************************************/
213
214 /* See local README for interface description */
215
216 void
217 static dbmdb_close(void *handle)
218 {
219 exim_dbclose((EXIM_DB *)handle);
220 }
221
222
223
224 /*************************************************
225 *         Version reporting entry point          *
226 *************************************************/
227
228 /* See local README for interface description. */
229
230 #include "../version.h"
231
232 gstring *
233 dbm_version_report(gstring * g)
234 {
235 #ifdef DYNLOOKUP
236 g = string_fmt_append(g, "Library version: DBM: Exim version %s\n", EXIM_VERSION_STR);
237 #endif
238 return g;
239 }
240
241
242 lookup_info dbm_lookup_info = {
243   .name = US"dbm",                      /* lookup name */
244   .type = lookup_absfile,               /* uses absolute file name */
245   .open = dbmdb_open,                   /* open function */
246   .check = dbmdb_check,                 /* check function */
247   .find = dbmdb_find,                   /* find function */
248   .close = dbmdb_close,                 /* close function */
249   .tidy = NULL,                         /* no tidy function */
250   .quote = NULL,                        /* no quoting function */
251   .version_report = dbm_version_report             /* version reporting */
252 };
253
254 lookup_info dbmz_lookup_info = {
255   .name = US"dbmnz",                    /* lookup name */
256   .type = lookup_absfile,               /* uses absolute file name */
257   .open = dbmdb_open,                   /* sic */     /* open function */
258   .check = dbmdb_check,                 /* sic */     /* check function */
259   .find = dbmnz_find,                   /* find function */
260   .close = dbmdb_close,                 /* sic */     /* close function */
261   .tidy = NULL,                         /* no tidy function */
262   .quote = NULL,                        /* no quoting function */
263   .version_report = NULL                           /* no version reporting (redundant) */
264 };
265
266 lookup_info dbmjz_lookup_info = {
267   .name = US"dbmjz",                    /* lookup name */
268   .type = lookup_absfile,               /* uses absolute file name */
269   .open = dbmdb_open,                   /* sic */     /* open function */
270   .check = dbmdb_check,                 /* sic */     /* check function */
271   .find = dbmjz_find,                   /* find function */
272   .close = dbmdb_close,                 /* sic */     /* close function */
273   .tidy = NULL,                         /* no tidy function */
274   .quote = NULL,                        /* no quoting function */
275   .version_report = NULL                           /* no version reporting (redundant) */
276 };
277
278 #ifdef DYNLOOKUP
279 #define dbmdb_lookup_module_info _lookup_module_info
280 #endif
281
282 static lookup_info *_lookup_list[] = { &dbm_lookup_info, &dbmz_lookup_info, &dbmjz_lookup_info };
283 lookup_module_info dbmdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 3 };
284
285 /* End of lookups/dbmdb.c */