xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 51937)
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>
16*51937Seric # ifdef LOCKF
17*51937Seric # include <fcntl.h>
18*51937Seric # endif
1950577Seric 
2050577Seric # ifdef NEWDB
2150577Seric # include <db.h>
2250577Seric # endif
2350577Seric 
2433728Sbostic #ifndef lint
2551756Seric #ifdef NEWDB
26*51937Seric static char sccsid[] = "@(#)alias.c	5.31 (Berkeley) 12/14/91 (with NEWDB)";
2751756Seric #else
2833728Sbostic #ifdef DBM
29*51937Seric static char sccsid[] = "@(#)alias.c	5.31 (Berkeley) 12/14/91 (with DBM)";
3033728Sbostic #else
31*51937Seric static char sccsid[] = "@(#)alias.c	5.31 (Berkeley) 12/14/91 (without DBM)";
3233728Sbostic #endif
3350575Seric #endif
3433728Sbostic #endif /* not lint */
3551756Seric /*
36292Seric **  ALIAS -- Compute aliases.
37292Seric **
389368Seric **	Scans the alias file for an alias for the given address.
399368Seric **	If found, it arranges to deliver to the alias list instead.
409368Seric **	Uses libdbm database if -DDBM.
41292Seric **
42292Seric **	Parameters:
434097Seric **		a -- address to alias.
444999Seric **		sendq -- a pointer to the head of the send queue
454999Seric **			to put the aliases in.
46292Seric **
47292Seric **	Returns:
48292Seric **		none
49292Seric **
50292Seric **	Side Effects:
513185Seric **		Aliases found are expanded.
52292Seric **
53292Seric **	Notes:
54292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
55292Seric **			done.
56292Seric **
57292Seric **	Deficiencies:
58292Seric **		It should complain about names that are aliased to
59292Seric **			nothing.
60292Seric */
61292Seric 
62292Seric 
631503Smark #ifdef DBM
6451756Seric #ifndef NEWDB
652966Seric typedef struct
662966Seric {
6750575Seric 	char	*data;
6850575Seric 	int	size;
6950575Seric } DBT;
7051756Seric #endif
7150575Seric extern DBT fetch();
7250575Seric #endif /* DBM */
73292Seric 
7450575Seric #ifdef NEWDB
7550575Seric static DB	*AliasDBptr;
7650575Seric #endif
7750575Seric 
784999Seric alias(a, sendq)
794097Seric 	register ADDRESS *a;
804999Seric 	ADDRESS **sendq;
81292Seric {
824081Seric 	register char *p;
835701Seric 	extern char *aliaslookup();
84292Seric 
857671Seric 	if (tTd(27, 1))
864098Seric 		printf("alias(%s)\n", a->q_paddr);
87292Seric 
884098Seric 	/* don't realias already aliased names */
894098Seric 	if (bitset(QDONTSEND, a->q_flags))
904098Seric 		return;
914098Seric 
926898Seric 	CurEnv->e_to = a->q_paddr;
934098Seric 
944314Seric 	/*
954314Seric 	**  Look up this name
964314Seric 	*/
974314Seric 
9824944Seric 	if (NoAlias)
9924944Seric 		p = NULL;
10024944Seric 	else
10124944Seric 		p = aliaslookup(a->q_user);
1024098Seric 	if (p == NULL)
1034098Seric 		return;
104292Seric 
105292Seric 	/*
1064098Seric 	**  Match on Alias.
1074098Seric 	**	Deliver to the target list.
1081515Seric 	*/
1091515Seric 
1107671Seric 	if (tTd(27, 1))
1114098Seric 		printf("%s (%s, %s) aliased to %s\n",
1124098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
1137051Seric 	message(Arpa_Info, "aliased to %s", p);
1144098Seric 	AliasLevel++;
1159614Seric 	sendtolist(p, a, sendq);
1164098Seric 	AliasLevel--;
1174098Seric }
1184098Seric /*
1195701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1205701Seric **
1215701Seric **	Parameters:
1225701Seric **		name -- the name to look up.
1235701Seric **
1245701Seric **	Returns:
1255701Seric **		the value of name.
1265701Seric **		NULL if unknown.
1275701Seric **
1285701Seric **	Side Effects:
1295701Seric **		none.
1305701Seric **
1315701Seric **	Warnings:
1325701Seric **		The return value will be trashed across calls.
1335701Seric */
1345701Seric 
1355701Seric char *
1365701Seric aliaslookup(name)
1375701Seric 	char *name;
1385701Seric {
13950575Seric # if defined(NEWDB) || defined(DBM)
14050575Seric 	DBT rhs, lhs;
14150575Seric 	int s;
1425701Seric 
1435701Seric 	/* create a key for fetch */
14450575Seric 	lhs.data = name;
14550575Seric 	lhs.size = strlen(name) + 1;
14650575Seric # ifdef NEWDB
14751756Seric 	if (AliasDBptr != NULL)
14851756Seric 	{
14951756Seric 		s = AliasDBptr->get(AliasDBptr, &lhs, &rhs, 0);
15051756Seric 		if (s == 0)
15151756Seric 			return (rhs.data);
15251756Seric 	}
15351756Seric # ifdef DBM
15451756Seric 	else
15551756Seric 	{
15651756Seric 		rhs = fetch(lhs);
15751756Seric 		return (rhs.data);
15851756Seric 	}
15951756Seric # endif
16050575Seric # else
1615701Seric 	rhs = fetch(lhs);
16251756Seric 	return (rhs.data);
16350575Seric # endif
16451756Seric # else /* neither NEWDB nor DBM */
1655701Seric 	register STAB *s;
1665701Seric 
1675701Seric 	s = stab(name, ST_ALIAS, ST_FIND);
16851756Seric 	if (s != NULL)
16951756Seric 		return (s->s_alias);
17051756Seric # endif
17151756Seric 	return (NULL);
1725701Seric }
1735701Seric /*
1744098Seric **  INITALIASES -- initialize for aliasing
1754098Seric **
1764098Seric **	Very different depending on whether we are running DBM or not.
1774098Seric **
1784098Seric **	Parameters:
1794098Seric **		aliasfile -- location of aliases.
1804157Seric **		init -- if set and if DBM, initialize the DBM files.
1814098Seric **
1824098Seric **	Returns:
1834098Seric **		none.
1844098Seric **
1854098Seric **	Side Effects:
1864098Seric **		initializes aliases:
1874098Seric **		if DBM:  opens the database.
1884098Seric **		if ~DBM: reads the aliases into the symbol table.
1894098Seric */
1904098Seric 
19140559Sbostic # define DBMMODE	0644
1924157Seric 
1934157Seric initaliases(aliasfile, init)
1944098Seric 	char *aliasfile;
1954157Seric 	bool init;
1964098Seric {
19750575Seric #if defined(DBM) || defined(NEWDB)
1988437Seric 	int atcnt;
19925522Seric 	time_t modtime;
20025522Seric 	bool automatic = FALSE;
2014322Seric 	char buf[MAXNAME];
20250575Seric #endif
2039368Seric 	struct stat stb;
20427176Seric 	static bool initialized = FALSE;
20546928Sbostic 	static int readaliases();
2064322Seric 
20727176Seric 	if (initialized)
20827176Seric 		return;
20927176Seric 	initialized = TRUE;
21027176Seric 
21117984Seric 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
2128437Seric 	{
21325522Seric 		if (aliasfile != NULL && init)
21425522Seric 			syserr("Cannot open %s", aliasfile);
2158437Seric 		NoAlias = TRUE;
21611937Seric 		errno = 0;
2178437Seric 		return;
2188437Seric 	}
2198437Seric 
22050575Seric # if defined(DBM) || defined(NEWDB)
2214322Seric 	/*
2228437Seric 	**  Check to see that the alias file is complete.
2238437Seric 	**	If not, we will assume that someone died, and it is up
2248437Seric 	**	to us to rebuild it.
2258437Seric 	*/
2268437Seric 
22725689Seric 	if (!init)
22850575Seric 	{
22950575Seric # ifdef NEWDB
23050575Seric 		(void) strcpy(buf, aliasfile);
23150575Seric 		(void) strcat(buf, ".db");
23251171Sbostic 		AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
23350576Seric 		if (AliasDBptr == NULL)
23450576Seric 		{
23551756Seric # ifdef DBM
23651756Seric 			dbminit(aliasfile);
23751756Seric # else
23850576Seric 			syserr("initaliases: cannot open %s", buf);
23950576Seric 			NoAlias = TRUE;
24050576Seric 			return;
24151756Seric # endif
24250576Seric 		}
24350575Seric # else
24425689Seric 		dbminit(aliasfile);
24550575Seric # endif
24650575Seric 	}
24717471Seric 	atcnt = SafeAlias * 2;
24817471Seric 	if (atcnt > 0)
24917471Seric 	{
25017471Seric 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
25125689Seric 		{
25225689Seric 			/*
25325689Seric 			**  Reinitialize alias file in case the new
25425689Seric 			**  one is mv'ed in instead of cp'ed in.
25525689Seric 			**
25625689Seric 			**	Only works with new DBM -- old one will
25725689Seric 			**	just consume file descriptors forever.
25825689Seric 			**	If you have a dbmclose() it can be
25925689Seric 			**	added before the sleep(30).
26025689Seric 			*/
26125689Seric 
26250575Seric # ifdef NEWDB
26351756Seric 			if (AliasDBptr != NULL)
26451756Seric 				AliasDBptr->close(AliasDBptr);
26550575Seric # endif
26650575Seric 
26717471Seric 			sleep(30);
26850575Seric # ifdef NEWDB
26950575Seric 			(void) strcpy(buf, aliasfile);
27050575Seric 			(void) strcat(buf, ".db");
27151171Sbostic 			AliasDBptr =
27251171Sbostic 			    dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
27350576Seric 			if (AliasDBptr == NULL)
27450576Seric 			{
27551756Seric # ifdef NDBM
27651756Seric 				dbminit(aliasfile);
27751756Seric # else
27850576Seric 				syserr("initaliases: cannot open %s", buf);
27950576Seric 				NoAlias = TRUE;
28050576Seric 				return;
28151756Seric # endif
28250576Seric 			}
28350575Seric # else
28425689Seric # ifdef NDBM
28525689Seric 			dbminit(aliasfile);
28650575Seric # endif
28750575Seric # endif
28825689Seric 		}
28917471Seric 	}
29017471Seric 	else
29117471Seric 		atcnt = 1;
2928437Seric 
2938437Seric 	/*
2944322Seric 	**  See if the DBM version of the file is out of date with
2954322Seric 	**  the text version.  If so, go into 'init' mode automatically.
29640559Sbostic 	**	This only happens if our effective userid owns the DBM.
29740559Sbostic 	**	Note the unpalatable hack to see if the stat succeeded.
2984322Seric 	*/
2994322Seric 
3004322Seric 	modtime = stb.st_mtime;
3014322Seric 	(void) strcpy(buf, aliasfile);
30250575Seric # ifdef NEWDB
30350575Seric 	(void) strcat(buf, ".db");
30450575Seric # else
3054322Seric 	(void) strcat(buf, ".pag");
30650575Seric # endif
3074322Seric 	stb.st_ino = 0;
30819039Seric 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
3094322Seric 	{
31011937Seric 		errno = 0;
31140559Sbostic 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
3124322Seric 		{
3134322Seric 			init = TRUE;
31425522Seric 			automatic = TRUE;
3157051Seric 			message(Arpa_Info, "rebuilding alias database");
31624944Seric #ifdef LOG
31724944Seric 			if (LogLevel >= 7)
31824944Seric 				syslog(LOG_INFO, "rebuilding alias database");
31924944Seric #endif LOG
3204322Seric 		}
3214322Seric 		else
3224322Seric 		{
32319039Seric #ifdef LOG
32424944Seric 			if (LogLevel >= 7)
32524944Seric 				syslog(LOG_INFO, "alias database out of date");
32619039Seric #endif LOG
3274322Seric 			message(Arpa_Info, "Warning: alias database out of date");
3284322Seric 		}
3294322Seric 	}
3304322Seric 
3314322Seric 
3324322Seric 	/*
3338437Seric 	**  If necessary, load the DBM file.
3344322Seric 	**	If running without DBM, load the symbol table.
3354322Seric 	*/
3364322Seric 
3374157Seric 	if (init)
3388437Seric 	{
33925522Seric #ifdef LOG
34025522Seric 		if (LogLevel >= 6)
34125522Seric 		{
34225522Seric 			extern char *username();
34325522Seric 
34425522Seric 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
34525522Seric 				automatic ? "auto" : "", username());
34625522Seric 		}
34725522Seric #endif LOG
3484157Seric 		readaliases(aliasfile, TRUE);
3498437Seric 	}
3504098Seric # else DBM
3514157Seric 	readaliases(aliasfile, init);
3524157Seric # endif DBM
3534157Seric }
3544157Seric /*
3554157Seric **  READALIASES -- read and process the alias file.
3564157Seric **
3574157Seric **	This routine implements the part of initaliases that occurs
3584157Seric **	when we are not going to use the DBM stuff.
3594157Seric **
3604157Seric **	Parameters:
3614157Seric **		aliasfile -- the pathname of the alias file master.
3624157Seric **		init -- if set, initialize the DBM stuff.
3634157Seric **
3644157Seric **	Returns:
3654157Seric **		none.
3664157Seric **
3674157Seric **	Side Effects:
3684157Seric **		Reads aliasfile into the symbol table.
3694157Seric **		Optionally, builds the .dir & .pag files.
3704157Seric */
3714157Seric 
3724157Seric static
3734157Seric readaliases(aliasfile, init)
3744157Seric 	char *aliasfile;
3754157Seric 	bool init;
3764157Seric {
3774098Seric 	register char *p;
3784098Seric 	char *rhs;
3794098Seric 	bool skipping;
3809368Seric 	int naliases, bytes, longest;
3819368Seric 	FILE *af;
38240970Sbostic 	void (*oldsigint)();
3834098Seric 	ADDRESS al, bl;
3844106Seric 	register STAB *s;
38550575Seric # ifdef NEWDB
38650575Seric 	DB *dbp;
38750575Seric # endif
388*51937Seric # ifdef LOCKF
389*51937Seric 	struct flock fld;
390*51937Seric # endif
3919368Seric 	char line[BUFSIZ];
3924098Seric 
393*51937Seric 	if ((af = fopen(aliasfile, "r+")) == NULL)
3941515Seric 	{
3957671Seric 		if (tTd(27, 1))
3964106Seric 			printf("Can't open %s\n", aliasfile);
3974098Seric 		errno = 0;
3984098Seric 		NoAlias++;
3994098Seric 		return;
4004098Seric 	}
4014314Seric 
40250575Seric # if defined(DBM) || defined(NEWDB)
40319784Seric 	/* see if someone else is rebuilding the alias file already */
40451835Seric # ifdef LOCKF
405*51937Seric 	fld.l_type = F_WRLCK;
406*51937Seric 	fld.l_whence = fld.l_start = fld.l_len = 0;
407*51937Seric 	if (fcntl(fileno(af), F_SETLK, &fld) < 0)
40851835Seric # else
40919784Seric 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
41051835Seric # endif
41119784Seric 	{
41219784Seric 		/* yes, they are -- wait until done and then return */
41319784Seric 		message(Arpa_Info, "Alias file is already being rebuilt");
41419784Seric 		if (OpMode != MD_INITALIAS)
41519784Seric 		{
41619784Seric 			/* wait for other rebuild to complete */
41751835Seric # ifdef LOCKF
418*51937Seric 			(void) fcntl(fileno(af), F_SETLKW, &fld);
41951835Seric # else
42019784Seric 			(void) flock(fileno(af), LOCK_EX);
42151835Seric # endif
42219784Seric 		}
42323108Seric 		(void) fclose(af);
42419784Seric 		errno = 0;
42519784Seric 		return;
42619784Seric 	}
42719784Seric # endif DBM
42819784Seric 
4294314Seric 	/*
43019784Seric 	**  If initializing, create the new DBM files.
43119784Seric 	*/
43219784Seric 
43319784Seric 	if (init)
43419784Seric 	{
43519784Seric 		oldsigint = signal(SIGINT, SIG_IGN);
43651756Seric # ifdef NEWDB
43751756Seric 		(void) strcpy(line, aliasfile);
43851756Seric 		(void) strcat(line, ".db");
43951756Seric 		dbp = dbopen(line,
44051756Seric 		    O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL);
44151756Seric 		if (dbp == NULL)
44251756Seric 		{
44351756Seric 			syserr("readaliases: cannot create %s", line);
44451756Seric 			(void) signal(SIGINT, oldsigint);
44551756Seric 			return;
44651756Seric 		}
44751756Seric # else
44850575Seric # ifdef DBM
44919784Seric 		(void) strcpy(line, aliasfile);
45019784Seric 		(void) strcat(line, ".dir");
45119784Seric 		if (close(creat(line, DBMMODE)) < 0)
45219784Seric 		{
45319784Seric 			syserr("cannot make %s", line);
45419784Seric 			(void) signal(SIGINT, oldsigint);
45519784Seric 			return;
45619784Seric 		}
45719784Seric 		(void) strcpy(line, aliasfile);
45819784Seric 		(void) strcat(line, ".pag");
45919784Seric 		if (close(creat(line, DBMMODE)) < 0)
46019784Seric 		{
46119784Seric 			syserr("cannot make %s", line);
46219784Seric 			(void) signal(SIGINT, oldsigint);
46319784Seric 			return;
46419784Seric 		}
46526501Seric 		dbminit(aliasfile);
46650575Seric # endif
46750575Seric # endif
46819784Seric 	}
46919784Seric 
47019784Seric 	/*
4714314Seric 	**  Read and interpret lines
4724314Seric 	*/
4734314Seric 
4749368Seric 	FileName = aliasfile;
4759368Seric 	LineNumber = 0;
4764322Seric 	naliases = bytes = longest = 0;
4774098Seric 	skipping = FALSE;
4784098Seric 	while (fgets(line, sizeof (line), af) != NULL)
4794098Seric 	{
4804322Seric 		int lhssize, rhssize;
4814322Seric 
4829368Seric 		LineNumber++;
48325278Seric 		p = index(line, '\n');
48425278Seric 		if (p != NULL)
48525278Seric 			*p = '\0';
4864098Seric 		switch (line[0])
4874098Seric 		{
4884098Seric 		  case '#':
4894098Seric 		  case '\0':
4904098Seric 			skipping = FALSE;
4914098Seric 			continue;
4924065Seric 
4934098Seric 		  case ' ':
4944098Seric 		  case '\t':
4954098Seric 			if (!skipping)
4969368Seric 				syserr("Non-continuation line starts with space");
4974098Seric 			skipping = TRUE;
4984097Seric 			continue;
4994098Seric 		}
5004098Seric 		skipping = FALSE;
5011874Seric 
5024314Seric 		/*
5034314Seric 		**  Process the LHS
5044314Seric 		**	Find the final colon, and parse the address.
50516898Seric 		**	It should resolve to a local name -- this will
50616898Seric 		**	be checked later (we want to optionally do
50716898Seric 		**	parsing of the RHS first to maximize error
50816898Seric 		**	detection).
5094314Seric 		*/
5104314Seric 
5114098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
5124097Seric 			continue;
51316898Seric 		if (*p++ != ':')
5144098Seric 		{
5159368Seric 			syserr("missing colon");
5164097Seric 			continue;
5174098Seric 		}
51816898Seric 		if (parseaddr(line, &al, 1, ':') == NULL)
5194098Seric 		{
52016898Seric 			syserr("illegal alias name");
52116898Seric 			continue;
5224098Seric 		}
52316898Seric 		loweraddr(&al);
5244314Seric 
5254314Seric 		/*
5264314Seric 		**  Process the RHS.
5274314Seric 		**	'al' is the internal form of the LHS address.
5284314Seric 		**	'p' points to the text of the RHS.
5294314Seric 		*/
5304314Seric 
5314098Seric 		rhs = p;
5324098Seric 		for (;;)
5334098Seric 		{
5344098Seric 			register char c;
5351515Seric 
53625821Seric 			if (init && CheckAliases)
5374098Seric 			{
5384157Seric 				/* do parsing & compression of addresses */
53925278Seric 				while (*p != '\0')
5404098Seric 				{
54125278Seric 					extern char *DelimChar;
54225278Seric 
54325278Seric 					while (isspace(*p) || *p == ',')
5444157Seric 						p++;
54525278Seric 					if (*p == '\0')
54625278Seric 						break;
54725278Seric 					if (parseaddr(p, &bl, -1, ',') == NULL)
54825278Seric 						usrerr("%s... bad address", p);
54925278Seric 					p = DelimChar;
5504098Seric 				}
5514098Seric 			}
5524157Seric 			else
55315769Seric 			{
55416898Seric 				p = &p[strlen(p)];
55516898Seric 				if (p[-1] == '\n')
55616898Seric 					*--p = '\0';
55715769Seric 			}
5581515Seric 
5594098Seric 			/* see if there should be a continuation line */
5604106Seric 			c = fgetc(af);
5614106Seric 			if (!feof(af))
5624314Seric 				(void) ungetc(c, af);
5634106Seric 			if (c != ' ' && c != '\t')
5644098Seric 				break;
5654098Seric 
5664098Seric 			/* read continuation line */
5674098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
5684098Seric 				break;
5699368Seric 			LineNumber++;
5704098Seric 		}
57116898Seric 		if (al.q_mailer != LocalMailer)
57216898Seric 		{
57316898Seric 			syserr("cannot alias non-local names");
57416898Seric 			continue;
57516898Seric 		}
5764314Seric 
5774314Seric 		/*
5784314Seric 		**  Insert alias into symbol table or DBM file
5794314Seric 		*/
5804314Seric 
58116898Seric 		lhssize = strlen(al.q_user) + 1;
5824322Seric 		rhssize = strlen(rhs) + 1;
5834322Seric 
58450575Seric # if defined(DBM) || defined(NEWDB)
5854157Seric 		if (init)
5864157Seric 		{
58750575Seric 			DBT key, content;
5884157Seric 
58950575Seric 			key.size = lhssize;
59050575Seric 			key.data = al.q_user;
59150575Seric 			content.size = rhssize;
59250575Seric 			content.data = rhs;
59351756Seric # ifdef NEWDB
59451171Sbostic 			if (dbp->put(dbp, &key, &content, 0) != 0)
59550576Seric 				syserr("readaliases: db put (%s)", al.q_user);
59651756Seric # else
59751756Seric 			store(key, content);
59850575Seric # endif
5994157Seric 		}
6004157Seric 		else
6014157Seric # endif DBM
6024157Seric 		{
6034157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
6044157Seric 			s->s_alias = newstr(rhs);
6054157Seric 		}
6064322Seric 
6074322Seric 		/* statistics */
6084322Seric 		naliases++;
6094322Seric 		bytes += lhssize + rhssize;
6104322Seric 		if (rhssize > longest)
6114322Seric 			longest = rhssize;
6121515Seric 	}
61319784Seric 
61450575Seric # if defined(DBM) || defined(NEWDB)
61519784Seric 	if (init)
61619784Seric 	{
61719784Seric 		/* add the distinquished alias "@" */
61850575Seric 		DBT key;
61919784Seric 
62050575Seric 		key.size = 2;
62150575Seric 		key.data = "@";
62250575Seric # ifdef NEWDB
62350576Seric 		if (dbp->sync(dbp) != 0 ||
62451171Sbostic 		    dbp->put(dbp, &key, &key, 0) != 0 ||
62550576Seric 		    dbp->close(dbp) != 0)
62650576Seric 			syserr("readaliases: db close failure");
62750575Seric # else
62819784Seric 		store(key, key);
62950575Seric # endif
63019784Seric 
63119784Seric 		/* restore the old signal */
63219784Seric 		(void) signal(SIGINT, oldsigint);
63319784Seric 	}
63419784Seric # endif DBM
63519784Seric 
63619784Seric 	/* closing the alias file drops the lock */
6374098Seric 	(void) fclose(af);
6386898Seric 	CurEnv->e_to = NULL;
6399368Seric 	FileName = NULL;
6407051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
6414322Seric 			naliases, longest, bytes);
64224944Seric # ifdef LOG
64324944Seric 	if (LogLevel >= 8)
64424944Seric 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
64524944Seric 			naliases, longest, bytes);
64624944Seric # endif LOG
647292Seric }
648292Seric /*
649292Seric **  FORWARD -- Try to forward mail
650292Seric **
651292Seric **	This is similar but not identical to aliasing.
652292Seric **
653292Seric **	Parameters:
6544314Seric **		user -- the name of the user who's mail we would like
6554314Seric **			to forward to.  It must have been verified --
6564314Seric **			i.e., the q_home field must have been filled
6574314Seric **			in.
6584999Seric **		sendq -- a pointer to the head of the send queue to
6594999Seric **			put this user's aliases in.
660292Seric **
661292Seric **	Returns:
6624098Seric **		none.
663292Seric **
664292Seric **	Side Effects:
6653185Seric **		New names are added to send queues.
666292Seric */
667292Seric 
6684999Seric forward(user, sendq)
6692966Seric 	ADDRESS *user;
6704999Seric 	ADDRESS **sendq;
671292Seric {
6724078Seric 	char buf[60];
6734536Seric 	extern bool safefile();
6744069Seric 
6757671Seric 	if (tTd(27, 1))
6764098Seric 		printf("forward(%s)\n", user->q_paddr);
6774098Seric 
6784594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
6794098Seric 		return;
6804314Seric 	if (user->q_home == NULL)
6814314Seric 		syserr("forward: no home");
6824069Seric 
6834069Seric 	/* good address -- look for .forward file in home */
6849368Seric 	define('z', user->q_home, CurEnv);
68516154Seric 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
6864536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
6874098Seric 		return;
6884069Seric 
6894069Seric 	/* we do have an address to forward to -- do it */
6904999Seric 	include(buf, "forwarding", user, sendq);
691292Seric }
692