1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
9 /* A small freestanding program to build dbm databases from serial input. For
10 alias files, this program fulfils the function of the newaliases program used
11 by other mailers, but it can be used for other dbm data files too. It operates
12 by writing a new file or files, and then renaming; otherwise old entries can
13 never get flushed out.
15 This program is clever enough to cope with ndbm, which creates two files called
16 <name>.dir and <name>.pag, or with db, which creates a single file called
17 <name>.db. If native db is in use (USE_DB defined) or tdb is in use (USE_TDB
18 defined) there is no extension to the output filename. This is also handled. If
19 there are any other variants, the program won't cope.
21 The first argument to the program is the name of the serial file; the second
22 is the base name for the DBM file(s). When native db is in use, these must be
25 Input lines beginning with # are ignored, as are blank lines. Entries begin
26 with a key terminated by a colon or end of line or whitespace and continue with
27 indented lines. Keys may be quoted if they contain colons or whitespace or #
33 uschar * spool_directory = NULL; /* dummy for dbstuff.h */
35 /******************************************************************************/
36 /* dummies needed by Solaris build */
41 readconf_printtime(int t)
44 store_get_3(int size, BOOL tainted, const char *filename, int linenumber)
47 store_reset_3(void **ptr, int pool, const char *filename, int linenumber)
50 store_release_above_3(void *ptr, const char *func, int linenumber)
53 string_vformat_trc(gstring * g, const uschar * func, unsigned line,
54 unsigned size_limit, unsigned flags, const char *format, va_list ap)
57 string_sprintf_trc(const char * a, const uschar * b, unsigned c, ...)
60 string_format_trc(uschar * buf, int len, const uschar * func, unsigned line,
61 const char * fmt, ...)
65 struct global_flags f;
66 unsigned int log_selector[1];
68 BOOL split_spool_directory;
69 /******************************************************************************/
72 #define max_insize 20000
73 #define max_outsize 100000
75 /* This is global because it's defined in the headers and compilers grumble
76 if it is made static. */
78 const uschar *hex_digits = CUS"0123456789abcdef";
81 #ifdef STRERROR_FROM_ERRLIST
82 /* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
83 in their libraries, but can provide the same facility by this simple
84 alternative function. */
89 if (n < 0 || n >= sys_nerr) return "unknown error number";
90 return sys_errlist[n];
92 #endif /* STRERROR_FROM_ERRLIST */
95 /* For Berkeley DB >= 2, we can define a function to be called in case of DB
96 errors. This should help with debugging strange DB problems, e.g. getting "File
97 exists" when you try to open a db file. The API changed at release 4.3. */
99 #if defined(USE_DB) && defined(DB_VERSION_STRING)
101 # if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
102 dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg)
106 dbfn_bdb_error_callback(const char *pfx, char *msg)
110 printf("Berkeley DB error: %s\n", msg);
116 /*************************************************
117 * Interpret escape sequence *
118 *************************************************/
120 /* This function is copied from the main Exim code.
123 pp points a pointer to the initiating "\" in the string;
124 the pointer gets updated to point to the final character
125 Returns: the value of the character escape
129 string_interpret_escape(const uschar **pp)
132 const uschar *p = *pp;
134 if (ch == '\0') return **pp;
135 if (isdigit(ch) && ch != '8' && ch != '9')
138 if (isdigit(p[1]) && p[1] != '8' && p[1] != '9')
140 ch = ch * 8 + *(++p) - '0';
141 if (isdigit(p[1]) && p[1] != '8' && p[1] != '9')
142 ch = ch * 8 + *(++p) - '0';
147 case 'n': ch = '\n'; break;
148 case 'r': ch = '\r'; break;
149 case 't': ch = '\t'; break;
155 Ustrchr(hex_digits, tolower(*(++p))) - hex_digits;
156 if (isxdigit(p[1])) ch = ch * 16 +
157 Ustrchr(hex_digits, tolower(*(++p))) - hex_digits;
166 /*************************************************
168 *************************************************/
170 int main(int argc, char **argv)
178 BOOL lowercase = TRUE;
181 BOOL lastdup = FALSE;
182 #if !defined (USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM)
188 EXIM_DATUM key, content;
190 uschar keybuffer[256];
191 uschar temp_dbmname[512];
192 uschar real_dbmname[512];
194 uschar *buffer = malloc(max_outsize);
195 uschar *line = malloc(max_insize);
199 if (Ustrcmp(argv[arg], "-nolc") == 0) lowercase = FALSE;
200 else if (Ustrcmp(argv[arg], "-nowarn") == 0) warn = FALSE;
201 else if (Ustrcmp(argv[arg], "-lastdup") == 0) lastdup = TRUE;
202 else if (Ustrcmp(argv[arg], "-noduperr") == 0) duperr = FALSE;
203 else if (Ustrcmp(argv[arg], "-nozero") == 0) add_zero = 0;
211 printf("usage: exim_dbmbuild [-nolc] <source file> <dbm base name>\n");
215 if (Ustrcmp(argv[arg], "-") == 0)
217 else if (!(f = fopen(argv[arg], "rb")))
219 printf("exim_dbmbuild: unable to open %s: %s\n", argv[arg], strerror(errno));
223 /* By default Berkeley db does not put extensions on... which
226 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
227 if (Ustrcmp(argv[arg], argv[arg+1]) == 0)
229 printf("exim_dbmbuild: input and output filenames are the same\n");
234 /* Check length of filename; allow for adding .dbmbuild_temp and .db or
237 if (strlen(argv[arg+1]) > sizeof(temp_dbmname) - 20)
239 printf("exim_dbmbuild: output filename is ridiculously long\n");
243 Ustrcpy(temp_dbmname, US argv[arg+1]);
244 Ustrcat(temp_dbmname, US".dbmbuild_temp");
246 Ustrcpy(dirname, temp_dbmname);
247 if ((bptr = Ustrrchr(dirname, '/')))
250 Ustrcpy(dirname, US".");
252 /* It is apparently necessary to open with O_RDWR for this to work
253 with gdbm-1.7.3, though no reading is actually going to be done. */
255 EXIM_DBOPEN(temp_dbmname, dirname, O_RDWR|O_CREAT|O_EXCL, 0644, &d);
259 printf("exim_dbmbuild: unable to create %s: %s\n", temp_dbmname,
265 /* Unless using native db calls, see if we have created <name>.db; if not,
266 assume .dir & .pag */
268 #if !defined(USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM)
269 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
270 is_db = Ustat(real_dbmname, &statbuf) == 0;
273 /* Now do the business */
278 while (Ufgets(line, max_insize, f) != NULL)
281 int len = Ustrlen(line);
285 if (len >= max_insize - 1 && p[-1] != '\n')
287 printf("Overlong line read: max permitted length is %d\n", max_insize - 1);
292 if (line[0] == '#') continue;
293 while (p > line && isspace(p[-1])) p--;
295 if (line[0] == 0) continue;
297 /* A continuation line is valid only if there was a previous first
300 if (isspace(line[0]))
305 printf("Unexpected continuation line ignored\n%s\n\n", line);
308 while (isspace(*s)) s++;
311 if (bptr - buffer + p - s >= max_outsize - 1)
313 printf("Continued set of lines is too long: max permitted length is %d\n",
323 /* A first line must have a name followed by a colon or whitespace or
324 end of line, but first finish with a previous line. The key is lower
325 cased by default - this is what the newaliases program for sendmail does.
326 However, there's an option not to do this. */
336 EXIM_DATUM_INIT(content);
337 EXIM_DATUM_DATA(content) = CS buffer;
338 EXIM_DATUM_SIZE(content) = bptr - buffer + add_zero;
340 switch(rc = EXIM_DBPUTB(d, key, content))
346 case EXIM_DBPUTB_DUP:
347 if (warn) fprintf(stderr, "** Duplicate key \"%s\"\n", keybuffer);
349 if(duperr) yield = 1;
350 if (lastdup) EXIM_DBPUT(d, key, content);
354 fprintf(stderr, "Error %d while writing key %s: errno=%d\n", rc,
363 EXIM_DATUM_INIT(key);
364 EXIM_DATUM_DATA(key) = CS keybuffer;
366 /* Deal with quoted keys. Escape sequences always make one character
367 out of several, so we can re-build in place. */
373 while (*s != 0 && *s != '\"')
376 ? string_interpret_escape((const uschar **)&s)
380 if (*s != 0) s++; /* Past terminating " */
381 EXIM_DATUM_SIZE(key) = t - keystart + add_zero;
386 while (*s != 0 && *s != ':' && !isspace(*s)) s++;
387 EXIM_DATUM_SIZE(key) = s - keystart + add_zero;
390 if (EXIM_DATUM_SIZE(key) > 256)
392 printf("Keys longer than 255 characters cannot be handled\n");
399 for (i = 0; i < EXIM_DATUM_SIZE(key) - add_zero; i++)
400 keybuffer[i] = tolower(keystart[i]);
402 for (i = 0; i < EXIM_DATUM_SIZE(key) - add_zero; i++)
403 keybuffer[i] = keystart[i];
408 while (isspace(*s))s++;
412 while (isspace(*s))s++;
426 EXIM_DATUM_INIT(content);
427 EXIM_DATUM_DATA(content) = CS buffer;
428 EXIM_DATUM_SIZE(content) = bptr - buffer + add_zero;
430 switch(rc = EXIM_DBPUTB(d, key, content))
436 case EXIM_DBPUTB_DUP:
437 if (warn) fprintf(stderr, "** Duplicate key \"%s\"\n", keybuffer);
439 if (duperr) yield = 1;
440 if (lastdup) EXIM_DBPUT(d, key, content);
444 fprintf(stderr, "Error %d while writing key %s: errno=%d\n", rc,
451 /* Close files, rename or abandon the temporary files, and exit */
458 /* If successful, output the number of entries and rename the temporary
461 if (yield == 0 || yield == 1)
463 printf("%d entr%s written\n", count, (count == 1)? "y" : "ies");
466 printf("%d duplicate key%s \n", dupcount, (dupcount > 1)? "s" : "");
469 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
470 Ustrcpy(real_dbmname, temp_dbmname);
471 Ustrcpy(buffer, US argv[arg+1]);
472 if (Urename(real_dbmname, buffer) != 0)
474 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
479 /* Rename a single .db file */
483 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
484 sprintf(CS buffer, "%s.db", argv[arg+1]);
485 if (Urename(real_dbmname, buffer) != 0)
487 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
492 /* Rename .dir and .pag files */
496 sprintf(CS real_dbmname, "%s.dir", temp_dbmname);
497 sprintf(CS buffer, "%s.dir", argv[arg+1]);
498 if (Urename(real_dbmname, buffer) != 0)
500 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
504 sprintf(CS real_dbmname, "%s.pag", temp_dbmname);
505 sprintf(CS buffer, "%s.pag", argv[arg+1]);
506 if (Urename(real_dbmname, buffer) != 0)
508 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
513 #endif /* USE_DB || USE_TDB || USE_GDBM */
516 /* Otherwise unlink the temporary files. */
520 printf("dbmbuild abandoned\n");
521 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
522 /* We created it, so safe to delete despite the name coming from outside */
523 /* coverity[tainted_string] */
524 Uunlink(temp_dbmname);
528 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
529 Uunlink(real_dbmname);
533 sprintf(CS real_dbmname, "%s.dir", temp_dbmname);
534 Uunlink(real_dbmname);
535 sprintf(CS real_dbmname, "%s.pag", temp_dbmname);
536 Uunlink(real_dbmname);
538 #endif /* USE_DB || USE_TDB */
544 /* End of exim_dbmbuild.c */