* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)alias.c 8.40 (Berkeley) %G%";
MAP
*AliasDB
[MAXALIASDB
+ 1]; /* actual database list */
int NAliasDBs
; /* number of alias databases */
** 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.
** aliaslevel -- the current alias nesting depth.
** e -- the current envelope.
** Aliases found are expanded.
** It should complain about names that are aliased to
alias(a
, sendq
, aliaslevel
, e
)
extern ADDRESS
*sendto();
extern char *aliaslookup();
printf("alias(%s)\n", a
->q_user
);
/* don't realias already aliased names */
if (bitset(QDONTSEND
|QBADADDR
|QVERIFIED
, a
->q_flags
))
p
= aliaslookup(a
->q_user
, e
);
** Deliver to the target list.
printf("%s (%s, %s) aliased to %s\n",
a
->q_paddr
, a
->q_host
, a
->q_user
, p
);
if (bitset(EF_VRFYONLY
, e
->e_flags
))
message("aliased to %s", p
);
syslog(LOG_INFO
, "%s: alias %s => %s",
e
->e_id
== NULL
? "NOQUEUE" : e
->e_id
,
printf("alias: 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");
(void) strcat(obuf
, a
->q_user
);
if (!bitnset(M_USR_UPPER
, a
->q_mailer
->m_flags
))
owner
= aliaslookup(obuf
, e
);
/* reflect owner into envelope sender */
if (strpbrk(owner
, ",:/|\"") != NULL
)
a
->q_owner
= newstr(owner
);
/* announce delivery to this alias; NORECEIPT bit set later */
fprintf(e
->e_xfp
, "Message delivered to mailing list %s\n",
e
->e_flags
|= EF_SENDRECEIPT
;
a
->q_flags
|= QREPORT
|QEXPLODED
;
** ALIASLOOKUP -- look up a name in the alias file.
** name -- the name to look up.
** The return value will be trashed across calls.
for (dbno
= 0; dbno
< NAliasDBs
; dbno
++)
if (!bitset(MF_OPEN
, map
->map_mflags
))
p
= (*map
->map_class
->map_lookup
)(map
, name
, NULL
, &stat
);
** SETALIAS -- set up an alias map
** Called when reading configuration file.
** spec -- the alias specification
static bool first_unqual
= TRUE
;
printf("setalias(%s)\n", spec
);
for (p
= spec
; p
!= NULL
; )
** Treat simple filename specially -- this is the file name
** for the files implementation, not necessarily in order.
if (spec
[0] == '/' && first_unqual
)
s
= stab("aliases.files", ST_MAP
, ST_ENTER
);
if (NAliasDBs
>= MAXALIASDB
)
syserr("Too many alias databases defined, %d max",
(void) sprintf(aname
, "Alias%d", NAliasDBs
);
s
= stab(aname
, ST_MAP
, ST_ENTER
);
AliasDB
[NAliasDBs
] = map
;
map
->map_mname
= s
->s_name
;
if (p
!= NULL
&& *p
== ':')
map
->map_mflags
= MF_OPTIONAL
|MF_INCLNULL
;
printf(" map %s:%s %s\n", class, s
->s_name
, spec
);
s
= stab(class, ST_MAPCLASS
, ST_FIND
);
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",
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
)
** ALIASWAIT -- wait for distinguished @:@ token to appear.
** This can decide to reopen or rebuild the alias file
** map -- a pointer to the map descriptor for this alias file.
** ext -- the filename extension (e.g., ".db") for the
** 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.
** TRUE -- if the database is open when we return.
** FALSE -- if the database is closed when we return.
aliaswait(map
, ext
, isopen
)
printf("aliaswait(%s:%s)\n",
map
->map_class
->map_cname
, map
->map_file
);
if (bitset(MF_ALIASWAIT
, map
->map_mflags
))
map
->map_mflags
|= MF_ALIASWAIT
;
time_t toolong
= curtime() + SafeAlias
;
unsigned int sleeptime
= 2;
map
->map_class
->map_lookup(map
, "@", NULL
, &st
) == NULL
)
** Close and re-open the alias database in case
** the one is mv'ed instead of cp'ed in.
printf("aliaswait: sleeping for %d seconds\n",
map
->map_class
->map_close(map
);
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
))
printf("aliaswait: not rebuildable\n");
map
->map_mflags
&= ~MF_ALIASWAIT
;
if (stat(map
->map_file
, &stb
) < 0)
printf("aliaswait: no source file\n");
map
->map_mflags
&= ~MF_ALIASWAIT
;
(void) strcpy(buf
, map
->map_file
);
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())
message("auto-rebuilding alias database %s", buf
);
map
->map_class
->map_close(map
);
rebuildaliases(map
, TRUE
);
isopen
= map
->map_class
->map_open(map
, O_RDONLY
);
syslog(LOG_INFO
, "alias database %s out of date",
message("Warning: alias database %s out of date", buf
);
map
->map_mflags
&= ~MF_ALIASWAIT
;
** REBUILDALIASES -- rebuild the alias database.
** map -- the database to rebuild.
** automatic -- set if this was automatically generated.
** Reads the text version of the database, builds the
rebuildaliases(map
, automatic
)
sigfunc_t oldsigint
, oldsigquit
;
if (!bitset(MCF_REBUILDABLE
, map
->map_class
->map_cflags
))
/* 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
)
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
));
message("warning: cannot lock %s: %s",
map
->map_file
, errstring(errno
));
/* see if someone else is rebuilding the alias file */
!lockfile(fileno(af
), map
->map_file
, NULL
, LOCK_EX
|LOCK_NB
))
/* yes, they are -- wait until done */
message("Alias file %s is already being rebuilt",
if (OpMode
!= MD_INITALIAS
)
/* wait for other rebuild to complete */
(void) lockfile(fileno(af
), map
->map_file
, NULL
,
(void) xfclose(af
, "rebuildaliases1", map
->map_file
);
/* avoid denial-of-service attacks */
oldsigint
= setsignal(SIGINT
, SIG_IGN
);
oldsigquit
= setsignal(SIGQUIT
, SIG_IGN
);
oldsigtstp
= setsignal(SIGTSTP
, SIG_IGN
);
if (map
->map_class
->map_open(map
, O_RDWR
))
syslog(LOG_NOTICE
, "alias database %s %srebuilt by %s",
map
->map_file
, automatic
? "auto" : "",
map
->map_mflags
|= MF_OPEN
|MF_WRITABLE
;
readaliases(map
, af
, !automatic
, TRUE
);
printf("Can't create database for %s: %s\n",
map
->map_file
, errstring(errno
));
syserr("Cannot create database for alias file %s",
/* 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
);
(void) setsignal(SIGTSTP
, oldsigtstp
);
** 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.
** 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.
** Reads aliasfile into the symbol table.
** Optionally, builds the .dir & .pag files.
readaliases(map
, af
, announcestats
, logstats
)
long naliases
, bytes
, longest
;
** Read and interpret lines
FileName
= map
->map_file
;
naliases
= bytes
= longest
= 0;
while (fgets(line
, sizeof (line
), af
) != NULL
)
syserr("554 Non-continuation line starts with space");
** Find the colon separator, and parse the address.
** It should resolve to a local name.
** Alternatively, it can be "@hostname" for host
** aliases -- all we do here is map case. Hostname
** need not be a single token.
for (p
= line
; *p
!= '\0' && *p
!= ':' && *p
!= '\n'; p
++)
syserr("%s, line %d: syntax error", aliasfile
, lineno
);
if (parseaddr(line
, &al
, 1, ':') == NULL
)
syserr("illegal alias name");
if (al
.q_mailer
!= LocalMailer
)
syserr("cannot alias non-local names");
** 'al' is the internal form of the LHS address.
** 'p' points to the text of the RHS.
** 'p' may begin with a colon (i.e., the
** separator was "::") which will use the
** first address as the person to send
** errors to -- i.e., designates the
while (isascii(*p
) && isspace(*p
))
while (*p
!= '\0' && *p
!= ',')
maint
= parse(p
, (ADDRESS
*) NULL
, 1);
syserr("Illegal list maintainer for list %s", al
.q_user
);
else if (CurEnv
->e_returnto
== &CurEnv
->e_from
)
CurEnv
->e_returnto
= maint
;
/* do parsing & compression of addresses */
while ((isascii(*p
) && isspace(*p
)) ||
if (parseaddr(p
, &bl
, RF_COPYNONE
, ',',
&delimptr
, CurEnv
) == NULL
)
usrerr("553 %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
)
/* check for line overflow */
if (strchr(p
, '\n') == NULL
)
usrerr("554 alias too long");
** Insert alias into symbol table or DBM file
lhssize
= strlen(lhs
) + 1;
lhssize
= strlen(al
.q_user
);
map
->map_class
->map_store(map
, al
.q_user
, rhs
);
bytes
+= lhssize
+ rhssize
;
if (Verbose
|| announcestats
)
message("%s: %d aliases, longest %d bytes, %d bytes total",
map
->map_file
, 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
);
** 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.
** aliaslevel -- the current alias nesting depth.
** e -- the current envelope.
** New names are added to send queues.
forward(user
, sendq
, aliaslevel
, e
)
printf("forward(%s)\n", user
->q_paddr
);
if (!bitnset(M_HASPWENT
, user
->q_mailer
->m_flags
) ||
bitset(QBADADDR
, user
->q_flags
))
if (user
->q_home
== NULL
)
syserr("554 forward: no home");
user
->q_home
= "/nosuchdirectory";
/* good address -- look for .forward file in home */
define('z', user
->q_home
, e
);
define('u', user
->q_user
, e
);
define('h', user
->q_host
, e
);
ForwardPath
= newstr("\201z/.forward");
for (pp
= ForwardPath
; pp
!= NULL
; pp
= ep
)
expand(pp
, buf
, sizeof buf
, e
);
printf("forward: trying %s\n", buf
);
err
= include(buf
, TRUE
, user
, sendq
, aliaslevel
, e
);
else if (transienterror(err
))
/* we have to suspend this message */
printf("forward: transient error on %s\n", buf
);
syslog(LOG_ERR
, "%s: forward %s: transient error: %s",
e
->e_id
== NULL
? "NOQUEUE" : e
->e_id
,
message("%s: %s: message queued", buf
, errstring(err
));
user
->q_flags
|= QQUEUEUP
;
** MAPHOST -- given a host description, produce a mapping.
** This is done by looking up the name in the alias file,
** preceeded by an "@". This can be used for UUCP mapping.
** For example, a call with {blia, ., UUCP} as arguments
** might return {ucsfcgl, !, blia, ., UUCP} as the result.
** We first break the input into three parts -- before the
** lookup, the lookup itself, and after the lookup. We
** then do the lookup, concatenate them together, and rescan
** pvp -- the parameter vector to map.
** The result of the mapping. If nothing found, it
** should just concatenate the three parts together and
** Extract the three parts of the input as strings.
/* find the part before the lookup */
for (bvp
= pvp
; *bvp
!= NULL
&& **bvp
!= MATCHLOOKUP
; bvp
++)
cataddr(pvp
, buf1
, sizeof buf1
);
/* find the rest of the lookup */
for (avp
= bvp
; *pvp
!= NULL
&& **bvp
!= MATCHELOOKUP
; bvp
++)
cataddr(avp
, buf2
, sizeof buf2
);
/* save the part after the lookup */
cataddr(bvp
, buf3
, sizeof buf3
);
** Now look up the middle part.
** Put the three parts back together and break into tokens.
avp
= prescan(buf1
, '\0');
/* return this mapping */