more cleanup for DSN drafts
[unix-history] / usr / src / usr.sbin / sendmail / src / alias.c
index 85bb039..8f9df51 100644 (file)
@@ -1,44 +1,21 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
  *
  * %sccs.include.redist.c%
  */
 
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <signal.h>
 # include "sendmail.h"
 # include "sendmail.h"
-# include <sys/file.h>
 # include <pwd.h>
 # include <pwd.h>
-# ifdef LOCKF
-# include <fcntl.h>
-# endif
-
-# ifdef DBM
-ERROR: DBM is no longer supported -- use NDBM instead.
-# endif
-
-# ifdef NEWDB
-# include <db.h>
-# endif
-
-# ifdef NDBM
-# include <ndbm.h>
-# endif
 
 #ifndef lint
 
 #ifndef lint
-#ifdef NEWDB
-static char sccsid[] = "@(#)alias.c    5.41 (Berkeley) %G% (with NEWDB)";
-#else
-#ifdef NDBM
-static char sccsid[] = "@(#)alias.c    5.41 (Berkeley) %G% (with NDBM)";
-#else
-static char sccsid[] = "@(#)alias.c    5.41 (Berkeley) %G% (without NDBM)";
-#endif
-#endif
+static char sccsid[] = "@(#)alias.c    8.40 (Berkeley) %G%";
 #endif /* not lint */
 #endif /* not lint */
+
+
+MAP    *AliasDB[MAXALIASDB + 1];       /* actual database list */
+int    NAliasDBs;                      /* number of alias databases */
 \f/*
 **  ALIAS -- Compute aliases.
 **
 \f/*
 **  ALIAS -- Compute aliases.
 **
@@ -50,6 +27,8 @@ static char sccsid[] = "@(#)alias.c   5.41 (Berkeley) %G% (without NDBM)";
 **             a -- address to alias.
 **             sendq -- a pointer to the head of the send queue
 **                     to put the aliases in.
 **             a -- address to alias.
 **             sendq -- a pointer to the head of the send queue
 **                     to put the aliases in.
+**             aliaslevel -- the current alias nesting depth.
+**             e -- the current envelope.
 **
 **     Returns:
 **             none
 **
 **     Returns:
 **             none
@@ -57,67 +36,33 @@ static char sccsid[] = "@(#)alias.c 5.41 (Berkeley) %G% (without NDBM)";
 **     Side Effects:
 **             Aliases found are expanded.
 **
 **     Side Effects:
 **             Aliases found are expanded.
 **
-**     Notes:
-**             If NoAlias (the "-n" flag) is set, no aliasing is
-**                     done.
-**
 **     Deficiencies:
 **             It should complain about names that are aliased to
 **                     nothing.
 */
 
 **     Deficiencies:
 **             It should complain about names that are aliased to
 **                     nothing.
 */
 
-
-/*
-**  Sun YP servers read the dbm files directly, so we have to build them
-**  even if NEWDB
-*/
-
-#ifdef NDBM
-# ifndef NEWDB
-#  define IF_MAKEDBMFILES
-# else
-#  ifdef YPCOMPAT
-#   define IF_MAKEDBMFILES             if (makedbmfiles)
-#  endif
-# endif
-#endif
-
-typedef union
-{
-#ifdef NDBM
-       datum   dbm;
-#endif
-#ifdef NEWDB
-       DBT     dbt;
-#endif
-       struct
-       {
-               char    *data;
-               int     size;
-       } xx;
-} DBdatum;
-
-#ifdef NEWDB
-static DB      *AliasDBptr;
-#endif
-#ifdef NDBM
-static DBM     *AliasDBMptr;
-#endif
-
-alias(a, sendq, e)
+void
+alias(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        ADDRESS **sendq;
        register ADDRESS *a;
        ADDRESS **sendq;
+       int aliaslevel;
        register ENVELOPE *e;
 {
        register char *p;
        register ENVELOPE *e;
 {
        register char *p;
+       int naliases;
+       char *owner;
+       char obuf[MAXNAME + 6];
        extern ADDRESS *sendto();
        extern char *aliaslookup();
 
        if (tTd(27, 1))
        extern ADDRESS *sendto();
        extern char *aliaslookup();
 
        if (tTd(27, 1))
-               printf("alias(%s)\n", a->q_paddr);
+               printf("alias(%s)\n", a->q_user);
 
        /* don't realias already aliased names */
 
        /* don't realias already aliased names */
-       if (bitset(QDONTSEND, a->q_flags))
+       if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
+               return;
+
+       if (NoAlias)
                return;
 
        e->e_to = a->q_paddr;
                return;
 
        e->e_to = a->q_paddr;
@@ -126,10 +71,7 @@ alias(a, sendq, e)
        **  Look up this name
        */
 
        **  Look up this name
        */
 
-       if (NoAlias)
-               p = NULL;
-       else
-               p = aliaslookup(a->q_user);
+       p = aliaslookup(a->q_user, e);
        if (p == NULL)
                return;
 
        if (p == NULL)
                return;
 
@@ -141,10 +83,56 @@ alias(a, sendq, e)
        if (tTd(27, 1))
                printf("%s (%s, %s) aliased to %s\n",
                    a->q_paddr, a->q_host, a->q_user, p);
        if (tTd(27, 1))
                printf("%s (%s, %s) aliased to %s\n",
                    a->q_paddr, a->q_host, a->q_user, p);
-       message(Arpa_Info, "aliased to %s", p);
-       AliasLevel++;
-       a->q_child = sendto(p, 1, a, 0);
-       AliasLevel--;
+       if (bitset(EF_VRFYONLY, e->e_flags))
+       {
+               a->q_flags |= QVERIFIED;
+               e->e_nrcpts++;
+               return;
+       }
+       message("aliased to %s", p);
+#ifdef LOG
+       if (LogLevel > 9)
+               syslog(LOG_INFO, "%s: alias %s => %s",
+                       e->e_id == NULL ? "NOQUEUE" : e->e_id,
+                       a->q_paddr, p);
+#endif
+       a->q_flags &= ~QSELFREF;
+       if (tTd(27, 5))
+       {
+               printf("alias: QDONTSEND ");
+               printaddr(a, FALSE);
+       }
+       a->q_flags |= QDONTSEND;
+       naliases = sendtolist(p, a, sendq, aliaslevel + 1, e);
+       if (bitset(QSELFREF, a->q_flags))
+               a->q_flags &= ~QDONTSEND;
+
+       /*
+       **  Look for owner of alias
+       */
+
+       (void) strcpy(obuf, "owner-");
+       if (strncmp(a->q_user, "owner-", 6) == 0)
+               (void) strcat(obuf, "owner");
+       else
+               (void) strcat(obuf, a->q_user);
+       if (!bitnset(M_USR_UPPER, a->q_mailer->m_flags))
+               makelower(obuf);
+       owner = aliaslookup(obuf, e);
+       if (owner == NULL)
+               return;
+
+       /* reflect owner into envelope sender */
+       if (strpbrk(owner, ",:/|\"") != NULL)
+               owner = obuf;
+       a->q_owner = newstr(owner);
+
+       /* announce delivery to this alias; NORECEIPT bit set later */
+       if (e->e_xfp != NULL)
+               fprintf(e->e_xfp, "Message delivered to mailing list %s\n",
+                       a->q_paddr);
+       e->e_flags |= EF_SENDRECEIPT;
+       a->q_flags |= QREPORT|QEXPLODED;
 }
 \f/*
 **  ALIASLOOKUP -- look up a name in the alias file.
 }
 \f/*
 **  ALIASLOOKUP -- look up a name in the alias file.
@@ -164,353 +152,410 @@ alias(a, sendq, e)
 */
 
 char *
 */
 
 char *
-aliaslookup(name)
+aliaslookup(name, e)
        char *name;
        char *name;
+       ENVELOPE *e;
 {
 {
-# if defined(NEWDB) || defined(NDBM)
-       DBdatum rhs, lhs;
-       int s;
-
-       /* create a key for fetch */
-       lhs.xx.data = name;
-       lhs.xx.size = strlen(name) + 1;
-# ifdef NEWDB
-       if (AliasDBptr != NULL)
-       {
-               s = AliasDBptr->get(AliasDBptr, &lhs.dbt, &rhs.dbt, 0);
-               if (s == 0)
-                       return (rhs.dbt.data);
-       }
-# ifdef NDBM
-       else
+       register int dbno;
+       register MAP *map;
+       register char *p;
+
+       for (dbno = 0; dbno < NAliasDBs; dbno++)
        {
        {
-               rhs.dbm = dbm_fetch(AliasDBMptr, lhs.dbm);
-               return (rhs.dbm.dptr);
+               auto int stat;
+
+               map = AliasDB[dbno];
+               if (!bitset(MF_OPEN, map->map_mflags))
+                       continue;
+               p = (*map->map_class->map_lookup)(map, name, NULL, &stat);
+               if (p != NULL)
+                       return p;
        }
        }
-# endif /* NDBM */
-# else /* not NEWDB */
-       rhs.dbm = dbm_fetch(AliasDBMptr, lhs.dbm);
-       return (rhs.dbm.dptr);
-# endif /* NEWDB */
-# else /* neither NEWDB nor NDBM */
-       register STAB *s;
-
-       s = stab(name, ST_ALIAS, ST_FIND);
-       if (s != NULL)
-               return (s->s_alias);
-# endif
-       return (NULL);
+       return NULL;
 }
 \f/*
 }
 \f/*
-**  INITALIASES -- initialize for aliasing
+**  SETALIAS -- set up an alias map
 **
 **
-**     Very different depending on whether we are running NDBM or not.
+**     Called when reading configuration file.
 **
 **     Parameters:
 **
 **     Parameters:
-**             aliasfile -- location of aliases.
-**             init -- if set and if NDBM, initialize the NDBM files.
+**             spec -- the alias specification
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
-**
-**     Side Effects:
-**             initializes aliases:
-**             if NDBM:  opens the database.
-**             if ~NDBM: reads the aliases into the symbol table.
 */
 
 */
 
-# define DBMMODE       0644
-
-initaliases(aliasfile, init, e)
-       char *aliasfile;
-       bool init;
-       register ENVELOPE *e;
+void
+setalias(spec)
+       char *spec;
 {
 {
-#if defined(NDBM) || defined(NEWDB)
-       int atcnt;
-       time_t modtime;
-       bool automatic = FALSE;
-       char buf[MAXNAME];
-#endif
-       struct stat stb;
-       static bool initialized = FALSE;
-       static int readaliases();
+       register char *p;
+       register MAP *map;
+       char *class;
+       STAB *s;
+       static bool first_unqual = TRUE;
 
 
-       if (initialized)
-               return;
-       initialized = TRUE;
+       if (tTd(27, 8))
+               printf("setalias(%s)\n", spec);
 
 
-       if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
+       for (p = spec; p != NULL; )
        {
        {
-               if (aliasfile != NULL && init)
-                       syserr("Cannot open %s", aliasfile);
-               NoAlias = TRUE;
-               errno = 0;
-               return;
-       }
+               while (isspace(*p))
+                       p++;
+               if (*p == '\0')
+                       break;
+               spec = p;
 
 
-# if defined(NDBM) || defined(NEWDB)
-       /*
-       **  Check to see that the alias file is complete.
-       **      If not, we will assume that someone died, and it is up
-       **      to us to rebuild it.
-       */
+               /*
+               **  Treat simple filename specially -- this is the file name
+               **  for the files implementation, not necessarily in order.
+               */
 
 
-       if (!init)
-       {
-# ifdef NEWDB
-               (void) strcpy(buf, aliasfile);
-               (void) strcat(buf, ".db");
-               AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
-               if (AliasDBptr == NULL)
+               if (spec[0] == '/' && first_unqual)
                {
                {
-# ifdef NDBM
-                       AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
-# else
-                       syserr("initaliases: cannot open %s", buf);
-                       NoAlias = TRUE;
-                       return;
-# endif
+                       s = stab("aliases.files", ST_MAP, ST_ENTER);
+                       map = &s->s_map;
+                       first_unqual = FALSE;
                }
                }
-# else
-               AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
-# endif
-       }
-       atcnt = SafeAlias * 2;
-       if (atcnt > 0)
-       {
-               while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
+               else
                {
                {
-                       /*
-                       **  Reinitialize alias file in case the new
-                       **  one is mv'ed in instead of cp'ed in.
-                       **
-                       **      Only works with new DBM -- old one will
-                       **      just consume file descriptors forever.
-                       **      If you have a dbmclose() it can be
-                       **      added before the sleep(30).
-                       */
+                       char aname[50];
 
 
-# ifdef NEWDB
-                       if (AliasDBptr != NULL)
-                               AliasDBptr->close(AliasDBptr);
-# endif
-# ifdef NDBM
-                       if (AliasDBMptr != NULL)
-                               dbm_close(AliasDBMptr);
-# endif
-
-                       sleep(30);
-# ifdef NEWDB
-                       (void) strcpy(buf, aliasfile);
-                       (void) strcat(buf, ".db");
-                       AliasDBptr =
-                           dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
-                       if (AliasDBptr == NULL)
+                       if (NAliasDBs >= MAXALIASDB)
                        {
                        {
-# ifdef NDBM
-                               AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
-# else
-                               syserr("initaliases: cannot open %s", buf);
-                               NoAlias = TRUE;
+                               syserr("Too many alias databases defined, %d max",
+                                       MAXALIASDB);
                                return;
                                return;
-# endif
                        }
                        }
-# else
-# ifdef NDBM
-                       AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
-# endif
-# endif
+                       (void) sprintf(aname, "Alias%d", NAliasDBs);
+                       s = stab(aname, ST_MAP, ST_ENTER);
+                       map = &s->s_map;
+                       AliasDB[NAliasDBs] = map;
                }
                }
-       }
-       else
-               atcnt = 1;
+               bzero(map, sizeof *map);
+               map->map_mname = s->s_name;
 
 
-       /*
-       **  See if the NDBM version of the file is out of date with
-       **  the text version.  If so, go into 'init' mode automatically.
-       **      This only happens if our effective userid owns the DBM.
-       **      Note the unpalatable hack to see if the stat succeeded.
-       */
+               p = strpbrk(p, " ,/:");
+               if (p != NULL && *p == ':')
+               {
+                       /* map name */
+                       *p++ = '\0';
+                       class = spec;
+                       spec = p;
+               }
+               else
+               {
+                       class = "implicit";
+                       map->map_mflags = MF_OPTIONAL|MF_INCLNULL;
+               }
 
 
-       modtime = stb.st_mtime;
-       (void) strcpy(buf, aliasfile);
-# ifdef NEWDB
-       (void) strcat(buf, ".db");
-# else
-       (void) strcat(buf, ".pag");
-# endif
-       stb.st_ino = 0;
-       if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
-       {
-               errno = 0;
-               if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
+               /* find end of spec */
+               if (p != NULL)
+                       p = strchr(p, ',');
+               if (p != NULL)
+                       *p++ = '\0';
+
+               if (tTd(27, 20))
+                       printf("  map %s:%s %s\n", class, s->s_name, spec);
+
+               /* look up class */
+               s = stab(class, ST_MAPCLASS, ST_FIND);
+               if (s == NULL)
                {
                {
-                       init = TRUE;
-                       automatic = TRUE;
-                       message(Arpa_Info, "rebuilding alias database");
-#ifdef LOG
-                       if (LogLevel >= 7)
-                               syslog(LOG_INFO, "rebuilding alias database");
-#endif /* LOG */
+                       if (tTd(27, 1))
+                               printf("Unknown alias class %s\n", class);
+               }
+               else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
+               {
+                       syserr("setalias: map class %s can't handle aliases",
+                               class);
                }
                else
                {
                }
                else
                {
-#ifdef LOG
-                       if (LogLevel >= 7)
-                               syslog(LOG_INFO, "alias database out of date");
-#endif /* LOG */
-                       message(Arpa_Info, "Warning: alias database out of date");
+                       map->map_class = &s->s_mapclass;
+                       if (map->map_class->map_parse(map, spec))
+                       {
+                               map->map_mflags |= MF_VALID|MF_ALIAS;
+                               if (AliasDB[NAliasDBs] == map)
+                                       NAliasDBs++;
+                       }
                }
        }
                }
        }
+}
+\f/*
+**  ALIASWAIT -- wait for distinguished @:@ token to appear.
+**
+**     This can decide to reopen or rebuild the alias file
+**
+**     Parameters:
+**             map -- a pointer to the map descriptor for this alias file.
+**             ext -- the filename extension (e.g., ".db") for the
+**                     database file.
+**             isopen -- if set, the database is already open, and we
+**                     should check for validity; otherwise, we are
+**                     just checking to see if it should be created.
+**
+**     Returns:
+**             TRUE -- if the database is open when we return.
+**             FALSE -- if the database is closed when we return.
+*/
 
 
+bool
+aliaswait(map, ext, isopen)
+       MAP *map;
+       char *ext;
+       int isopen;
+{
+       bool attimeout = FALSE;
+       time_t mtime;
+       struct stat stb;
+       char buf[MAXNAME + 1];
 
 
-       /*
-       **  If necessary, load the NDBM file.
-       **      If running without NDBM, load the symbol table.
-       */
+       if (tTd(27, 3))
+               printf("aliaswait(%s:%s)\n",
+                       map->map_class->map_cname, map->map_file);
+       if (bitset(MF_ALIASWAIT, map->map_mflags))
+               return isopen;
+       map->map_mflags |= MF_ALIASWAIT;
 
 
-       if (init)
+       if (SafeAlias > 0)
        {
        {
-#ifdef LOG
-               if (LogLevel >= 6)
+               auto int st;
+               time_t toolong = curtime() + SafeAlias;
+               unsigned int sleeptime = 2;
+
+               while (isopen &&
+                      map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
                {
                {
-                       extern char *username();
+                       if (curtime() > toolong)
+                       {
+                               /* we timed out */
+                               attimeout = TRUE;
+                               break;
+                       }
+
+                       /*
+                       **  Close and re-open the alias database in case
+                       **  the one is mv'ed instead of cp'ed in.
+                       */
+
+                       if (tTd(27, 2))
+                               printf("aliaswait: sleeping for %d seconds\n",
+                                       sleeptime);
 
 
-                       syslog(LOG_NOTICE, "alias database %srebuilt by %s",
-                               automatic ? "auto" : "", username());
+                       map->map_class->map_close(map);
+                       sleep(sleeptime);
+                       sleeptime *= 2;
+                       if (sleeptime > 60)
+                               sleeptime = 60;
+                       isopen = map->map_class->map_open(map, O_RDONLY);
+               }
+       }
+
+       /* see if we need to go into auto-rebuild mode */
+       if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
+       {
+               if (tTd(27, 3))
+                       printf("aliaswait: not rebuildable\n");
+               map->map_mflags &= ~MF_ALIASWAIT;
+               return isopen;
+       }
+       if (stat(map->map_file, &stb) < 0)
+       {
+               if (tTd(27, 3))
+                       printf("aliaswait: no source file\n");
+               map->map_mflags &= ~MF_ALIASWAIT;
+               return isopen;
+       }
+       mtime = stb.st_mtime;
+       (void) strcpy(buf, map->map_file);
+       if (ext != NULL)
+               (void) strcat(buf, ext);
+       if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
+       {
+               /* database is out of date */
+               if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
+               {
+                       bool oldSuprErrs;
+
+                       message("auto-rebuilding alias database %s", buf);
+                       oldSuprErrs = SuprErrs;
+                       SuprErrs = TRUE;
+                       if (isopen)
+                               map->map_class->map_close(map);
+                       rebuildaliases(map, TRUE);
+                       isopen = map->map_class->map_open(map, O_RDONLY);
+                       SuprErrs = oldSuprErrs;
                }
                }
+               else
+               {
+#ifdef LOG
+                       if (LogLevel > 3)
+                               syslog(LOG_INFO, "alias database %s out of date",
+                                       buf);
 #endif /* LOG */
 #endif /* LOG */
-               readaliases(aliasfile, TRUE, e);
+                       message("Warning: alias database %s out of date", buf);
+               }
        }
        }
-# else /* NDBM */
-       readaliases(aliasfile, init, e);
-# endif /* NDBM */
+       map->map_mflags &= ~MF_ALIASWAIT;
+       return isopen;
 }
 \f/*
 }
 \f/*
-**  READALIASES -- read and process the alias file.
-**
-**     This routine implements the part of initaliases that occurs
-**     when we are not going to use the DBM stuff.
+**  REBUILDALIASES -- rebuild the alias database.
 **
 **     Parameters:
 **
 **     Parameters:
-**             aliasfile -- the pathname of the alias file master.
-**             init -- if set, initialize the NDBM stuff.
+**             map -- the database to rebuild.
+**             automatic -- set if this was automatically generated.
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
-**             Reads aliasfile into the symbol table.
-**             Optionally, builds the .dir & .pag files.
+**             Reads the text version of the database, builds the
+**             DBM or DB version.
 */
 
 */
 
-static
-readaliases(aliasfile, init, e)
-       char *aliasfile;
-       bool init;
-       register ENVELOPE *e;
+void
+rebuildaliases(map, automatic)
+       register MAP *map;
+       bool automatic;
 {
 {
-       register char *p;
-       char *lhs;
-       char *rhs;
-       bool skipping;
-       int naliases, bytes, longest;
        FILE *af;
        FILE *af;
-       bool makedbmfiles;
-       void (*oldsigint)();
-       ADDRESS al, bl;
-       register STAB *s;
-# ifdef NEWDB
-       DB *dbp;
-# endif
-# ifdef NDBM
-       DBM *dbmp;
-# endif
-# ifdef LOCKF
-       struct flock fld;
-# endif
-       char line[BUFSIZ];
+       bool nolock = FALSE;
+       sigfunc_t oldsigint, oldsigquit;
+#ifdef SIGTSTP
+       sigfunc_t oldsigtstp;
+#endif
 
 
-       if ((af = fopen(aliasfile, "r+")) == NULL)
-       {
-               if (tTd(27, 1))
-                       printf("Can't open %s\n", aliasfile);
-               errno = 0;
-               NoAlias++;
+       if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
                return;
                return;
+
+       /* try to lock the source file */
+       if ((af = fopen(map->map_file, "r+")) == NULL)
+       {
+               if ((errno != EACCES && errno != EROFS) || automatic ||
+                   (af = fopen(map->map_file, "r")) == NULL)
+               {
+                       int saveerr = errno;
+
+                       if (tTd(27, 1))
+                               printf("Can't open %s: %s\n",
+                                       map->map_file, errstring(saveerr));
+                       if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
+                               message("newaliases: cannot open %s: %s",
+                                       map->map_file, errstring(saveerr));
+                       errno = 0;
+                       return;
+               }
+               nolock = TRUE;
+               message("warning: cannot lock %s: %s",
+                       map->map_file, errstring(errno));
        }
 
        }
 
-# if defined(NDBM) || defined(NEWDB)
-       /* see if someone else is rebuilding the alias file already */
-# ifdef LOCKF
-       fld.l_type = F_WRLCK;
-       fld.l_whence = fld.l_start = fld.l_len = 0;
-       if (fcntl(fileno(af), F_SETLK, &fld) < 0)
-# else
-       if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
-# endif
+       /* see if someone else is rebuilding the alias file */
+       if (!nolock &&
+           !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB))
        {
        {
-               /* yes, they are -- wait until done and then return */
-               message(Arpa_Info, "Alias file is already being rebuilt");
+               /* yes, they are -- wait until done */
+               message("Alias file %s is already being rebuilt",
+                       map->map_file);
                if (OpMode != MD_INITALIAS)
                {
                        /* wait for other rebuild to complete */
                if (OpMode != MD_INITALIAS)
                {
                        /* wait for other rebuild to complete */
-# ifdef LOCKF
-                       (void) fcntl(fileno(af), F_SETLKW, &fld);
-# else
-                       (void) flock(fileno(af), LOCK_EX);
-# endif
+                       (void) lockfile(fileno(af), map->map_file, NULL,
+                                       LOCK_EX);
                }
                }
-               (void) fclose(af);
+               (void) xfclose(af, "rebuildaliases1", map->map_file);
                errno = 0;
                return;
        }
                errno = 0;
                return;
        }
-# endif /* NDBM */
 
 
-       /*
-       **  If initializing, create the new DBM files.
-       */
+       /* avoid denial-of-service attacks */
+       resetlimits();
+       oldsigint = setsignal(SIGINT, SIG_IGN);
+       oldsigquit = setsignal(SIGQUIT, SIG_IGN);
+#ifdef SIGTSTP
+       oldsigtstp = setsignal(SIGTSTP, SIG_IGN);
+#endif
 
 
-       if (init)
+       if (map->map_class->map_open(map, O_RDWR))
        {
        {
-               oldsigint = signal(SIGINT, SIG_IGN);
-# ifdef NEWDB
-               (void) strcpy(line, aliasfile);
-               (void) strcat(line, ".db");
-               dbp = dbopen(line,
-                   O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL);
-               if (dbp == NULL)
-               {
-                       syserr("readaliases: cannot create %s", line);
-                       (void) signal(SIGINT, oldsigint);
-                       return;
-               }
-# endif
-# ifdef IF_MAKEDBMFILES
-# ifdef NEWDB
-               makedbmfiles = access("/var/yp/Makefile", R_OK) == 0;
-# endif
-               IF_MAKEDBMFILES
+#ifdef LOG
+               if (LogLevel > 7)
                {
                {
-                       dbmp = dbm_open(aliasfile,
-                                              O_RDWR|O_CREAT|O_TRUNC, DBMMODE);
-                       if (dbmp == NULL)
-                       {
-                               syserr("readaliases: cannot create %s.{dir,pag}",
-                                       aliasfile);
-                               (void) signal(SIGINT, oldsigint);
-                               return;
-                       }
+                       syslog(LOG_NOTICE, "alias database %s %srebuilt by %s",
+                               map->map_file, automatic ? "auto" : "",
+                               username());
                }
                }
-# endif
+#endif /* LOG */
+               map->map_mflags |= MF_OPEN|MF_WRITABLE;
+               readaliases(map, af, !automatic, TRUE);
+       }
+       else
+       {
+               if (tTd(27, 1))
+                       printf("Can't create database for %s: %s\n",
+                               map->map_file, errstring(errno));
+               if (!automatic)
+                       syserr("Cannot create database for alias file %s",
+                               map->map_file);
        }
 
        }
 
+       /* close the file, thus releasing locks */
+       xfclose(af, "rebuildaliases2", map->map_file);
+
+       /* add distinguished entries and close the database */
+       if (bitset(MF_OPEN, map->map_mflags))
+               map->map_class->map_close(map);
+
+       /* restore the old signals */
+       (void) setsignal(SIGINT, oldsigint);
+       (void) setsignal(SIGQUIT, oldsigquit);
+#ifdef SIGTSTP
+       (void) setsignal(SIGTSTP, oldsigtstp);
+#endif
+}
+\f/*
+**  READALIASES -- read and process the alias file.
+**
+**     This routine implements the part of initaliases that occurs
+**     when we are not going to use the DBM stuff.
+**
+**     Parameters:
+**             map -- the alias database descriptor.
+**             af -- file to read the aliases from.
+**             announcestats -- anounce statistics regarding number of
+**                     aliases, longest alias, etc.
+**             logstats -- lot the same info.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Reads aliasfile into the symbol table.
+**             Optionally, builds the .dir & .pag files.
+*/
+
+void
+readaliases(map, af, announcestats, logstats)
+       register MAP *map;
+       FILE *af;
+       bool announcestats;
+       bool logstats;
+{
+       register char *p;
+       char *lhs;
+       char *rhs;
+       bool skipping;
+       long naliases, bytes, longest;
+       ADDRESS al, bl;
+       char line[BUFSIZ];
+
        /*
        **  Read and interpret lines
        */
 
        /*
        **  Read and interpret lines
        */
 
-       FileName = aliasfile;
+       FileName = map->map_file;
        LineNumber = 0;
        naliases = bytes = longest = 0;
        skipping = FALSE;
        LineNumber = 0;
        naliases = bytes = longest = 0;
        skipping = FALSE;
@@ -532,7 +577,7 @@ readaliases(aliasfile, init, e)
                  case ' ':
                  case '\t':
                        if (!skipping)
                  case ' ':
                  case '\t':
                        if (!skipping)
-                               syserr("Non-continuation line starts with space");
+                               syserr("554 Non-continuation line starts with space");
                        skipping = TRUE;
                        continue;
                }
                        skipping = TRUE;
                        continue;
                }
@@ -541,7 +586,7 @@ readaliases(aliasfile, init, e)
                /*
                **  Process the LHS
                **
                /*
                **  Process the LHS
                **
-               **      Find the final colon, and parse the address.
+               **      Find the colon separator, and parse the address.
                **      It should resolve to a local name.
                **
                **      Alternatively, it can be "@hostname" for host
                **      It should resolve to a local name.
                **
                **      Alternatively, it can be "@hostname" for host
@@ -591,6 +636,8 @@ readaliases(aliasfile, init, e)
                **              list maintainer.
                */
 
                **              list maintainer.
                */
 
+               while (isascii(*p) && isspace(*p))
+                       p++;
                if (*p == ':')
                {
                        ADDRESS *maint;
                if (*p == ':')
                {
                        ADDRESS *maint;
@@ -617,28 +664,33 @@ readaliases(aliasfile, init, e)
                for (;;)
                {
                        register char c;
                for (;;)
                {
                        register char c;
+                       register char *nlp;
 
 
-                       if (init && CheckAliases)
+                       nlp = &p[strlen(p)];
+                       if (nlp[-1] == '\n')
+                               *--nlp = '\0';
+
+                       if (CheckAliases)
                        {
                                /* do parsing & compression of addresses */
                                while (*p != '\0')
                                {
                        {
                                /* do parsing & compression of addresses */
                                while (*p != '\0')
                                {
-                                       extern char *DelimChar;
+                                       auto char *delimptr;
 
 
-                                       while (isspace(*p) || *p == ',')
+                                       while ((isascii(*p) && isspace(*p)) ||
+                                                               *p == ',')
                                                p++;
                                        if (*p == '\0')
                                                break;
                                                p++;
                                        if (*p == '\0')
                                                break;
-                                       if (parseaddr(p, &bl, -1, ',', e) == NULL)
-                                               usrerr("%s... bad address", p);
-                                       p = DelimChar;
+                                       if (parseaddr(p, &bl, RF_COPYNONE, ',',
+                                                     &delimptr, CurEnv) == NULL)
+                                               usrerr("553 %s... bad address", p);
+                                       p = delimptr;
                                }
                        }
                        else
                        {
                                }
                        }
                        else
                        {
-                               p = &p[strlen(p)];
-                               if (p[-1] == '\n')
-                                       *--p = '\0';
+                               p = nlp;
                        }
 
                        /* see if there should be a continuation line */
                        }
 
                        /* see if there should be a continuation line */
@@ -656,7 +708,7 @@ readaliases(aliasfile, init, e)
                        /* check for line overflow */
                        if (strchr(p, '\n') == NULL)
                        {
                        /* check for line overflow */
                        if (strchr(p, '\n') == NULL)
                        {
-                               usrerr("alias too long");
+                               usrerr("554 alias too long");
                                break;
                        }
                }
                                break;
                        }
                }
@@ -666,34 +718,17 @@ readaliases(aliasfile, init, e)
                */
 
                lhssize = strlen(lhs) + 1;
                */
 
                lhssize = strlen(lhs) + 1;
-               rhssize = strlen(rhs) + 1;
 
 
-# if defined(NDBM) || defined(NEWDB)
-               if (init)
-               {
-                       DBdatum key, content;
-
-                       key.xx.size = lhssize;
-                       key.xx.data = al.q_user;
-                       content.xx.size = rhssize;
-                       content.xx.data = rhs;
-# ifdef NEWDB
-                       if (dbp->put(dbp, &key.dbt, &content.dbt, 0) != 0)
-                               syserr("readaliases: db put (%s)", al.q_user);
-# endif
-# ifdef IF_MAKEDBMFILES
-                       IF_MAKEDBMFILES
-                               if (dbm_store(dbmp, key.dbm, content.dbm, DBM_REPLACE) != 0)
-                                       syserr("readaliases: dbm store (%s)",
-                                               al.q_user);
-# endif
-               }
-               else
-# endif /* NDBM */
-               {
-                       s = stab(al.q_user, ST_ALIAS, ST_ENTER);
-                       s->s_alias = newstr(rhs);
-               }
+               lhssize = strlen(al.q_user);
+               rhssize = strlen(rhs);
+               map->map_class->map_store(map, al.q_user, rhs);
+
+               if (al.q_paddr != NULL)
+                       free(al.q_paddr);
+               if (al.q_host != NULL)
+                       free(al.q_host);
+               if (al.q_user != NULL)
+                       free(al.q_user);
 
                /* statistics */
                naliases++;
 
                /* statistics */
                naliases++;
@@ -702,45 +737,15 @@ readaliases(aliasfile, init, e)
                        longest = rhssize;
        }
 
                        longest = rhssize;
        }
 
-# if defined(NDBM) || defined(NEWDB)
-       if (init)
-       {
-               /* add the distinquished alias "@" */
-               DBdatum key;
-
-               key.xx.size = 2;
-               key.xx.data = "@";
-# ifdef NEWDB
-               if (dbp->sync(dbp) != 0 ||
-                   dbp->put(dbp, &key.dbt, &key.dbt, 0) != 0 ||
-                   dbp->close(dbp) != 0)
-                       syserr("readaliases: db close failure");
-# endif
-# ifdef IF_MAKEDBMFILES
-               IF_MAKEDBMFILES
-               {
-                       if (dbm_store(dbmp, key.dbm, key.dbm, DBM_REPLACE) != 0 ||
-                           dbm_error(dbmp))
-                               syserr("readaliases: dbm close failure");
-                       dbm_close(dbmp);
-               }
-# endif
-
-               /* restore the old signal */
-               (void) signal(SIGINT, oldsigint);
-       }
-# endif /* NDBM */
-
-       /* closing the alias file drops the lock */
-       (void) fclose(af);
-       e->e_to = NULL;
+       CurEnv->e_to = NULL;
        FileName = NULL;
        FileName = NULL;
-       message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
-                       naliases, longest, bytes);
+       if (Verbose || announcestats)
+               message("%s: %d aliases, longest %d bytes, %d bytes total",
+                       map->map_file, naliases, longest, bytes);
 # ifdef LOG
 # ifdef LOG
-       if (LogLevel >= 8)
-               syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
-                       naliases, longest, bytes);
+       if (LogLevel > 7 && logstats)
+               syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total",
+                       map->map_file, naliases, longest, bytes);
 # endif /* LOG */
 }
 \f/*
 # endif /* LOG */
 }
 \f/*
@@ -755,6 +760,8 @@ readaliases(aliasfile, init, e)
 **                     in.
 **             sendq -- a pointer to the head of the send queue to
 **                     put this user's aliases in.
 **                     in.
 **             sendq -- a pointer to the head of the send queue to
 **                     put this user's aliases in.
+**             aliaslevel -- the current alias nesting depth.
+**             e -- the current envelope.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -763,26 +770,69 @@ readaliases(aliasfile, init, e)
 **             New names are added to send queues.
 */
 
 **             New names are added to send queues.
 */
 
-forward(user, sendq, e)
+void
+forward(user, sendq, aliaslevel, e)
        ADDRESS *user;
        ADDRESS **sendq;
        ADDRESS *user;
        ADDRESS **sendq;
+       int aliaslevel;
        register ENVELOPE *e;
 {
        register ENVELOPE *e;
 {
-       char buf[60];
-       extern bool safefile();
+       char *pp;
+       char *ep;
 
        if (tTd(27, 1))
                printf("forward(%s)\n", user->q_paddr);
 
 
        if (tTd(27, 1))
                printf("forward(%s)\n", user->q_paddr);
 
-       if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
+       if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
+           bitset(QBADADDR, user->q_flags))
                return;
        if (user->q_home == NULL)
                return;
        if (user->q_home == NULL)
-               syserr("forward: no home");
+       {
+               syserr("554 forward: no home");
+               user->q_home = "/nosuchdirectory";
+       }
 
        /* good address -- look for .forward file in home */
        define('z', user->q_home, e);
 
        /* good address -- look for .forward file in home */
        define('z', user->q_home, e);
-       expand("\001z/.forward", buf, &buf[sizeof buf - 1], e);
-       include(buf, TRUE, user, sendq, e);
+       define('u', user->q_user, e);
+       define('h', user->q_host, e);
+       if (ForwardPath == NULL)
+               ForwardPath = newstr("\201z/.forward");
+
+       for (pp = ForwardPath; pp != NULL; pp = ep)
+       {
+               int err;
+               char buf[MAXPATHLEN+1];
+               extern int include();
+
+               ep = strchr(pp, ':');
+               if (ep != NULL)
+                       *ep = '\0';
+               expand(pp, buf, sizeof buf, e);
+               if (ep != NULL)
+                       *ep++ = ':';
+               if (tTd(27, 3))
+                       printf("forward: trying %s\n", buf);
+
+               err = include(buf, TRUE, user, sendq, aliaslevel, e);
+               if (err == 0)
+                       break;
+               else if (transienterror(err))
+               {
+                       /* we have to suspend this message */
+                       if (tTd(27, 2))
+                               printf("forward: transient error on %s\n", buf);
+#ifdef LOG
+                       if (LogLevel > 2)
+                               syslog(LOG_ERR, "%s: forward %s: transient error: %s",
+                                       e->e_id == NULL ? "NOQUEUE" : e->e_id,
+                                       buf, errstring(err));
+#endif
+                       message("%s: %s: message queued", buf, errstring(err));
+                       user->q_flags |= QQUEUEUP;
+                       return;
+               }
+       }
 }
 \f/*
 **  MAPHOST -- given a host description, produce a mapping.
 }
 \f/*
 **  MAPHOST -- given a host description, produce a mapping.