xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 51756)
122694Sdist /*
235073Sbostic  * Copyright (c) 1983 Eric P. Allman
333728Sbostic  * Copyright (c) 1988 Regents of the University of California.
433728Sbostic  * All rights reserved.
533728Sbostic  *
642824Sbostic  * %sccs.include.redist.c%
733728Sbostic  */
822694Sdist 
950577Seric # include <sys/types.h>
1050577Seric # include <sys/stat.h>
1150577Seric # include <signal.h>
1250577Seric # include <errno.h>
1350577Seric # include "sendmail.h"
1450577Seric # include <sys/file.h>
1550577Seric # include <pwd.h>
1650577Seric 
1750577Seric # ifdef NEWDB
1850577Seric # include <db.h>
1950577Seric # endif
2050577Seric 
2133728Sbostic #ifndef lint
22*51756Seric #ifdef NEWDB
23*51756Seric static char sccsid[] = "@(#)alias.c	5.27 (Berkeley) 11/19/91 (with NEWDB)";
24*51756Seric #else
2533728Sbostic #ifdef DBM
26*51756Seric static char sccsid[] = "@(#)alias.c	5.27 (Berkeley) 11/19/91 (with DBM)";
2733728Sbostic #else
28*51756Seric static char sccsid[] = "@(#)alias.c	5.27 (Berkeley) 11/19/91 (without DBM)";
2933728Sbostic #endif
3050575Seric #endif
3133728Sbostic #endif /* not lint */
32*51756Seric /*
33292Seric **  ALIAS -- Compute aliases.
34292Seric **
359368Seric **	Scans the alias file for an alias for the given address.
369368Seric **	If found, it arranges to deliver to the alias list instead.
379368Seric **	Uses libdbm database if -DDBM.
38292Seric **
39292Seric **	Parameters:
404097Seric **		a -- address to alias.
414999Seric **		sendq -- a pointer to the head of the send queue
424999Seric **			to put the aliases in.
43292Seric **
44292Seric **	Returns:
45292Seric **		none
46292Seric **
47292Seric **	Side Effects:
483185Seric **		Aliases found are expanded.
49292Seric **
50292Seric **	Notes:
51292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
52292Seric **			done.
53292Seric **
54292Seric **	Deficiencies:
55292Seric **		It should complain about names that are aliased to
56292Seric **			nothing.
57292Seric */
58292Seric 
59292Seric 
601503Smark #ifdef DBM
61*51756Seric #ifndef NEWDB
622966Seric typedef struct
632966Seric {
6450575Seric 	char	*data;
6550575Seric 	int	size;
6650575Seric } DBT;
67*51756Seric #endif
6850575Seric extern DBT fetch();
6950575Seric #endif /* DBM */
70292Seric 
7150575Seric #ifdef NEWDB
7250575Seric static DB	*AliasDBptr;
7350575Seric #endif
7450575Seric 
754999Seric alias(a, sendq)
764097Seric 	register ADDRESS *a;
774999Seric 	ADDRESS **sendq;
78292Seric {
794081Seric 	register char *p;
805701Seric 	extern char *aliaslookup();
81292Seric 
827671Seric 	if (tTd(27, 1))
834098Seric 		printf("alias(%s)\n", a->q_paddr);
84292Seric 
854098Seric 	/* don't realias already aliased names */
864098Seric 	if (bitset(QDONTSEND, a->q_flags))
874098Seric 		return;
884098Seric 
896898Seric 	CurEnv->e_to = a->q_paddr;
904098Seric 
914314Seric 	/*
924314Seric 	**  Look up this name
934314Seric 	*/
944314Seric 
9524944Seric 	if (NoAlias)
9624944Seric 		p = NULL;
9724944Seric 	else
9824944Seric 		p = aliaslookup(a->q_user);
994098Seric 	if (p == NULL)
1004098Seric 		return;
101292Seric 
102292Seric 	/*
1034098Seric 	**  Match on Alias.
1044098Seric 	**	Deliver to the target list.
1051515Seric 	*/
1061515Seric 
1077671Seric 	if (tTd(27, 1))
1084098Seric 		printf("%s (%s, %s) aliased to %s\n",
1094098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
1107051Seric 	message(Arpa_Info, "aliased to %s", p);
1114098Seric 	AliasLevel++;
1129614Seric 	sendtolist(p, a, sendq);
1134098Seric 	AliasLevel--;
1144098Seric }
1154098Seric /*
1165701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1175701Seric **
1185701Seric **	Parameters:
1195701Seric **		name -- the name to look up.
1205701Seric **
1215701Seric **	Returns:
1225701Seric **		the value of name.
1235701Seric **		NULL if unknown.
1245701Seric **
1255701Seric **	Side Effects:
1265701Seric **		none.
1275701Seric **
1285701Seric **	Warnings:
1295701Seric **		The return value will be trashed across calls.
1305701Seric */
1315701Seric 
1325701Seric char *
1335701Seric aliaslookup(name)
1345701Seric 	char *name;
1355701Seric {
13650575Seric # if defined(NEWDB) || defined(DBM)
13750575Seric 	DBT rhs, lhs;
13850575Seric 	int s;
1395701Seric 
1405701Seric 	/* create a key for fetch */
14150575Seric 	lhs.data = name;
14250575Seric 	lhs.size = strlen(name) + 1;
14350575Seric # ifdef NEWDB
144*51756Seric 	if (AliasDBptr != NULL)
145*51756Seric 	{
146*51756Seric 		s = AliasDBptr->get(AliasDBptr, &lhs, &rhs, 0);
147*51756Seric 		if (s == 0)
148*51756Seric 			return (rhs.data);
149*51756Seric 	}
150*51756Seric # ifdef DBM
151*51756Seric 	else
152*51756Seric 	{
153*51756Seric 		rhs = fetch(lhs);
154*51756Seric 		return (rhs.data);
155*51756Seric 	}
156*51756Seric # endif
15750575Seric # else
1585701Seric 	rhs = fetch(lhs);
159*51756Seric 	return (rhs.data);
16050575Seric # endif
161*51756Seric # else /* neither NEWDB nor DBM */
1625701Seric 	register STAB *s;
1635701Seric 
1645701Seric 	s = stab(name, ST_ALIAS, ST_FIND);
165*51756Seric 	if (s != NULL)
166*51756Seric 		return (s->s_alias);
167*51756Seric # endif
168*51756Seric 	return (NULL);
1695701Seric }
1705701Seric /*
1714098Seric **  INITALIASES -- initialize for aliasing
1724098Seric **
1734098Seric **	Very different depending on whether we are running DBM or not.
1744098Seric **
1754098Seric **	Parameters:
1764098Seric **		aliasfile -- location of aliases.
1774157Seric **		init -- if set and if DBM, initialize the DBM files.
1784098Seric **
1794098Seric **	Returns:
1804098Seric **		none.
1814098Seric **
1824098Seric **	Side Effects:
1834098Seric **		initializes aliases:
1844098Seric **		if DBM:  opens the database.
1854098Seric **		if ~DBM: reads the aliases into the symbol table.
1864098Seric */
1874098Seric 
18840559Sbostic # define DBMMODE	0644
1894157Seric 
1904157Seric initaliases(aliasfile, init)
1914098Seric 	char *aliasfile;
1924157Seric 	bool init;
1934098Seric {
19450575Seric #if defined(DBM) || defined(NEWDB)
1958437Seric 	int atcnt;
19625522Seric 	time_t modtime;
19725522Seric 	bool automatic = FALSE;
1984322Seric 	char buf[MAXNAME];
19950575Seric #endif
2009368Seric 	struct stat stb;
20127176Seric 	static bool initialized = FALSE;
20246928Sbostic 	static int readaliases();
2034322Seric 
20427176Seric 	if (initialized)
20527176Seric 		return;
20627176Seric 	initialized = TRUE;
20727176Seric 
20817984Seric 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
2098437Seric 	{
21025522Seric 		if (aliasfile != NULL && init)
21125522Seric 			syserr("Cannot open %s", aliasfile);
2128437Seric 		NoAlias = TRUE;
21311937Seric 		errno = 0;
2148437Seric 		return;
2158437Seric 	}
2168437Seric 
21750575Seric # if defined(DBM) || defined(NEWDB)
2184322Seric 	/*
2198437Seric 	**  Check to see that the alias file is complete.
2208437Seric 	**	If not, we will assume that someone died, and it is up
2218437Seric 	**	to us to rebuild it.
2228437Seric 	*/
2238437Seric 
22425689Seric 	if (!init)
22550575Seric 	{
22650575Seric # ifdef NEWDB
22750575Seric 		(void) strcpy(buf, aliasfile);
22850575Seric 		(void) strcat(buf, ".db");
22951171Sbostic 		AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
23050576Seric 		if (AliasDBptr == NULL)
23150576Seric 		{
232*51756Seric # ifdef DBM
233*51756Seric 			dbminit(aliasfile);
234*51756Seric # else
23550576Seric 			syserr("initaliases: cannot open %s", buf);
23650576Seric 			NoAlias = TRUE;
23750576Seric 			return;
238*51756Seric # endif
23950576Seric 		}
24050575Seric # else
24125689Seric 		dbminit(aliasfile);
24250575Seric # endif
24350575Seric 	}
24417471Seric 	atcnt = SafeAlias * 2;
24517471Seric 	if (atcnt > 0)
24617471Seric 	{
24717471Seric 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
24825689Seric 		{
24925689Seric 			/*
25025689Seric 			**  Reinitialize alias file in case the new
25125689Seric 			**  one is mv'ed in instead of cp'ed in.
25225689Seric 			**
25325689Seric 			**	Only works with new DBM -- old one will
25425689Seric 			**	just consume file descriptors forever.
25525689Seric 			**	If you have a dbmclose() it can be
25625689Seric 			**	added before the sleep(30).
25725689Seric 			*/
25825689Seric 
25950575Seric # ifdef NEWDB
260*51756Seric 			if (AliasDBptr != NULL)
261*51756Seric 				AliasDBptr->close(AliasDBptr);
26250575Seric # endif
26350575Seric 
26417471Seric 			sleep(30);
26550575Seric # ifdef NEWDB
26650575Seric 			(void) strcpy(buf, aliasfile);
26750575Seric 			(void) strcat(buf, ".db");
26851171Sbostic 			AliasDBptr =
26951171Sbostic 			    dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
27050576Seric 			if (AliasDBptr == NULL)
27150576Seric 			{
272*51756Seric # ifdef NDBM
273*51756Seric 				dbminit(aliasfile);
274*51756Seric # else
27550576Seric 				syserr("initaliases: cannot open %s", buf);
27650576Seric 				NoAlias = TRUE;
27750576Seric 				return;
278*51756Seric # endif
27950576Seric 			}
28050575Seric # else
28125689Seric # ifdef NDBM
28225689Seric 			dbminit(aliasfile);
28350575Seric # endif
28450575Seric # endif
28525689Seric 		}
28617471Seric 	}
28717471Seric 	else
28817471Seric 		atcnt = 1;
2898437Seric 
2908437Seric 	/*
2914322Seric 	**  See if the DBM version of the file is out of date with
2924322Seric 	**  the text version.  If so, go into 'init' mode automatically.
29340559Sbostic 	**	This only happens if our effective userid owns the DBM.
29440559Sbostic 	**	Note the unpalatable hack to see if the stat succeeded.
2954322Seric 	*/
2964322Seric 
2974322Seric 	modtime = stb.st_mtime;
2984322Seric 	(void) strcpy(buf, aliasfile);
29950575Seric # ifdef NEWDB
30050575Seric 	(void) strcat(buf, ".db");
30150575Seric # else
3024322Seric 	(void) strcat(buf, ".pag");
30350575Seric # endif
3044322Seric 	stb.st_ino = 0;
30519039Seric 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
3064322Seric 	{
30711937Seric 		errno = 0;
30840559Sbostic 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
3094322Seric 		{
3104322Seric 			init = TRUE;
31125522Seric 			automatic = TRUE;
3127051Seric 			message(Arpa_Info, "rebuilding alias database");
31324944Seric #ifdef LOG
31424944Seric 			if (LogLevel >= 7)
31524944Seric 				syslog(LOG_INFO, "rebuilding alias database");
31624944Seric #endif LOG
3174322Seric 		}
3184322Seric 		else
3194322Seric 		{
32019039Seric #ifdef LOG
32124944Seric 			if (LogLevel >= 7)
32224944Seric 				syslog(LOG_INFO, "alias database out of date");
32319039Seric #endif LOG
3244322Seric 			message(Arpa_Info, "Warning: alias database out of date");
3254322Seric 		}
3264322Seric 	}
3274322Seric 
3284322Seric 
3294322Seric 	/*
3308437Seric 	**  If necessary, load the DBM file.
3314322Seric 	**	If running without DBM, load the symbol table.
3324322Seric 	*/
3334322Seric 
3344157Seric 	if (init)
3358437Seric 	{
33625522Seric #ifdef LOG
33725522Seric 		if (LogLevel >= 6)
33825522Seric 		{
33925522Seric 			extern char *username();
34025522Seric 
34125522Seric 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
34225522Seric 				automatic ? "auto" : "", username());
34325522Seric 		}
34425522Seric #endif LOG
3454157Seric 		readaliases(aliasfile, TRUE);
3468437Seric 	}
3474098Seric # else DBM
3484157Seric 	readaliases(aliasfile, init);
3494157Seric # endif DBM
3504157Seric }
3514157Seric /*
3524157Seric **  READALIASES -- read and process the alias file.
3534157Seric **
3544157Seric **	This routine implements the part of initaliases that occurs
3554157Seric **	when we are not going to use the DBM stuff.
3564157Seric **
3574157Seric **	Parameters:
3584157Seric **		aliasfile -- the pathname of the alias file master.
3594157Seric **		init -- if set, initialize the DBM stuff.
3604157Seric **
3614157Seric **	Returns:
3624157Seric **		none.
3634157Seric **
3644157Seric **	Side Effects:
3654157Seric **		Reads aliasfile into the symbol table.
3664157Seric **		Optionally, builds the .dir & .pag files.
3674157Seric */
3684157Seric 
3694157Seric static
3704157Seric readaliases(aliasfile, init)
3714157Seric 	char *aliasfile;
3724157Seric 	bool init;
3734157Seric {
3744098Seric 	register char *p;
3754098Seric 	char *rhs;
3764098Seric 	bool skipping;
3779368Seric 	int naliases, bytes, longest;
3789368Seric 	FILE *af;
37940970Sbostic 	void (*oldsigint)();
3804098Seric 	ADDRESS al, bl;
3814106Seric 	register STAB *s;
38250575Seric # ifdef NEWDB
38350575Seric 	DB *dbp;
38450575Seric # endif
3859368Seric 	char line[BUFSIZ];
3864098Seric 
3874098Seric 	if ((af = fopen(aliasfile, "r")) == NULL)
3881515Seric 	{
3897671Seric 		if (tTd(27, 1))
3904106Seric 			printf("Can't open %s\n", aliasfile);
3914098Seric 		errno = 0;
3924098Seric 		NoAlias++;
3934098Seric 		return;
3944098Seric 	}
3954314Seric 
39650575Seric # if defined(DBM) || defined(NEWDB)
39719784Seric 	/* see if someone else is rebuilding the alias file already */
39819784Seric 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
39919784Seric 	{
40019784Seric 		/* yes, they are -- wait until done and then return */
40119784Seric 		message(Arpa_Info, "Alias file is already being rebuilt");
40219784Seric 		if (OpMode != MD_INITALIAS)
40319784Seric 		{
40419784Seric 			/* wait for other rebuild to complete */
40519784Seric 			(void) flock(fileno(af), LOCK_EX);
40619784Seric 		}
40723108Seric 		(void) fclose(af);
40819784Seric 		errno = 0;
40919784Seric 		return;
41019784Seric 	}
41119784Seric # endif DBM
41219784Seric 
4134314Seric 	/*
41419784Seric 	**  If initializing, create the new DBM files.
41519784Seric 	*/
41619784Seric 
41719784Seric 	if (init)
41819784Seric 	{
41919784Seric 		oldsigint = signal(SIGINT, SIG_IGN);
420*51756Seric # ifdef NEWDB
421*51756Seric 		(void) strcpy(line, aliasfile);
422*51756Seric 		(void) strcat(line, ".db");
423*51756Seric 		dbp = dbopen(line,
424*51756Seric 		    O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL);
425*51756Seric 		if (dbp == NULL)
426*51756Seric 		{
427*51756Seric 			syserr("readaliases: cannot create %s", line);
428*51756Seric 			(void) signal(SIGINT, oldsigint);
429*51756Seric 			return;
430*51756Seric 		}
431*51756Seric # else
43250575Seric # ifdef DBM
43319784Seric 		(void) strcpy(line, aliasfile);
43419784Seric 		(void) strcat(line, ".dir");
43519784Seric 		if (close(creat(line, DBMMODE)) < 0)
43619784Seric 		{
43719784Seric 			syserr("cannot make %s", line);
43819784Seric 			(void) signal(SIGINT, oldsigint);
43919784Seric 			return;
44019784Seric 		}
44119784Seric 		(void) strcpy(line, aliasfile);
44219784Seric 		(void) strcat(line, ".pag");
44319784Seric 		if (close(creat(line, DBMMODE)) < 0)
44419784Seric 		{
44519784Seric 			syserr("cannot make %s", line);
44619784Seric 			(void) signal(SIGINT, oldsigint);
44719784Seric 			return;
44819784Seric 		}
44926501Seric 		dbminit(aliasfile);
45050575Seric # endif
45150575Seric # endif
45219784Seric 	}
45319784Seric 
45419784Seric 	/*
4554314Seric 	**  Read and interpret lines
4564314Seric 	*/
4574314Seric 
4589368Seric 	FileName = aliasfile;
4599368Seric 	LineNumber = 0;
4604322Seric 	naliases = bytes = longest = 0;
4614098Seric 	skipping = FALSE;
4624098Seric 	while (fgets(line, sizeof (line), af) != NULL)
4634098Seric 	{
4644322Seric 		int lhssize, rhssize;
4654322Seric 
4669368Seric 		LineNumber++;
46725278Seric 		p = index(line, '\n');
46825278Seric 		if (p != NULL)
46925278Seric 			*p = '\0';
4704098Seric 		switch (line[0])
4714098Seric 		{
4724098Seric 		  case '#':
4734098Seric 		  case '\0':
4744098Seric 			skipping = FALSE;
4754098Seric 			continue;
4764065Seric 
4774098Seric 		  case ' ':
4784098Seric 		  case '\t':
4794098Seric 			if (!skipping)
4809368Seric 				syserr("Non-continuation line starts with space");
4814098Seric 			skipping = TRUE;
4824097Seric 			continue;
4834098Seric 		}
4844098Seric 		skipping = FALSE;
4851874Seric 
4864314Seric 		/*
4874314Seric 		**  Process the LHS
4884314Seric 		**	Find the final colon, and parse the address.
48916898Seric 		**	It should resolve to a local name -- this will
49016898Seric 		**	be checked later (we want to optionally do
49116898Seric 		**	parsing of the RHS first to maximize error
49216898Seric 		**	detection).
4934314Seric 		*/
4944314Seric 
4954098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
4964097Seric 			continue;
49716898Seric 		if (*p++ != ':')
4984098Seric 		{
4999368Seric 			syserr("missing colon");
5004097Seric 			continue;
5014098Seric 		}
50216898Seric 		if (parseaddr(line, &al, 1, ':') == NULL)
5034098Seric 		{
50416898Seric 			syserr("illegal alias name");
50516898Seric 			continue;
5064098Seric 		}
50716898Seric 		loweraddr(&al);
5084314Seric 
5094314Seric 		/*
5104314Seric 		**  Process the RHS.
5114314Seric 		**	'al' is the internal form of the LHS address.
5124314Seric 		**	'p' points to the text of the RHS.
5134314Seric 		*/
5144314Seric 
5154098Seric 		rhs = p;
5164098Seric 		for (;;)
5174098Seric 		{
5184098Seric 			register char c;
5191515Seric 
52025821Seric 			if (init && CheckAliases)
5214098Seric 			{
5224157Seric 				/* do parsing & compression of addresses */
52325278Seric 				while (*p != '\0')
5244098Seric 				{
52525278Seric 					extern char *DelimChar;
52625278Seric 
52725278Seric 					while (isspace(*p) || *p == ',')
5284157Seric 						p++;
52925278Seric 					if (*p == '\0')
53025278Seric 						break;
53125278Seric 					if (parseaddr(p, &bl, -1, ',') == NULL)
53225278Seric 						usrerr("%s... bad address", p);
53325278Seric 					p = DelimChar;
5344098Seric 				}
5354098Seric 			}
5364157Seric 			else
53715769Seric 			{
53816898Seric 				p = &p[strlen(p)];
53916898Seric 				if (p[-1] == '\n')
54016898Seric 					*--p = '\0';
54115769Seric 			}
5421515Seric 
5434098Seric 			/* see if there should be a continuation line */
5444106Seric 			c = fgetc(af);
5454106Seric 			if (!feof(af))
5464314Seric 				(void) ungetc(c, af);
5474106Seric 			if (c != ' ' && c != '\t')
5484098Seric 				break;
5494098Seric 
5504098Seric 			/* read continuation line */
5514098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
5524098Seric 				break;
5539368Seric 			LineNumber++;
5544098Seric 		}
55516898Seric 		if (al.q_mailer != LocalMailer)
55616898Seric 		{
55716898Seric 			syserr("cannot alias non-local names");
55816898Seric 			continue;
55916898Seric 		}
5604314Seric 
5614314Seric 		/*
5624314Seric 		**  Insert alias into symbol table or DBM file
5634314Seric 		*/
5644314Seric 
56516898Seric 		lhssize = strlen(al.q_user) + 1;
5664322Seric 		rhssize = strlen(rhs) + 1;
5674322Seric 
56850575Seric # if defined(DBM) || defined(NEWDB)
5694157Seric 		if (init)
5704157Seric 		{
57150575Seric 			DBT key, content;
5724157Seric 
57350575Seric 			key.size = lhssize;
57450575Seric 			key.data = al.q_user;
57550575Seric 			content.size = rhssize;
57650575Seric 			content.data = rhs;
577*51756Seric # ifdef NEWDB
57851171Sbostic 			if (dbp->put(dbp, &key, &content, 0) != 0)
57950576Seric 				syserr("readaliases: db put (%s)", al.q_user);
580*51756Seric # else
581*51756Seric 			store(key, content);
58250575Seric # endif
5834157Seric 		}
5844157Seric 		else
5854157Seric # endif DBM
5864157Seric 		{
5874157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
5884157Seric 			s->s_alias = newstr(rhs);
5894157Seric 		}
5904322Seric 
5914322Seric 		/* statistics */
5924322Seric 		naliases++;
5934322Seric 		bytes += lhssize + rhssize;
5944322Seric 		if (rhssize > longest)
5954322Seric 			longest = rhssize;
5961515Seric 	}
59719784Seric 
59850575Seric # if defined(DBM) || defined(NEWDB)
59919784Seric 	if (init)
60019784Seric 	{
60119784Seric 		/* add the distinquished alias "@" */
60250575Seric 		DBT key;
60319784Seric 
60450575Seric 		key.size = 2;
60550575Seric 		key.data = "@";
60650575Seric # ifdef NEWDB
60750576Seric 		if (dbp->sync(dbp) != 0 ||
60851171Sbostic 		    dbp->put(dbp, &key, &key, 0) != 0 ||
60950576Seric 		    dbp->close(dbp) != 0)
61050576Seric 			syserr("readaliases: db close failure");
61150575Seric # else
61219784Seric 		store(key, key);
61350575Seric # endif
61419784Seric 
61519784Seric 		/* restore the old signal */
61619784Seric 		(void) signal(SIGINT, oldsigint);
61719784Seric 	}
61819784Seric # endif DBM
61919784Seric 
62019784Seric 	/* closing the alias file drops the lock */
6214098Seric 	(void) fclose(af);
6226898Seric 	CurEnv->e_to = NULL;
6239368Seric 	FileName = NULL;
6247051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
6254322Seric 			naliases, longest, bytes);
62624944Seric # ifdef LOG
62724944Seric 	if (LogLevel >= 8)
62824944Seric 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
62924944Seric 			naliases, longest, bytes);
63024944Seric # endif LOG
631292Seric }
632292Seric /*
633292Seric **  FORWARD -- Try to forward mail
634292Seric **
635292Seric **	This is similar but not identical to aliasing.
636292Seric **
637292Seric **	Parameters:
6384314Seric **		user -- the name of the user who's mail we would like
6394314Seric **			to forward to.  It must have been verified --
6404314Seric **			i.e., the q_home field must have been filled
6414314Seric **			in.
6424999Seric **		sendq -- a pointer to the head of the send queue to
6434999Seric **			put this user's aliases in.
644292Seric **
645292Seric **	Returns:
6464098Seric **		none.
647292Seric **
648292Seric **	Side Effects:
6493185Seric **		New names are added to send queues.
650292Seric */
651292Seric 
6524999Seric forward(user, sendq)
6532966Seric 	ADDRESS *user;
6544999Seric 	ADDRESS **sendq;
655292Seric {
6564078Seric 	char buf[60];
6574536Seric 	extern bool safefile();
6584069Seric 
6597671Seric 	if (tTd(27, 1))
6604098Seric 		printf("forward(%s)\n", user->q_paddr);
6614098Seric 
6624594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
6634098Seric 		return;
6644314Seric 	if (user->q_home == NULL)
6654314Seric 		syserr("forward: no home");
6664069Seric 
6674069Seric 	/* good address -- look for .forward file in home */
6689368Seric 	define('z', user->q_home, CurEnv);
66916154Seric 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
6704536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
6714098Seric 		return;
6724069Seric 
6734069Seric 	/* we do have an address to forward to -- do it */
6744999Seric 	include(buf, "forwarding", user, sendq);
675292Seric }
676