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 struct global_flags f;
61 unsigned int log_selector[1];
63 BOOL split_spool_directory;
64 /******************************************************************************/
67 #define max_insize 20000
68 #define max_outsize 100000
70 /* This is global because it's defined in the headers and compilers grumble
71 if it is made static. */
73 const uschar *hex_digits = CUS"0123456789abcdef";
76 #ifdef STRERROR_FROM_ERRLIST
77 /* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
78 in their libraries, but can provide the same facility by this simple
79 alternative function. */
84 if (n < 0 || n >= sys_nerr) return "unknown error number";
85 return sys_errlist[n];
87 #endif /* STRERROR_FROM_ERRLIST */
90 /* For Berkeley DB >= 2, we can define a function to be called in case of DB
91 errors. This should help with debugging strange DB problems, e.g. getting "File
92 exists" when you try to open a db file. The API changed at release 4.3. */
94 #if defined(USE_DB) && defined(DB_VERSION_STRING)
96 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
97 dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg)
101 dbfn_bdb_error_callback(const char *pfx, char *msg)
105 printf("Berkeley DB error: %s\n", msg);
111 /*************************************************
112 * Interpret escape sequence *
113 *************************************************/
115 /* This function is copied from the main Exim code.
118 pp points a pointer to the initiating "\" in the string;
119 the pointer gets updated to point to the final character
120 Returns: the value of the character escape
124 string_interpret_escape(const uschar **pp)
127 const uschar *p = *pp;
129 if (ch == '\0') return **pp;
130 if (isdigit(ch) && ch != '8' && ch != '9')
133 if (isdigit(p[1]) && p[1] != '8' && p[1] != '9')
135 ch = ch * 8 + *(++p) - '0';
136 if (isdigit(p[1]) && p[1] != '8' && p[1] != '9')
137 ch = ch * 8 + *(++p) - '0';
142 case 'n': ch = '\n'; break;
143 case 'r': ch = '\r'; break;
144 case 't': ch = '\t'; break;
150 Ustrchr(hex_digits, tolower(*(++p))) - hex_digits;
151 if (isxdigit(p[1])) ch = ch * 16 +
152 Ustrchr(hex_digits, tolower(*(++p))) - hex_digits;
161 /*************************************************
163 *************************************************/
165 int main(int argc, char **argv)
173 BOOL lowercase = TRUE;
176 BOOL lastdup = FALSE;
177 #if !defined (USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM)
183 EXIM_DATUM key, content;
185 uschar keybuffer[256];
186 uschar temp_dbmname[512];
187 uschar real_dbmname[512];
189 uschar *buffer = malloc(max_outsize);
190 uschar *line = malloc(max_insize);
194 if (Ustrcmp(argv[arg], "-nolc") == 0) lowercase = FALSE;
195 else if (Ustrcmp(argv[arg], "-nowarn") == 0) warn = FALSE;
196 else if (Ustrcmp(argv[arg], "-lastdup") == 0) lastdup = TRUE;
197 else if (Ustrcmp(argv[arg], "-noduperr") == 0) duperr = FALSE;
198 else if (Ustrcmp(argv[arg], "-nozero") == 0) add_zero = 0;
206 printf("usage: exim_dbmbuild [-nolc] <source file> <dbm base name>\n");
210 if (Ustrcmp(argv[arg], "-") == 0) f = stdin; else
212 f = fopen(argv[arg], "rb");
215 printf("exim_dbmbuild: unable to open %s: %s\n", argv[arg], strerror(errno));
220 /* By default Berkeley db does not put extensions on... which
223 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
224 if (Ustrcmp(argv[arg], argv[arg+1]) == 0)
226 printf("exim_dbmbuild: input and output filenames are the same\n");
231 /* Check length of filename; allow for adding .dbmbuild_temp and .db or
234 if (strlen(argv[arg+1]) > sizeof(temp_dbmname) - 20)
236 printf("exim_dbmbuild: output filename is ridiculously long\n");
240 Ustrcpy(temp_dbmname, US argv[arg+1]);
241 Ustrcat(temp_dbmname, US".dbmbuild_temp");
243 Ustrcpy(dirname, temp_dbmname);
244 if ((bptr = Ustrrchr(dirname, '/')))
247 Ustrcpy(dirname, US".");
249 /* It is apparently necessary to open with O_RDWR for this to work
250 with gdbm-1.7.3, though no reading is actually going to be done. */
252 EXIM_DBOPEN(temp_dbmname, dirname, O_RDWR|O_CREAT|O_EXCL, 0644, &d);
256 printf("exim_dbmbuild: unable to create %s: %s\n", temp_dbmname,
262 /* Unless using native db calls, see if we have created <name>.db; if not,
263 assume .dir & .pag */
265 #if !defined(USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM)
266 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
267 is_db = Ustat(real_dbmname, &statbuf) == 0;
270 /* Now do the business */
275 while (Ufgets(line, max_insize, f) != NULL)
278 int len = Ustrlen(line);
282 if (len >= max_insize - 1 && p[-1] != '\n')
284 printf("Overlong line read: max permitted length is %d\n", max_insize - 1);
289 if (line[0] == '#') continue;
290 while (p > line && isspace(p[-1])) p--;
292 if (line[0] == 0) continue;
294 /* A continuation line is valid only if there was a previous first
297 if (isspace(line[0]))
302 printf("Unexpected continuation line ignored\n%s\n\n", line);
305 while (isspace(*s)) s++;
308 if (bptr - buffer + p - s >= max_outsize - 1)
310 printf("Continued set of lines is too long: max permitted length is %d\n",
320 /* A first line must have a name followed by a colon or whitespace or
321 end of line, but first finish with a previous line. The key is lower
322 cased by default - this is what the newaliases program for sendmail does.
323 However, there's an option not to do this. */
333 EXIM_DATUM_INIT(content);
334 EXIM_DATUM_DATA(content) = CS buffer;
335 EXIM_DATUM_SIZE(content) = bptr - buffer + add_zero;
337 switch(rc = EXIM_DBPUTB(d, key, content))
343 case EXIM_DBPUTB_DUP:
344 if (warn) fprintf(stderr, "** Duplicate key \"%s\"\n", keybuffer);
346 if(duperr) yield = 1;
347 if (lastdup) EXIM_DBPUT(d, key, content);
351 fprintf(stderr, "Error %d while writing key %s: errno=%d\n", rc,
360 EXIM_DATUM_INIT(key);
361 EXIM_DATUM_DATA(key) = CS keybuffer;
363 /* Deal with quoted keys. Escape sequences always make one character
364 out of several, so we can re-build in place. */
370 while (*s != 0 && *s != '\"')
373 ? string_interpret_escape((const uschar **)&s)
377 if (*s != 0) s++; /* Past terminating " */
378 EXIM_DATUM_SIZE(key) = t - keystart + add_zero;
383 while (*s != 0 && *s != ':' && !isspace(*s)) s++;
384 EXIM_DATUM_SIZE(key) = s - keystart + add_zero;
387 if (EXIM_DATUM_SIZE(key) > 256)
389 printf("Keys longer than 255 characters cannot be handled\n");
396 for (i = 0; i < EXIM_DATUM_SIZE(key) - add_zero; i++)
397 keybuffer[i] = tolower(keystart[i]);
399 for (i = 0; i < EXIM_DATUM_SIZE(key) - add_zero; i++)
400 keybuffer[i] = keystart[i];
405 while (isspace(*s))s++;
409 while (isspace(*s))s++;
423 EXIM_DATUM_INIT(content);
424 EXIM_DATUM_DATA(content) = CS buffer;
425 EXIM_DATUM_SIZE(content) = bptr - buffer + add_zero;
427 switch(rc = EXIM_DBPUTB(d, key, content))
433 case EXIM_DBPUTB_DUP:
434 if (warn) fprintf(stderr, "** Duplicate key \"%s\"\n", keybuffer);
436 if (duperr) yield = 1;
437 if (lastdup) EXIM_DBPUT(d, key, content);
441 fprintf(stderr, "Error %d while writing key %s: errno=%d\n", rc,
448 /* Close files, rename or abandon the temporary files, and exit */
455 /* If successful, output the number of entries and rename the temporary
458 if (yield == 0 || yield == 1)
460 printf("%d entr%s written\n", count, (count == 1)? "y" : "ies");
463 printf("%d duplicate key%s \n", dupcount, (dupcount > 1)? "s" : "");
466 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
467 Ustrcpy(real_dbmname, temp_dbmname);
468 Ustrcpy(buffer, US argv[arg+1]);
469 if (Urename(real_dbmname, buffer) != 0)
471 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
476 /* Rename a single .db file */
480 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
481 sprintf(CS buffer, "%s.db", argv[arg+1]);
482 if (Urename(real_dbmname, buffer) != 0)
484 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
489 /* Rename .dir and .pag files */
493 sprintf(CS real_dbmname, "%s.dir", temp_dbmname);
494 sprintf(CS buffer, "%s.dir", argv[arg+1]);
495 if (Urename(real_dbmname, buffer) != 0)
497 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
501 sprintf(CS real_dbmname, "%s.pag", temp_dbmname);
502 sprintf(CS buffer, "%s.pag", argv[arg+1]);
503 if (Urename(real_dbmname, buffer) != 0)
505 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
510 #endif /* USE_DB || USE_TDB || USE_GDBM */
513 /* Otherwise unlink the temporary files. */
517 printf("dbmbuild abandoned\n");
518 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
519 /* We created it, so safe to delete despite the name coming from outside */
520 /* coverity[tainted_string] */
521 Uunlink(temp_dbmname);
525 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
526 Uunlink(real_dbmname);
530 sprintf(CS real_dbmname, "%s.dir", temp_dbmname);
531 Uunlink(real_dbmname);
532 sprintf(CS real_dbmname, "%s.pag", temp_dbmname);
533 Uunlink(real_dbmname);
535 #endif /* USE_DB || USE_TDB */
541 /* End of exim_dbmbuild.c */