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, ...)
64 log_write(unsigned int selector, int flags, const char *format, ...)
68 struct global_flags f;
69 unsigned int log_selector[1];
71 BOOL split_spool_directory;
72 /******************************************************************************/
75 #define max_insize 20000
76 #define max_outsize 100000
78 /* This is global because it's defined in the headers and compilers grumble
79 if it is made static. */
81 const uschar *hex_digits = CUS"0123456789abcdef";
84 #ifdef STRERROR_FROM_ERRLIST
85 /* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
86 in their libraries, but can provide the same facility by this simple
87 alternative function. */
92 if (n < 0 || n >= sys_nerr) return "unknown error number";
93 return sys_errlist[n];
95 #endif /* STRERROR_FROM_ERRLIST */
98 /* For Berkeley DB >= 2, we can define a function to be called in case of DB
99 errors. This should help with debugging strange DB problems, e.g. getting "File
100 exists" when you try to open a db file. The API changed at release 4.3. */
102 #if defined(USE_DB) && defined(DB_VERSION_STRING)
104 # if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
105 dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg)
109 dbfn_bdb_error_callback(const char *pfx, char *msg)
113 printf("Berkeley DB error: %s\n", msg);
119 /*************************************************
120 * Interpret escape sequence *
121 *************************************************/
123 /* This function is copied from the main Exim code.
126 pp points a pointer to the initiating "\" in the string;
127 the pointer gets updated to point to the final character
128 Returns: the value of the character escape
132 string_interpret_escape(const uschar **pp)
135 const uschar *p = *pp;
137 if (ch == '\0') return **pp;
138 if (isdigit(ch) && ch != '8' && ch != '9')
141 if (isdigit(p[1]) && p[1] != '8' && p[1] != '9')
143 ch = ch * 8 + *(++p) - '0';
144 if (isdigit(p[1]) && p[1] != '8' && p[1] != '9')
145 ch = ch * 8 + *(++p) - '0';
150 case 'n': ch = '\n'; break;
151 case 'r': ch = '\r'; break;
152 case 't': ch = '\t'; break;
158 Ustrchr(hex_digits, tolower(*(++p))) - hex_digits;
159 if (isxdigit(p[1])) ch = ch * 16 +
160 Ustrchr(hex_digits, tolower(*(++p))) - hex_digits;
169 /*************************************************
171 *************************************************/
173 int main(int argc, char **argv)
181 BOOL lowercase = TRUE;
184 BOOL lastdup = FALSE;
185 #if !defined (USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM)
191 EXIM_DATUM key, content;
193 uschar keybuffer[256];
194 uschar temp_dbmname[512];
195 uschar real_dbmname[512];
197 uschar *buffer = malloc(max_outsize);
198 uschar *line = malloc(max_insize);
202 if (Ustrcmp(argv[arg], "-nolc") == 0) lowercase = FALSE;
203 else if (Ustrcmp(argv[arg], "-nowarn") == 0) warn = FALSE;
204 else if (Ustrcmp(argv[arg], "-lastdup") == 0) lastdup = TRUE;
205 else if (Ustrcmp(argv[arg], "-noduperr") == 0) duperr = FALSE;
206 else if (Ustrcmp(argv[arg], "-nozero") == 0) add_zero = 0;
214 printf("usage: exim_dbmbuild [-nolc] <source file> <dbm base name>\n");
218 if (Ustrcmp(argv[arg], "-") == 0)
220 else if (!(f = fopen(argv[arg], "rb")))
222 printf("exim_dbmbuild: unable to open %s: %s\n", argv[arg], strerror(errno));
226 /* By default Berkeley db does not put extensions on... which
229 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
230 if (Ustrcmp(argv[arg], argv[arg+1]) == 0)
232 printf("exim_dbmbuild: input and output filenames are the same\n");
237 /* Check length of filename; allow for adding .dbmbuild_temp and .db or
240 if (strlen(argv[arg+1]) > sizeof(temp_dbmname) - 20)
242 printf("exim_dbmbuild: output filename is ridiculously long\n");
246 Ustrcpy(temp_dbmname, US argv[arg+1]);
247 Ustrcat(temp_dbmname, US".dbmbuild_temp");
249 Ustrcpy(dirname, temp_dbmname);
250 if ((bptr = Ustrrchr(dirname, '/')))
253 Ustrcpy(dirname, US".");
255 /* It is apparently necessary to open with O_RDWR for this to work
256 with gdbm-1.7.3, though no reading is actually going to be done. */
258 EXIM_DBOPEN(temp_dbmname, dirname, O_RDWR|O_CREAT|O_EXCL, 0644, &d);
262 printf("exim_dbmbuild: unable to create %s: %s\n", temp_dbmname,
268 /* Unless using native db calls, see if we have created <name>.db; if not,
269 assume .dir & .pag */
271 #if !defined(USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM)
272 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
273 is_db = Ustat(real_dbmname, &statbuf) == 0;
276 /* Now do the business */
281 while (Ufgets(line, max_insize, f) != NULL)
284 int len = Ustrlen(line);
288 if (len >= max_insize - 1 && p[-1] != '\n')
290 printf("Overlong line read: max permitted length is %d\n", max_insize - 1);
295 if (line[0] == '#') continue;
296 while (p > line && isspace(p[-1])) p--;
298 if (line[0] == 0) continue;
300 /* A continuation line is valid only if there was a previous first
303 if (isspace(line[0]))
308 printf("Unexpected continuation line ignored\n%s\n\n", line);
311 while (isspace(*s)) s++;
314 if (bptr - buffer + p - s >= max_outsize - 1)
316 printf("Continued set of lines is too long: max permitted length is %d\n",
326 /* A first line must have a name followed by a colon or whitespace or
327 end of line, but first finish with a previous line. The key is lower
328 cased by default - this is what the newaliases program for sendmail does.
329 However, there's an option not to do this. */
339 EXIM_DATUM_INIT(content);
340 EXIM_DATUM_DATA(content) = CS buffer;
341 EXIM_DATUM_SIZE(content) = bptr - buffer + add_zero;
343 switch(rc = EXIM_DBPUTB(d, key, content))
349 case EXIM_DBPUTB_DUP:
350 if (warn) fprintf(stderr, "** Duplicate key \"%s\"\n", keybuffer);
352 if(duperr) yield = 1;
353 if (lastdup) EXIM_DBPUT(d, key, content);
357 fprintf(stderr, "Error %d while writing key %s: errno=%d\n", rc,
366 EXIM_DATUM_INIT(key);
367 EXIM_DATUM_DATA(key) = CS keybuffer;
369 /* Deal with quoted keys. Escape sequences always make one character
370 out of several, so we can re-build in place. */
376 while (*s != 0 && *s != '\"')
379 ? string_interpret_escape((const uschar **)&s)
383 if (*s != 0) s++; /* Past terminating " */
384 EXIM_DATUM_SIZE(key) = t - keystart + add_zero;
389 while (*s != 0 && *s != ':' && !isspace(*s)) s++;
390 EXIM_DATUM_SIZE(key) = s - keystart + add_zero;
393 if (EXIM_DATUM_SIZE(key) > 256)
395 printf("Keys longer than 255 characters cannot be handled\n");
402 for (i = 0; i < EXIM_DATUM_SIZE(key) - add_zero; i++)
403 keybuffer[i] = tolower(keystart[i]);
405 for (i = 0; i < EXIM_DATUM_SIZE(key) - add_zero; i++)
406 keybuffer[i] = keystart[i];
411 while (isspace(*s))s++;
415 while (isspace(*s))s++;
429 EXIM_DATUM_INIT(content);
430 EXIM_DATUM_DATA(content) = CS buffer;
431 EXIM_DATUM_SIZE(content) = bptr - buffer + add_zero;
433 switch(rc = EXIM_DBPUTB(d, key, content))
439 case EXIM_DBPUTB_DUP:
440 if (warn) fprintf(stderr, "** Duplicate key \"%s\"\n", keybuffer);
442 if (duperr) yield = 1;
443 if (lastdup) EXIM_DBPUT(d, key, content);
447 fprintf(stderr, "Error %d while writing key %s: errno=%d\n", rc,
454 /* Close files, rename or abandon the temporary files, and exit */
461 /* If successful, output the number of entries and rename the temporary
464 if (yield == 0 || yield == 1)
466 printf("%d entr%s written\n", count, (count == 1)? "y" : "ies");
469 printf("%d duplicate key%s \n", dupcount, (dupcount > 1)? "s" : "");
472 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
473 Ustrcpy(real_dbmname, temp_dbmname);
474 Ustrcpy(buffer, US argv[arg+1]);
475 if (Urename(real_dbmname, buffer) != 0)
477 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
482 /* Rename a single .db file */
486 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
487 sprintf(CS buffer, "%s.db", argv[arg+1]);
488 if (Urename(real_dbmname, buffer) != 0)
490 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
495 /* Rename .dir and .pag files */
499 sprintf(CS real_dbmname, "%s.dir", temp_dbmname);
500 sprintf(CS buffer, "%s.dir", argv[arg+1]);
501 if (Urename(real_dbmname, buffer) != 0)
503 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
507 sprintf(CS real_dbmname, "%s.pag", temp_dbmname);
508 sprintf(CS buffer, "%s.pag", argv[arg+1]);
509 if (Urename(real_dbmname, buffer) != 0)
511 printf("Unable to rename %s as %s\n", real_dbmname, buffer);
516 #endif /* USE_DB || USE_TDB || USE_GDBM */
519 /* Otherwise unlink the temporary files. */
523 printf("dbmbuild abandoned\n");
524 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
525 /* We created it, so safe to delete despite the name coming from outside */
526 /* coverity[tainted_string] */
527 Uunlink(temp_dbmname);
531 sprintf(CS real_dbmname, "%s.db", temp_dbmname);
532 Uunlink(real_dbmname);
536 sprintf(CS real_dbmname, "%s.dir", temp_dbmname);
537 Uunlink(real_dbmname);
538 sprintf(CS real_dbmname, "%s.pag", temp_dbmname);
539 Uunlink(real_dbmname);
541 #endif /* USE_DB || USE_TDB */
547 /* End of exim_dbmbuild.c */