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