Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

947 lines
25 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
static char sccsid[] = "@(#)86 1.14 src/bos/usr/lib/sendmail/alias.c, cmdsend, bos411, 9428A410j 3/16/93 09:57:44";
/*
* COMPONENT_NAME: CMDSEND alias.c
*
* FUNCTIONS: MSGSTR, alias, aliaslookup, forward, openaliases,
* readaliases
*
* ORIGINS: 10 26 27
*
* (C) COPYRIGHT International Business Machines Corp. 1985, 1989
* All Rights Reserved
* Licensed Materials - Property of IBM
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
** Sendmail
** Copyright (c) 1983 Eric P. Allman
** Berkeley, California
**
** Copyright (c) 1983 Regents of the University of California.
** All rights reserved. The Berkeley software License Agreement
** specifies the terms and conditions for redistribution.
**
*/
#include <nl_types.h>
#include "sendmail_msg.h"
extern nl_catd catd;
#define MSGSTR(n,s) catgets(catd,MS_SENDMAIL,n,s)
# include <sys/stat.h>
# include <errno.h>
# include <stdio.h>
# include <ctype.h>
# include <fcntl.h>
# include <sys/lockf.h>
# include "conf.h"
# include "useful.h"
# include <netinet/in.h>
# include "sysexits.h"
# include "sendmail.h"
#ifdef _SUN
char *ypDomain = NULL;
char *yellowlookup();
#endif _SUN
# include <string.h>
char *aliaslookup();
ADDRESS *parseaddr ();
char *cat ();
char *DelimChar;
/*
** 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.
**
** Parameters:
** a -- address to alias.
** sendq -- a pointer to the head of the send queue
** to put the aliases in.
**
** Returns:
** none
**
** 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.
*/
typedef struct
{
char *dptr;
int dsize;
} DATUM;
DATUM fetch();
alias(a, sendq)
register ADDRESS *a;
ADDRESS **sendq;
{
register char *p;
# ifdef DEBUG
if (tTd(27, 1))
(void) printf("alias(%s)\n", a->q_paddr);
# endif DEBUG
/* don't realias already aliased names */
if (bitset(QDONTSEND, a->q_flags))
return;
CurEnv->e_to = a->q_paddr;
/*
** Look up this name
*/
if (NoAlias)
p = NULL;
else
p = aliaslookup(a->q_user);
#ifdef _SUN
if (p == NULL)
p = yellowlookup(a);
#endif _SUN
if (p == NULL)
return;
/*
** Match on Alias.
** Deliver to the target list.
*/
# ifdef DEBUG
if (tTd(27, 1))
(void) printf("%s (%s, %s) aliased to %s\n",
a->q_paddr, a->q_host, a->q_user, p);
# endif DEBUG
message(Arpa_Info, MSGSTR(AL_ALTO, "aliased to %s"), p); /*MSG*/
AliasLevel++;
sendtolist(p, a, sendq);
AliasLevel--;
}
/*
** ALIASLOOKUP -- look up a name in the alias file.
**
** Parameters:
** name -- the name to look up.
**
** Returns:
** the value of name.
** NULL if unknown.
**
** Side Effects:
** none.
**
** Warnings:
** The return value will be trashed across calls.
*/
char *
aliaslookup(name)
char *name;
{
DATUM rhs, lhs;
/* create a key for fetch */
lhs.dptr = name;
lhs.dsize = strlen(name) + 1;
rhs = fetch(lhs);
return (rhs.dptr);
}
#ifdef _SUN
/*
** YELLOWLOOKUP -- look up a name in the Yellow Pages.
**
** Parameters:
** a -- the address to look up.
**
** Returns:
** the value of name.
** NULL if unknown.
**
** Side Effects:
** sets
**
** Warnings:
** The return value will be trashed across calls.
*/
char *
yellowlookup(a)
register ADDRESS *a;
{
char *result;
int insize, outsize;
/*
* if we did not find a local alias, then
* try a remote alias through yellow pages.
*/
if (AliasMap==NULL || *AliasMap=='\0') return(NULL);
if (ypDomain==NULL)
{
yp_get_default_domain(&ypDomain);
if (ypDomain == NULL) return(NULL);
#ifdef DEBUG
if (tTd(27, 1))
printf("Yellow pages domain is %s\n",ypDomain);
#endif DEBUG
}
if (bitset(QWASLOCAL,a->q_flags)) return(NULL);
insize = strlen(a->q_user);
/* Try yp_match() with and without insize including a NULL for
* a NULL terminated string. The reason for this is that SUN's
* yp_match() looks for a NULL terminated string and AIX's
* doesn't. Since we don't know who the client is, try both
*/
if (yp_match(ypDomain,AliasMap,a->q_user, insize+1, &result, &outsize))
{
if (yp_match(ypDomain,AliasMap,a->q_user, insize, &result, &outsize))
{
errno = 0;
return(NULL);
}
}
#ifdef DEBUG
if (tTd(27, 1))
printf("%s maps to %s\n",a->q_user, result );
#endif DEBUG
a->q_flags |= QDOMAIN;
return(result);
}
#endif _SUN
/*
** OPENALIASES -- Open and validate alias data base.
**
** Parameters:
** aliasfile -- data base name. Null string disables
** aliasing.
**
** Returns:
** EX_xx return code.
**
** Side Effects:
** None
*/
openaliases(aliasfile)
char *aliasfile;
{
int err, fd;
char lockfile[MAXNAME]; /* data base lock file name */
char aliasdb [MAXNAME]; /* data base name */
static int initialized = FALSE;
char *afn;
errno = 0; /* clean up any junk */
if (initialized)
return (EX_OK);
initialized = TRUE;
if (aliasfile == NULL) /* no, or improper, A option */
{
NoAlias = TRUE;
return (EX_OK);
}
/*
* <aliasfile> is the path of a text input file created by the system
* manager. The system manager uses the "sendmail -bi" function
* to transform this text input into a corresponding data base which
* can be used by sendmail. There is a separate database for each
* differently named alias file. Date base files reside in a
* subdirectory with respect to the input alias file. The sub-
* directory is named "<aliasfile>DB". Inside the subdirectory
* are two files named DB.dir and DB.pag.
*
* When rebuilding a database, the new version is temporarily
* placed in a subdirectory named "<aliasfile>DBt". After the
* new data base files are built, "<aliasfile>DBt" is "mv"d
* to "<aliasfile>DB". Only one rebuild on each input alias file
* should be active simultaneously. This is enforced by system
* management procedures.
*
* The directory "mv" operation must be unitary with respect to
* other processes wanting to open the data base for reading only.
* However, since the date base open is composed of two file open
* operations, the "mv" could take place between them. Therefore
* a "semaphore" must be used to serialize data base open and "mv".
* The semaphore effect is gained by locking the file at relative
* path "<aliasfile>DBl". Read-only users lock this file,
* open and verify data base content, then (keeping the files open)
* release the lock. An update process locks the semaphore file,
* perform the "mv" renaming the directory, then unlocks the file.
* Current users of old versions are unaffected.
*
* Why is a subdirectory used is to make the data base update
* into a single "mv" operation? Else, two "mv"s would be required.
* In that case, the update could fail after the first "mv",
* leaving the data bases inconsistent.
*
* Dbminit is not smart enough to detect this inconsistency, and
* neither are we since we don't compare to the text alias file
* modification date anymore.
*
* We do not look at the alias file itself because it may be updated
* at any time without affecting sendmail (until the sendmail
* -bi is performed for it). We don't want sendmail having an error
* or even issuing a warning if the alias file being used has been
* updated. It is the system manager's responsibility to see that
* the sendmail alias file data base update function is performed
* for each alias file that he updates.
*/
/*
* Create required path strings.
*/
if (cat ( aliasdb, sizeof aliasdb, aliasfile, "DB/DB") == NULL ||
cat (lockfile, sizeof lockfile, aliasfile, "DBl" ) == NULL)
{
syserr (MSGSTR(AL_ELONG, "Alias path \"%s\" too long"), aliasfile); /*MSG*/
return (EX_DB);
}
/*
* Isolate file name portion, and assure distinguishability.
*/
afn = strrchr (aliasfile, '/');
if (afn == NULL) afn = aliasfile;
else afn++;
if (strlen (afn) > MAXFNAME - strlen ("DBl"))
{
syserr (MSGSTR(AL_LONG, "Alias file name \"%s\", too long"), afn); /*MSG*/
return (EX_DB);
}
/*
* Get access to "<aliasfile>DBl".
*/
fd = open (lockfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (fd < 0)
{
syserr (MSGSTR(AL_ELOCK, "Unable to open alias lock file \"%s\""), lockfile); /*MSG*/
return (EX_DB);
}
/*
* Serialize open/update accesses at this point.
*
* This unnecessarily causes all sendmail openaliases
* operations to serialize execution at this point.
* However, this is not a problem since data base validation
* operations are rapid.
*/
if (lockf(fd, F_LOCK, 0) < 0) /* lock the whole thing */
{
syserr (MSGSTR(AL_ELOCK2, "Error locking alias lock file \"%s\""), lockfile); /*MSG*/
(void) close (fd);
return (EX_OSERR);
}
/*
* From this point onwards, release lock before exiting.
* This is done with a close.
*/
/*
* Gain access to data base files (open them with dbminit).
*
* ASSUMPTION: It is assumed that the dbm package keeps the data-
* base files open continuously. If not, then this synchronization
* logic fails.
*
* If we fail to open because it deoesn't exist, try building it
* automatically and re-opening.
*/
if ((err = dbminit(aliasdb)) && (errno == ENOENT)) {
/* try reading it and reopening */
syslog (LOG_NOTICE, MSGSTR(MN_ALFORCE,
"Attempting to build aliases data base \"%s\""), aliasdb);
if (! (err = readaliases(AliasFile) || dbminit(aliasdb)))
syslog (LOG_NOTICE, MSGSTR(MN_ALOK,
"Aliases data base \"%s\" created"), aliasdb);
}
if (err) {
syserr(MSGSTR(AL_EINIT,
"Cannot open aliases data base \"%s\""), aliasdb);
(void) close (fd); /* release locks */
return (EX_DB);
}
/*
* Verify content of the data base. Check for the last entry
* written to assure that data base update run didn't somehow
* fail. This is a relic and possibly can't happen under
* the current scheme, since the "mv" to update the data base
* won't occur unless the data base is completely written to
* begin with. Still, leave it in for warm fuzzies.
*/
if (aliaslookup("@") == NULL) /* file complete? */
{
syserr (MSGSTR(AL_INC, "\"%s\" incomplete"), aliasdb); /*MSG*/
(void) close (fd); /* release locks */
return (EX_DB);
}
/*
* We now have both db files open. Release the lock and we
* can't lose them or see them overwritten before our eyes.
*/
(void) close (fd); /* release locks */
return (EX_OK);
}
/*
** READALIASES -- Builds the aliasfile database.
**
** Parameters:
** aliasfile -- the pathname of the alias file master.
** Null path is an error.
**
** Returns:
** -1 on failure. Absence of alias file is an error.
**
*/
readaliases(aliasfile)
char *aliasfile;
{
register char *p;
char *rhs, *afn;
int skipping;
int naliases, bytes, longest;
int err, fd;
FILE *af;
ADDRESS al, bl;
DATUM key, content;
char line[BUFSIZ];
char lockfile [MAXNAME];
char dirname [MAXNAME];
char tdirname [MAXNAME];
char tdbname [MAXNAME];
char tfiledir [MAXNAME];
char tfilepag [MAXNAME];
errno = 0; /* clean up any junk */
/*
* Null path name is wrong.
*/
if (aliasfile == NULL)
{
syserr (MSGSTR(AL_NOPATH, "No valid alias file path; data base not initialized")); /*MSG*/
return (EX_DB);
}
/*
* SEE OPENALIASES FOR DATABASE UPDATE PHILOSOPHY.
*/
/*
* There is no way to protect against the INPUT alias file being
* changed (say by a text editor) while it is being read for
* purposes of rebuilding the corresponding data base. AIX does
* not provide the necessary exclusive open operation. Lockf
* operations are normally voluntary and are not performed by
* text editors.
*
* This problem is overcome by the operational stipulation that
* the system manager, only, be permitted to update the an alias
* text file; and furthermore that no one but the system manager
* be allowed to perform the necessary "sendmail -bi" function.
* He will perform the text update and data base rebuild serially
* for each alias file. Other active sendmail processes will
* continue to use the old data base until the data base reconstruction
* operation finishes.
*
* There is currently no protection against the system manager
* performing two or more simultaneous data base runs for the
* same data base. Again, this is solved operationally by
* correct procedures on the part of the system manager.
* However, if necessary, interlock can be programmed by locking
* on the input aliasfile.
*
* ANOTHER OPERATIONAL PROBLEM: If the daemon is using the data
* base being updated, it will have to be killed and restarted
* after the update is complete.
*
* The sendmail program, configuration file, and instructions
* for use are modified to reflect the fact that only the system
* manager can perform "sendmail -bi".
*
* SPACE PROBLEM: This update procedure requires enough additional
* space in the minidisk containing /usr/lib to allow for
* creation of the temporary subdirectory for the new data base. This
* may be a problem if there is not enough free space customarily
* available. If this is intolerable, then some other semaphore
* concept will have to be implemented to allow the data base files
* to be updated in place. The update will have to stall until all
* read-only users of the data base finish. Furthermore, new
* read-only users will also have to be stalled until the update
* operation is finished. (This could be easily provided by
* an exclusive open operation that UNIX doesn't have!)
* One unfortunate side affect of this alternate idea is that
* even read-only (ordinary) sendmail operations could wait a long
* time before finishing processing. This is probably intolerable
* in practice.
*/
/*
* Create required path names.
*/
if (cat ( dirname, sizeof dirname, aliasfile, "DB" ) == NULL ||
cat (tdirname, sizeof tdirname, aliasfile, "DBt" ) == NULL ||
cat ( tdbname, sizeof tdbname, tdirname, "/DB" ) == NULL ||
cat (lockfile, sizeof lockfile, aliasfile, "DBl" ) == NULL ||
cat (tfiledir, sizeof tfiledir, tdbname, ".dir") == NULL ||
cat (tfilepag, sizeof tfilepag, tdbname, ".pag") == NULL)
{
syserr (MSGSTR(AL_ELONG, "Alias path \"%s\" too long"), aliasfile); /*MSG*/
return (EX_DB);
}
/*
* Isolate file name portion and assure distinguishability.
*/
afn = strrchr (aliasfile, '/');
if (afn == NULL) afn = aliasfile;
else afn++;
if (strlen (afn) > MAXFNAME - strlen ("DBt"))
{
syserr (MSGSTR(AL_LONG, "Alias file name \"%s\", too long"), afn); /*MSG*/
return (EX_DB);
}
/*
* Create all path names required.
*/
/*
* Create a new work subdirectory at relative path "<aliasfile>DBt".
* Delete any previous version, using xrmdir() to remove dir and
* any files in it.
* This step is necessary since it is conceivable that the
* scratch name is pointing to something real due to a previous
* failure.
*/
if (xrmdir(tdirname, TRUE))
{
syserr (MSGSTR(AL_EULINK, "Can't remove \"%s\""), tdirname); /*MSG*/
return (EX_DB);
}
errno = 0; /* clean up any junk */
/*
* Create fresh directory.
*/
if (mkdir(tdirname, 0770))
{
syserr(MSGSTR(AL_EMAKE, "Cannot make \"%s\""), tdirname);
return (EX_DB);
}
/*
* Create empty data base files.
*/
if ((err = creat(tfiledir, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) < 0)
{
syserr(MSGSTR(AL_EMAKE, "Cannot make \"%s\""), tfiledir); /*MSG*/
return (EX_DB);
}
(void) close (err);
if ((err = creat(tfilepag, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) < 0)
{
syserr(MSGSTR(AL_EMAKE, "Cannot make \"%s\""), tfilepag); /*MSG*/
return (EX_DB);
}
(void) close (err);
/*
* Crank up dbm package.
*/
if ((err = dbminit(tdbname)) < 0)
{
syserr (MSGSTR(AL_EINIT,
"Cannot open aliases data base \"%s\""), tdbname);
return (EX_DB);
}
/*
* Get access to aliasfile.
*/
if ((af = fopen(aliasfile, "r")) == NULL)
{
syserr (MSGSTR(AL_EAVAIL, "Alias file \"%s\", not available"), aliasfile); /*MSG*/
return (EX_DB);
}
/*
** Read and interpret lines
*/
FileName = aliasfile;
LineNumber = 0;
naliases = bytes = longest = 0;
skipping = FALSE;
while (fgets(line, sizeof (line), af) != NULL)
{
int lhssize, rhssize;
LineNumber++;
p = strchr(line, '\n');
if (p != NULL)
*p = '\0';
switch (line[0])
{
case '#':
case '\0':
skipping = FALSE;
continue;
case ' ':
case '\t':
if (!skipping)
syserr(MSGSTR(AL_ESPACE, "Non-continuation line starts with space")); /*MSG*/
skipping = TRUE;
continue;
}
skipping = FALSE;
/*
** Process the LHS
** 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
** detection).
*/
for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
continue;
if (*p++ != ':')
{
syserr(MSGSTR(AL_ECOLON, "Missing colon")); /*MSG*/
continue;
}
if (parseaddr(line, &al, 1, ':') == NULL)
{
syserr(MSGSTR(AL_ENAME, "Illegal alias name")); /*MSG*/
continue;
}
loweraddr(&al);
/*
** Process the RHS.
** 'al' is the internal form of the LHS address.
** 'p' points to the text of the RHS.
*/
rhs = p;
while (1)
{
register char c;
if (CheckAliases)
{
/* do parsing & compression of addresses */
while (*p != '\0')
{
while (isspace(*p) || *p == ',')
p++;
if (*p == '\0')
break;
if (parseaddr(p, &bl, -1, ',') == NULL)
usrerr(MSGSTR(AL_EADDR, "%s... bad address"), p); /*MSG*/
p = DelimChar;
}
}
else
{
p = &p[strlen(p)];
if (p[-1] == '\n')
*--p = '\0';
}
/* see if there should be a continuation line */
c = fgetc(af);
if (ferror (af))
{
syserr (MSGSTR(AL_EREAD, "Error reading alias file \"%s\""), aliasfile); /*MSG*/
(void) fclose (af);
return (EX_DB);
}
if (!feof(af))
(void) ungetc(c, af);
if (c != ' ' && c != '\t')
break;
/* read continuation line */
if (fgets(p, (int) sizeof (line) - (int) (p - line), af) ==
NULL)
break;
LineNumber++;
}
if (ferror (af))
{
syserr (MSGSTR(AL_EREAD, "Error reading alias file \"%s\""), aliasfile); /*MSG*/
(void) fclose (af);
return (EX_DB);
}
if (al.q_mailer != LocalMailer)
{
syserr(MSGSTR(AL_ELOCAL, "Cannot alias non-local names")); /*MSG*/
continue;
}
/*
** Insert alias into DBM file
*/
lhssize = strlen(al.q_user) + 1;
rhssize = strlen(rhs) + 1;
key.dsize = lhssize;
key.dptr = al.q_user;
content.dsize = rhssize;
content.dptr = rhs;
if ((err = store(key, content)) < 0)
{
syserr (MSGSTR(AL_EDBM, "Dbm store error")); /*MSG*/
(void) fclose (af);
return (EX_DB);
}
/* statistics */
naliases++;
bytes += lhssize + rhssize;
if (rhssize > longest)
longest = rhssize;
}
/*
* Make sure we didn't have a read error.
*/
if (ferror (af))
{
syserr (MSGSTR(AL_EREAD, "Error reading alias file \"%s\""), aliasfile); /*MSG*/
(void) fclose (af);
return (EX_DB);
}
/* add the distinquished alias "@" */
key.dsize = 2;
key.dptr = "@";
if ((err = store(key, key)) < 0)
{
syserr (MSGSTR(AL_EDBM, "Dbm store error")); /*MSG*/
(void) fclose (af);
return (EX_DB);
}
(void) fclose (af); /* close input file */
CurEnv->e_to = NULL;
FileName = NULL;
/*
* The complete database is now available under the scratch directory
* name.
*/
/*
* Prevent other openaliases operations from accessing the
* data base until we are through. Open and lock the lock file
* Read-only users only perform their double data
* base open after successfully locking this file. They release
* the lock after they are either successfully opened, or failed.
*/
fd = open (lockfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (fd < 0)
{
syserr (MSGSTR(AL_ELOCK3, "Error opening alias lock file \"%s\""), lockfile); /*MSG*/
return (EX_DB);
}
if (lockf (fd, F_LOCK, 0) < 0)
{
syserr (MSGSTR(AL_ELOCK2, "Error locking alias lock file \"%s\""), lockfile); /*MSG*/
(void) close (fd);
return (EX_OSERR);
}
/*
* From this point onward, the semaphore file must be unlocked
* before exiting. This is done with a close operation.
*/
/*
* Move work directory name to production name.
*
* First, remove production directory name, if it exists. This
* unavoidably causes the directory name to temporarily not exist.
* However, no opens will be attempted since folks who do will be
* pended on the lock file. Those who have the data base files
* already fully opened are OK. In the latter case, they can't
* lose access to their open files even though the file names
* disappear from the directory world.
*/
if (xrmdir(dirname, TRUE))
{
syserr (MSGSTR(AL_EULINK, "Can't remove \"%s\""), dirname); /*MSG*/
(void) close (fd); /* close and rel locks */
return (EX_DB);
}
errno = 0; /* clean up junk */
/*
* The production directory name is no longer available to anyone.
* Attach the production name to the new directory before releasing
* the lock file.
*
* If this fails, we are left with no database. We code this
* all up professionally even though it might be possible to
* prove that a failure here cannot occur.
*/
if (link (tdirname, dirname) < 0) /* point to new direct.*/
{
syserr (MSGSTR(AL_ELINK, "Can't link \"%s\" to \"%s\""), dirname, tdirname); /*MSG*/
(void) close (fd); /* release lock file */
return (EX_DB);
}
/*
* Now allow readers access to the data base under production name.
*/
(void) close (fd); /* release lock file */
/*
* Remove scratch directory name from its parent directory.
*
* An unlink failure means that the scratch names and production
* names are left pointing to the same place. This would imply
* that the next rebuild would be into a working data base, thus
* defeating our protection scheme. This is overcome by assuring
* that the scratch directory name is unlinked (above) before being
* recreated.
*
* Since the database is good, we ignore any failure here.
* If tdirname can't be unlinked, then it will fail later when the
* rebuild is attempted again.
*/
(void) unlink (tdirname);
/*
* Log situation for users info.
*/
message(Arpa_Info, MSGSTR(AL_NBYTES, "%d aliases, longest %d bytes, %d bytes total"), naliases, longest, bytes); /*MSG*/
# ifdef LOG
syslog(LOG_INFO, MSGSTR(AL_NBYTES, "%d aliases, longest %d bytes, %d bytes total"), naliases, longest, bytes); /*MSG*/
# endif LOG
return (EX_OK);
}
/*
** FORWARD -- Try to forward mail
**
** This is similar but not identical to aliasing.
**
** Parameters:
** 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
** in.
** sendq -- a pointer to the head of the send queue to
** put this user's aliases in.
**
** Returns:
** none.
**
** Side Effects:
** New names are added to send queues.
*/
forward(user, sendq)
ADDRESS *user;
ADDRESS **sendq;
{
char buf[60];
# ifdef DEBUG
if (tTd(27, 1))
(void) printf("forward(%s)\n", user->q_paddr);
# endif DEBUG
if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
return;
if (user->q_home == NULL)
syserr (MSGSTR(AL_FORW, "Forward: no home")); /*MSG*/
/* good address -- look for .forward file in home */
define('z', user->q_home, CurEnv);
expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
if (!bitset (QGOODUID, user->q_flags) || !safefile(buf, user->q_uid, S_IREAD))
return;
/* we do have an address to forward to -- do it */
include(buf, MSGSTR(AL_FRWRD, "forwarding"), user, sendq); /*MSG*/
}
#ifdef DEBUG
/*
** dump contents of alias file to stdout
**/
dumpal()
{
extern DATUM firstkey(), nextkey();
DATUM key, al;
puts("\naliases:");
for (key = firstkey(); key.dptr; key = nextkey(key)) {
al = fetch(key);
if (! al.dptr) {
printf("\nerror on fetch(%s)\n", key.dptr);
continue;
}
printf(" key='%s', alias='%s'\n", key.dptr, al.dptr);
}
}
#endif DEBUG