* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)alias.c 5.22 (Berkeley) 3/2/91 (with DBM)";
static char sccsid
[] = "@(#)alias.c 5.22 (Berkeley) 3/2/91 (with DB)";
static char sccsid
[] = "@(#)alias.c 5.22 (Berkeley) 3/2/91 (without DBM)";
** ALIAS -- Compute aliases.
** Scans the alias file for an alias for the given address.
** If found, it arranges to deliver to the alias list instead.
** Uses libdbm database if -DDBM.
** a -- address to alias.
** sendq -- a pointer to the head of the send queue
** to put the aliases in.
** Aliases found are expanded.
** If NoAlias (the "-n" flag) is set, no aliasing is
** It should complain about names that are aliased to
** DB is a database structure containing pointers to access methods.
extern char *aliaslookup();
printf("alias(%s)\n", a
->q_paddr
);
/* don't realias already aliased names */
if (bitset(QDONTSEND
, a
->q_flags
))
CurEnv
->e_to
= a
->q_paddr
;
p
= aliaslookup(a
->q_user
);
** Deliver to the target list.
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
);
** ALIASLOOKUP -- look up a name in the alias file.
** name -- the name to look up.
** The return value will be trashed across calls.
/* create a key for fetch */
lhs
.dsize
= strlen(name
) + 1;
/* create a key for fetch */
lhs
.size
= strlen(name
) + 1;
aliasdb
->get(aliasdb
, &lhs
, &rhs
, 0);
s
= stab(name
, ST_ALIAS
, ST_FIND
);
** INITALIASES -- initialize for aliasing
** Very different depending on whether we are running DBM or not.
** aliasfile -- location of aliases.
** init -- if set and if DBM, initialize the DBM files.
** if DBM: opens the database.
** if ~DBM: reads the aliases into the symbol table.
# define DBEXTENSION ".db" /* extension for the database filename */
initaliases(aliasfile
, init
)
#if defined (DBM) || defined (USE_DB)
static bool initialized
= FALSE
;
static int readaliases();
if (aliasfile
== NULL
|| stat(aliasfile
, &stb
) < 0)
if (aliasfile
!= NULL
&& init
)
syserr("Cannot open %s", aliasfile
);
#if defined (DBM) || defined (USE_DB)
** Check to see that the alias file is complete.
** If not, we will assume that someone died, and it is up
(void) strcpy(buf
, aliasfile
);
(void) strcat(buf
, DBEXTENSION
);
if (aliasdb
) aliasdb
->close (aliasdb
);
aliasdb
= btree_open (buf
, O_RDWR
, DBMMODE
, 0);
syserr("Cannot open database %s", buf
);
while (!init
&& atcnt
-- >= 0 && aliaslookup("@") == NULL
)
** 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).
aliasdb
->close (aliasdb
);
aliasdb
= btree_open (buf
, O_RDWR
, DBMMODE
, 0);
syserr("Cannot open database %s", buf
);
** See if the DBM 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.
(void) strcpy(buf
, aliasfile
);
(void) strcat(buf
, ".pag");
(void) strcat(buf
, DBEXTENSION
);
if (!init
&& (stat(buf
, &stb
) < 0 || stb
.st_mtime
< modtime
|| atcnt
< 0))
if (AutoRebuild
&& stb
.st_ino
!= 0 && stb
.st_uid
== geteuid())
message(Arpa_Info
, "rebuilding alias database");
syslog(LOG_INFO
, "rebuilding alias database");
syslog(LOG_INFO
, "alias database out of date");
message(Arpa_Info
, "Warning: alias database out of date");
** If necessary, load the DBM file.
** If running without DBM, load the symbol table.
syslog(LOG_NOTICE
, "alias database %srebuilt by %s",
automatic
? "auto" : "", username());
readaliases(aliasfile
, TRUE
);
#else /* defined (DBM) || defined (USE_DB) */
readaliases(aliasfile
, init
);
#endif /* defined (DBM) || defined (USE_DB) */
** 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.
** aliasfile -- the pathname of the alias file master.
** init -- if set, initialize the DBM stuff.
** Reads aliasfile into the symbol table.
** Optionally, builds the .dir & .pag files.
readaliases(aliasfile
, init
)
int naliases
, bytes
, longest
;
if ((af
= fopen(aliasfile
, "r")) == NULL
)
printf("Can't open %s\n", aliasfile
);
#if defined (DBM) || defined (USE_DB)
/* see if someone else is rebuilding the alias file already */
if (flock(fileno(af
), LOCK_EX
| LOCK_NB
) < 0 && errno
== EWOULDBLOCK
)
/* yes, they are -- wait until done and then return */
message(Arpa_Info
, "Alias file is already being rebuilt");
if (OpMode
!= MD_INITALIAS
)
/* wait for other rebuild to complete */
(void) flock(fileno(af
), LOCK_EX
);
** If initializing, create the new DBM files.
oldsigint
= signal(SIGINT
, SIG_IGN
);
if (aliasdb
) aliasdb
->close (aliasdb
);
(void) strcpy(line
, aliasfile
);
(void) strcat(line
, ".dir");
if (close(creat(line
, DBMMODE
)) < 0)
syserr("cannot make %s", line
);
(void) signal(SIGINT
, oldsigint
);
(void) strcpy(line
, aliasfile
);
(void) strcat(line
, ".pag");
if (close(creat(line
, DBMMODE
)) < 0)
syserr("cannot make %s", line
);
(void) signal(SIGINT
, oldsigint
);
(void) strcat(line
, DBEXTENSION
);
/* unconditionally remove the database file so that a
corrupt file cannot cause the following open to fail */
aliasdb
= btree_open (line
, O_RDWR
|O_CREAT
, DBMMODE
, 0);
syserr("Cannot open database file %s", line
);
(void) signal(SIGINT
, oldsigint
);
#endif /* defined (DBM) || defined (USE_DB) */
** Read and interpret lines
naliases
= bytes
= longest
= 0;
while (fgets(line
, sizeof (line
), af
) != NULL
)
syserr("Non-continuation line starts with space");
** Find the final colon, and parse the address.
** It should resolve to a local name -- this will
** be checked later (we want to optionally do
** parsing of the RHS first to maximize error
for (p
= line
; *p
!= '\0' && *p
!= ':' && *p
!= '\n'; p
++)
if (parseaddr(line
, &al
, 1, ':') == NULL
)
syserr("illegal alias name");
** 'al' is the internal form of the LHS address.
** 'p' points to the text of the RHS.
if (init
&& CheckAliases
)
/* do parsing & compression of addresses */
while (isspace(*p
) || *p
== ',')
if (parseaddr(p
, &bl
, -1, ',') == NULL
)
usrerr("%s... bad address", p
);
/* see if there should be a continuation line */
if (c
!= ' ' && c
!= '\t')
/* read continuation line */
if (fgets(p
, sizeof line
- (p
- line
), af
) == NULL
)
if (al
.q_mailer
!= LocalMailer
)
syserr("cannot alias non-local names");
** Insert alias into symbol table or DBM file
lhssize
= strlen(al
.q_user
) + 1;
rhssize
= strlen(rhs
) + 1;
aliasdb
->put (aliasdb
, &key
, &content
, R_PUT
);
s
= stab(al
.q_user
, ST_ALIAS
, ST_ENTER
);
s
->s_alias
= newstr(rhs
);
bytes
+= lhssize
+ rhssize
;
/* add the distinquished alias "@" */
/* restore the old signal */
(void) signal(SIGINT
, oldsigint
);
/* add the distinquished alias "@" */
aliasdb
->put (aliasdb
, &key
, &key
, R_PUT
);
/* restore the old signal */
(void) signal(SIGINT
, oldsigint
);
/* closing the alias file drops the lock */
message(Arpa_Info
, "%d aliases, longest %d bytes, %d bytes total",
naliases
, longest
, bytes
);
syslog(LOG_INFO
, "%d aliases, longest %d bytes, %d bytes total",
naliases
, longest
, bytes
);
** FORWARD -- Try to forward mail
** This is similar but not identical to aliasing.
** user -- the name of the user who's mail we would like
** to forward to. It must have been verified --
** i.e., the q_home field must have been filled
** sendq -- a pointer to the head of the send queue to
** put this user's aliases in.
** New names are added to send queues.
printf("forward(%s)\n", user
->q_paddr
);
if (user
->q_mailer
!= LocalMailer
|| bitset(QBADADDR
, user
->q_flags
))
if (user
->q_home
== NULL
)
syserr("forward: no home");
/* good address -- look for .forward file in home */
define('z', user
->q_home
, CurEnv
);
expand("\001z/.forward", buf
, &buf
[sizeof buf
- 1], CurEnv
);
if (!safefile(buf
, user
->q_uid
, S_IREAD
))
/* we do have an address to forward to -- do it */
include(buf
, "forwarding", user
, sendq
);