xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 57249)
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 "sendmail.h"
1350577Seric # include <sys/file.h>
1450577Seric # include <pwd.h>
1551937Seric # ifdef LOCKF
1651937Seric # include <fcntl.h>
1751937Seric # endif
1850577Seric 
1956845Seric # ifdef DBM
2056848Seric ERROR: DBM is no longer supported -- use NDBM instead.
2156845Seric # endif
2256845Seric 
2350577Seric # ifdef NEWDB
2450577Seric # include <db.h>
2550577Seric # endif
2650577Seric 
2756766Seric # ifdef NDBM
2856845Seric # include <ndbm.h>
2956766Seric # endif
3056766Seric 
3133728Sbostic #ifndef lint
3251756Seric #ifdef NEWDB
33*57249Seric static char sccsid[] = "@(#)alias.c	5.44 (Berkeley) 12/21/92 (with NEWDB)";
3451756Seric #else
3556845Seric #ifdef NDBM
36*57249Seric static char sccsid[] = "@(#)alias.c	5.44 (Berkeley) 12/21/92 (with NDBM)";
3733728Sbostic #else
38*57249Seric static char sccsid[] = "@(#)alias.c	5.44 (Berkeley) 12/21/92 (without NDBM)";
3933728Sbostic #endif
4050575Seric #endif
4133728Sbostic #endif /* not lint */
4251756Seric /*
43292Seric **  ALIAS -- Compute aliases.
44292Seric **
459368Seric **	Scans the alias file for an alias for the given address.
469368Seric **	If found, it arranges to deliver to the alias list instead.
479368Seric **	Uses libdbm database if -DDBM.
48292Seric **
49292Seric **	Parameters:
504097Seric **		a -- address to alias.
514999Seric **		sendq -- a pointer to the head of the send queue
524999Seric **			to put the aliases in.
53292Seric **
54292Seric **	Returns:
55292Seric **		none
56292Seric **
57292Seric **	Side Effects:
583185Seric **		Aliases found are expanded.
59292Seric **
60292Seric **	Notes:
61292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
62292Seric **			done.
63292Seric **
64292Seric **	Deficiencies:
65292Seric **		It should complain about names that are aliased to
66292Seric **			nothing.
67292Seric */
68292Seric 
69292Seric 
7053742Seric /*
7153742Seric **  Sun YP servers read the dbm files directly, so we have to build them
7253742Seric **  even if NEWDB
7353742Seric */
7453742Seric 
7556845Seric #ifdef NDBM
7653742Seric # ifndef NEWDB
7753742Seric #  define IF_MAKEDBMFILES
7853742Seric # else
7953742Seric #  ifdef YPCOMPAT
8053742Seric #   define IF_MAKEDBMFILES		if (makedbmfiles)
8153742Seric #  endif
8253742Seric # endif
8353742Seric #endif
8453742Seric 
8556845Seric typedef union
862966Seric {
8756845Seric #ifdef NDBM
8856845Seric 	datum	dbm;
8951756Seric #endif
9056845Seric #ifdef NEWDB
9156845Seric 	DBT	dbt;
9256845Seric #endif
9356845Seric 	struct
9456845Seric 	{
9556845Seric 		char	*data;
9656845Seric 		int	size;
9756845Seric 	} xx;
9856845Seric } DBdatum;
99292Seric 
10050575Seric #ifdef NEWDB
10150575Seric static DB	*AliasDBptr;
10250575Seric #endif
10356845Seric #ifdef NDBM
10456845Seric static DBM	*AliasDBMptr;
10556845Seric #endif
10650575Seric 
10755012Seric alias(a, sendq, e)
1084097Seric 	register ADDRESS *a;
1094999Seric 	ADDRESS **sendq;
11055012Seric 	register ENVELOPE *e;
111292Seric {
1124081Seric 	register char *p;
1135701Seric 	extern char *aliaslookup();
114292Seric 
1157671Seric 	if (tTd(27, 1))
1164098Seric 		printf("alias(%s)\n", a->q_paddr);
117292Seric 
1184098Seric 	/* don't realias already aliased names */
1194098Seric 	if (bitset(QDONTSEND, a->q_flags))
1204098Seric 		return;
1214098Seric 
12255012Seric 	e->e_to = a->q_paddr;
1234098Seric 
1244314Seric 	/*
1254314Seric 	**  Look up this name
1264314Seric 	*/
1274314Seric 
12824944Seric 	if (NoAlias)
12924944Seric 		p = NULL;
13024944Seric 	else
13124944Seric 		p = aliaslookup(a->q_user);
1324098Seric 	if (p == NULL)
1334098Seric 		return;
134292Seric 
135292Seric 	/*
1364098Seric 	**  Match on Alias.
1374098Seric 	**	Deliver to the target list.
1381515Seric 	*/
1391515Seric 
1407671Seric 	if (tTd(27, 1))
1414098Seric 		printf("%s (%s, %s) aliased to %s\n",
1424098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
1437051Seric 	message(Arpa_Info, "aliased to %s", p);
1444098Seric 	AliasLevel++;
14555012Seric 	sendtolist(p, a, sendq, e);
1464098Seric 	AliasLevel--;
1474098Seric }
1484098Seric /*
1495701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1505701Seric **
1515701Seric **	Parameters:
1525701Seric **		name -- the name to look up.
1535701Seric **
1545701Seric **	Returns:
1555701Seric **		the value of name.
1565701Seric **		NULL if unknown.
1575701Seric **
1585701Seric **	Side Effects:
1595701Seric **		none.
1605701Seric **
1615701Seric **	Warnings:
1625701Seric **		The return value will be trashed across calls.
1635701Seric */
1645701Seric 
1655701Seric char *
1665701Seric aliaslookup(name)
1675701Seric 	char *name;
1685701Seric {
16956845Seric # if defined(NEWDB) || defined(NDBM)
17056845Seric 	DBdatum rhs, lhs;
17150575Seric 	int s;
1725701Seric 
1735701Seric 	/* create a key for fetch */
17456845Seric 	lhs.xx.data = name;
17556845Seric 	lhs.xx.size = strlen(name) + 1;
17650575Seric # ifdef NEWDB
17751756Seric 	if (AliasDBptr != NULL)
17851756Seric 	{
17956845Seric 		s = AliasDBptr->get(AliasDBptr, &lhs.dbt, &rhs.dbt, 0);
18051756Seric 		if (s == 0)
18156845Seric 			return (rhs.dbt.data);
18251756Seric 	}
18356845Seric # ifdef NDBM
184*57249Seric 	else if (AliasDBMptr != NULL)
18551756Seric 	{
18656845Seric 		rhs.dbm = dbm_fetch(AliasDBMptr, lhs.dbm);
18756845Seric 		return (rhs.dbm.dptr);
18851756Seric 	}
18956845Seric # endif /* NDBM */
19056845Seric # else /* not NEWDB */
19156845Seric 	rhs.dbm = dbm_fetch(AliasDBMptr, lhs.dbm);
19256845Seric 	return (rhs.dbm.dptr);
19356845Seric # endif /* NEWDB */
19456845Seric # else /* neither NEWDB nor NDBM */
1955701Seric 	register STAB *s;
1965701Seric 
1975701Seric 	s = stab(name, ST_ALIAS, ST_FIND);
19851756Seric 	if (s != NULL)
19951756Seric 		return (s->s_alias);
20051756Seric # endif
20151756Seric 	return (NULL);
2025701Seric }
2035701Seric /*
2044098Seric **  INITALIASES -- initialize for aliasing
2054098Seric **
20656845Seric **	Very different depending on whether we are running NDBM or not.
2074098Seric **
2084098Seric **	Parameters:
2094098Seric **		aliasfile -- location of aliases.
21056845Seric **		init -- if set and if NDBM, initialize the NDBM files.
2114098Seric **
2124098Seric **	Returns:
2134098Seric **		none.
2144098Seric **
2154098Seric **	Side Effects:
2164098Seric **		initializes aliases:
21756845Seric **		if NDBM:  opens the database.
21856845Seric **		if ~NDBM: reads the aliases into the symbol table.
2194098Seric */
2204098Seric 
22140559Sbostic # define DBMMODE	0644
2224157Seric 
22355012Seric initaliases(aliasfile, init, e)
2244098Seric 	char *aliasfile;
2254157Seric 	bool init;
22655012Seric 	register ENVELOPE *e;
2274098Seric {
22856845Seric #if defined(NDBM) || defined(NEWDB)
2298437Seric 	int atcnt;
23025522Seric 	time_t modtime;
23125522Seric 	bool automatic = FALSE;
2324322Seric 	char buf[MAXNAME];
23350575Seric #endif
2349368Seric 	struct stat stb;
23527176Seric 	static bool initialized = FALSE;
23646928Sbostic 	static int readaliases();
2374322Seric 
23827176Seric 	if (initialized)
23927176Seric 		return;
24027176Seric 	initialized = TRUE;
24127176Seric 
24217984Seric 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
2438437Seric 	{
24425522Seric 		if (aliasfile != NULL && init)
24525522Seric 			syserr("Cannot open %s", aliasfile);
2468437Seric 		NoAlias = TRUE;
24711937Seric 		errno = 0;
2488437Seric 		return;
2498437Seric 	}
2508437Seric 
25156845Seric # if defined(NDBM) || defined(NEWDB)
2524322Seric 	/*
2538437Seric 	**  Check to see that the alias file is complete.
2548437Seric 	**	If not, we will assume that someone died, and it is up
2558437Seric 	**	to us to rebuild it.
2568437Seric 	*/
2578437Seric 
25825689Seric 	if (!init)
25950575Seric 	{
26050575Seric # ifdef NEWDB
26150575Seric 		(void) strcpy(buf, aliasfile);
26250575Seric 		(void) strcat(buf, ".db");
26351171Sbostic 		AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
26450576Seric 		if (AliasDBptr == NULL)
26550576Seric 		{
26656845Seric # ifdef NDBM
26756845Seric 			AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
268*57249Seric 			if (AliasDBMptr == NULL)
269*57249Seric 			{
270*57249Seric 				syserr("initaliases: cannot open %s", buf);
271*57249Seric 				NoAlias = TRUE;
272*57249Seric 				return;
273*57249Seric 			}
27451756Seric # else
27550576Seric 			syserr("initaliases: cannot open %s", buf);
27650576Seric 			NoAlias = TRUE;
27750576Seric 			return;
27851756Seric # endif
27950576Seric 		}
28050575Seric # else
28156845Seric 		AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
282*57249Seric 		if (AliasDBMptr == NULL)
283*57249Seric 		{
284*57249Seric 			syserr("initaliases: cannot open %s", buf);
285*57249Seric 			NoAlias = TRUE;
286*57249Seric 			return;
287*57249Seric 		}
28850575Seric # endif
28950575Seric 	}
29017471Seric 	atcnt = SafeAlias * 2;
29117471Seric 	if (atcnt > 0)
29217471Seric 	{
29317471Seric 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
29425689Seric 		{
29525689Seric 			/*
29625689Seric 			**  Reinitialize alias file in case the new
29725689Seric 			**  one is mv'ed in instead of cp'ed in.
29825689Seric 			**
29925689Seric 			**	Only works with new DBM -- old one will
30025689Seric 			**	just consume file descriptors forever.
30125689Seric 			**	If you have a dbmclose() it can be
30225689Seric 			**	added before the sleep(30).
30325689Seric 			*/
30425689Seric 
30550575Seric # ifdef NEWDB
30651756Seric 			if (AliasDBptr != NULL)
30751756Seric 				AliasDBptr->close(AliasDBptr);
30850575Seric # endif
30956845Seric # ifdef NDBM
31056845Seric 			if (AliasDBMptr != NULL)
31156845Seric 				dbm_close(AliasDBMptr);
31256845Seric # endif
31350575Seric 
31417471Seric 			sleep(30);
31550575Seric # ifdef NEWDB
31650575Seric 			(void) strcpy(buf, aliasfile);
31750575Seric 			(void) strcat(buf, ".db");
31851171Sbostic 			AliasDBptr =
31951171Sbostic 			    dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
32050576Seric 			if (AliasDBptr == NULL)
32150576Seric 			{
32251756Seric # ifdef NDBM
32356845Seric 				AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
32451756Seric # else
32550576Seric 				syserr("initaliases: cannot open %s", buf);
32650576Seric 				NoAlias = TRUE;
32750576Seric 				return;
32851756Seric # endif
32950576Seric 			}
33050575Seric # else
33125689Seric # ifdef NDBM
33256845Seric 			AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
333*57249Seric 			if (AliasDBMptr == NULL)
334*57249Seric 			{
335*57249Seric 				syserr("initaliases: cannot open %s", buf);
336*57249Seric 				NoAlias = TRUE;
337*57249Seric 				return;
338*57249Seric 			}
33950575Seric # endif
34050575Seric # endif
34125689Seric 		}
34217471Seric 	}
34317471Seric 	else
34417471Seric 		atcnt = 1;
3458437Seric 
3468437Seric 	/*
34756845Seric 	**  See if the NDBM version of the file is out of date with
3484322Seric 	**  the text version.  If so, go into 'init' mode automatically.
34940559Sbostic 	**	This only happens if our effective userid owns the DBM.
35040559Sbostic 	**	Note the unpalatable hack to see if the stat succeeded.
3514322Seric 	*/
3524322Seric 
3534322Seric 	modtime = stb.st_mtime;
3544322Seric 	(void) strcpy(buf, aliasfile);
35550575Seric # ifdef NEWDB
35650575Seric 	(void) strcat(buf, ".db");
35750575Seric # else
3584322Seric 	(void) strcat(buf, ".pag");
35950575Seric # endif
3604322Seric 	stb.st_ino = 0;
36119039Seric 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
3624322Seric 	{
36311937Seric 		errno = 0;
36440559Sbostic 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
3654322Seric 		{
3664322Seric 			init = TRUE;
36725522Seric 			automatic = TRUE;
3687051Seric 			message(Arpa_Info, "rebuilding alias database");
36924944Seric #ifdef LOG
37024944Seric 			if (LogLevel >= 7)
37124944Seric 				syslog(LOG_INFO, "rebuilding alias database");
37256795Seric #endif /* LOG */
3734322Seric 		}
3744322Seric 		else
3754322Seric 		{
37619039Seric #ifdef LOG
37724944Seric 			if (LogLevel >= 7)
37824944Seric 				syslog(LOG_INFO, "alias database out of date");
37956795Seric #endif /* LOG */
3804322Seric 			message(Arpa_Info, "Warning: alias database out of date");
3814322Seric 		}
3824322Seric 	}
3834322Seric 
3844322Seric 
3854322Seric 	/*
38656845Seric 	**  If necessary, load the NDBM file.
38756845Seric 	**	If running without NDBM, load the symbol table.
3884322Seric 	*/
3894322Seric 
3904157Seric 	if (init)
3918437Seric 	{
39225522Seric #ifdef LOG
39325522Seric 		if (LogLevel >= 6)
39425522Seric 		{
39525522Seric 			extern char *username();
39625522Seric 
39725522Seric 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
39825522Seric 				automatic ? "auto" : "", username());
39925522Seric 		}
40056795Seric #endif /* LOG */
40155012Seric 		readaliases(aliasfile, TRUE, e);
4028437Seric 	}
40356845Seric # else /* NDBM */
40455012Seric 	readaliases(aliasfile, init, e);
40556845Seric # endif /* NDBM */
4064157Seric }
4074157Seric /*
4084157Seric **  READALIASES -- read and process the alias file.
4094157Seric **
4104157Seric **	This routine implements the part of initaliases that occurs
4114157Seric **	when we are not going to use the DBM stuff.
4124157Seric **
4134157Seric **	Parameters:
4144157Seric **		aliasfile -- the pathname of the alias file master.
41556845Seric **		init -- if set, initialize the NDBM stuff.
4164157Seric **
4174157Seric **	Returns:
4184157Seric **		none.
4194157Seric **
4204157Seric **	Side Effects:
4214157Seric **		Reads aliasfile into the symbol table.
4224157Seric **		Optionally, builds the .dir & .pag files.
4234157Seric */
4244157Seric 
4254157Seric static
42655012Seric readaliases(aliasfile, init, e)
4274157Seric 	char *aliasfile;
4284157Seric 	bool init;
42955012Seric 	register ENVELOPE *e;
4304157Seric {
4314098Seric 	register char *p;
4324098Seric 	char *rhs;
4334098Seric 	bool skipping;
4349368Seric 	int naliases, bytes, longest;
4359368Seric 	FILE *af;
43653742Seric 	bool makedbmfiles;
43740970Sbostic 	void (*oldsigint)();
4384098Seric 	ADDRESS al, bl;
4394106Seric 	register STAB *s;
44050575Seric # ifdef NEWDB
44150575Seric 	DB *dbp;
44250575Seric # endif
44356845Seric # ifdef NDBM
44456845Seric 	DBM *dbmp;
44556845Seric # endif
44651937Seric # ifdef LOCKF
44751937Seric 	struct flock fld;
44851937Seric # endif
4499368Seric 	char line[BUFSIZ];
4504098Seric 
45151937Seric 	if ((af = fopen(aliasfile, "r+")) == NULL)
4521515Seric 	{
453*57249Seric 		if (init)
454*57249Seric 			syserr("Can't open %s", aliasfile);
455*57249Seric 		else if (tTd(27, 1))
4564106Seric 			printf("Can't open %s\n", aliasfile);
4574098Seric 		errno = 0;
4584098Seric 		NoAlias++;
4594098Seric 		return;
4604098Seric 	}
4614314Seric 
46256845Seric # if defined(NDBM) || defined(NEWDB)
46319784Seric 	/* see if someone else is rebuilding the alias file already */
46451835Seric # ifdef LOCKF
46551937Seric 	fld.l_type = F_WRLCK;
46651937Seric 	fld.l_whence = fld.l_start = fld.l_len = 0;
46751937Seric 	if (fcntl(fileno(af), F_SETLK, &fld) < 0)
46851835Seric # else
46919784Seric 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
47051835Seric # endif
47119784Seric 	{
47219784Seric 		/* yes, they are -- wait until done and then return */
47319784Seric 		message(Arpa_Info, "Alias file is already being rebuilt");
47419784Seric 		if (OpMode != MD_INITALIAS)
47519784Seric 		{
47619784Seric 			/* wait for other rebuild to complete */
47751835Seric # ifdef LOCKF
47851937Seric 			(void) fcntl(fileno(af), F_SETLKW, &fld);
47951835Seric # else
48019784Seric 			(void) flock(fileno(af), LOCK_EX);
48151835Seric # endif
48219784Seric 		}
48323108Seric 		(void) fclose(af);
48419784Seric 		errno = 0;
48519784Seric 		return;
48619784Seric 	}
48756845Seric # endif /* NDBM */
48819784Seric 
4894314Seric 	/*
49019784Seric 	**  If initializing, create the new DBM files.
49119784Seric 	*/
49219784Seric 
49319784Seric 	if (init)
49419784Seric 	{
49519784Seric 		oldsigint = signal(SIGINT, SIG_IGN);
49651756Seric # ifdef NEWDB
49751756Seric 		(void) strcpy(line, aliasfile);
49851756Seric 		(void) strcat(line, ".db");
49951756Seric 		dbp = dbopen(line,
50051756Seric 		    O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL);
50151756Seric 		if (dbp == NULL)
50251756Seric 		{
50351756Seric 			syserr("readaliases: cannot create %s", line);
50451756Seric 			(void) signal(SIGINT, oldsigint);
50551756Seric 			return;
50651756Seric 		}
50753742Seric # endif
50853742Seric # ifdef IF_MAKEDBMFILES
50953742Seric # ifdef NEWDB
51053742Seric 		makedbmfiles = access("/var/yp/Makefile", R_OK) == 0;
51153742Seric # endif
51253742Seric 		IF_MAKEDBMFILES
51319784Seric 		{
51456845Seric 			dbmp = dbm_open(aliasfile,
51556845Seric 					       O_RDWR|O_CREAT|O_TRUNC, DBMMODE);
51656845Seric 			if (dbmp == NULL)
51753742Seric 			{
51856845Seric 				syserr("readaliases: cannot create %s.{dir,pag}",
51956845Seric 					aliasfile);
52053742Seric 				(void) signal(SIGINT, oldsigint);
52153742Seric 				return;
52253742Seric 			}
52319784Seric 		}
52450575Seric # endif
52519784Seric 	}
52619784Seric 
52719784Seric 	/*
5284314Seric 	**  Read and interpret lines
5294314Seric 	*/
5304314Seric 
5319368Seric 	FileName = aliasfile;
5329368Seric 	LineNumber = 0;
5334322Seric 	naliases = bytes = longest = 0;
5344098Seric 	skipping = FALSE;
5354098Seric 	while (fgets(line, sizeof (line), af) != NULL)
5364098Seric 	{
5374322Seric 		int lhssize, rhssize;
5384322Seric 
5399368Seric 		LineNumber++;
54056795Seric 		p = strchr(line, '\n');
54125278Seric 		if (p != NULL)
54225278Seric 			*p = '\0';
5434098Seric 		switch (line[0])
5444098Seric 		{
5454098Seric 		  case '#':
5464098Seric 		  case '\0':
5474098Seric 			skipping = FALSE;
5484098Seric 			continue;
5494065Seric 
5504098Seric 		  case ' ':
5514098Seric 		  case '\t':
5524098Seric 			if (!skipping)
5539368Seric 				syserr("Non-continuation line starts with space");
5544098Seric 			skipping = TRUE;
5554097Seric 			continue;
5564098Seric 		}
5574098Seric 		skipping = FALSE;
5581874Seric 
5594314Seric 		/*
5604314Seric 		**  Process the LHS
5614314Seric 		**	Find the final colon, and parse the address.
56216898Seric 		**	It should resolve to a local name -- this will
56316898Seric 		**	be checked later (we want to optionally do
56416898Seric 		**	parsing of the RHS first to maximize error
56516898Seric 		**	detection).
5664314Seric 		*/
5674314Seric 
5684098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
5694097Seric 			continue;
57016898Seric 		if (*p++ != ':')
5714098Seric 		{
5729368Seric 			syserr("missing colon");
5734097Seric 			continue;
5744098Seric 		}
57555012Seric 		if (parseaddr(line, &al, 1, ':', e) == NULL)
5764098Seric 		{
57716898Seric 			syserr("illegal alias name");
57816898Seric 			continue;
5794098Seric 		}
58016898Seric 		loweraddr(&al);
5814314Seric 
5824314Seric 		/*
5834314Seric 		**  Process the RHS.
5844314Seric 		**	'al' is the internal form of the LHS address.
5854314Seric 		**	'p' points to the text of the RHS.
5864314Seric 		*/
5874314Seric 
5884098Seric 		rhs = p;
5894098Seric 		for (;;)
5904098Seric 		{
5914098Seric 			register char c;
5921515Seric 
59325821Seric 			if (init && CheckAliases)
5944098Seric 			{
5954157Seric 				/* do parsing & compression of addresses */
59625278Seric 				while (*p != '\0')
5974098Seric 				{
59825278Seric 					extern char *DelimChar;
59925278Seric 
60025278Seric 					while (isspace(*p) || *p == ',')
6014157Seric 						p++;
60225278Seric 					if (*p == '\0')
60325278Seric 						break;
60455012Seric 					if (parseaddr(p, &bl, -1, ',', e) == NULL)
60525278Seric 						usrerr("%s... bad address", p);
60625278Seric 					p = DelimChar;
6074098Seric 				}
6084098Seric 			}
6094157Seric 			else
61015769Seric 			{
61116898Seric 				p = &p[strlen(p)];
61216898Seric 				if (p[-1] == '\n')
61316898Seric 					*--p = '\0';
61415769Seric 			}
6151515Seric 
6164098Seric 			/* see if there should be a continuation line */
6174106Seric 			c = fgetc(af);
6184106Seric 			if (!feof(af))
6194314Seric 				(void) ungetc(c, af);
6204106Seric 			if (c != ' ' && c != '\t')
6214098Seric 				break;
6224098Seric 
6234098Seric 			/* read continuation line */
6244098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
6254098Seric 				break;
6269368Seric 			LineNumber++;
62757135Seric 
62857135Seric 			/* check for line overflow */
62957135Seric 			if (strchr(p, '\n') == NULL)
63057135Seric 			{
63157135Seric 				usrerr("alias too long");
63257135Seric 				break;
63357135Seric 			}
6344098Seric 		}
63516898Seric 		if (al.q_mailer != LocalMailer)
63616898Seric 		{
63716898Seric 			syserr("cannot alias non-local names");
63816898Seric 			continue;
63916898Seric 		}
6404314Seric 
6414314Seric 		/*
6424314Seric 		**  Insert alias into symbol table or DBM file
6434314Seric 		*/
6444314Seric 
64516898Seric 		lhssize = strlen(al.q_user) + 1;
6464322Seric 		rhssize = strlen(rhs) + 1;
6474322Seric 
64856845Seric # if defined(NDBM) || defined(NEWDB)
6494157Seric 		if (init)
6504157Seric 		{
65156845Seric 			DBdatum key, content;
6524157Seric 
65356845Seric 			key.xx.size = lhssize;
65456845Seric 			key.xx.data = al.q_user;
65556845Seric 			content.xx.size = rhssize;
65656845Seric 			content.xx.data = rhs;
65751756Seric # ifdef NEWDB
65856845Seric 			if (dbp->put(dbp, &key.dbt, &content.dbt, 0) != 0)
65950576Seric 				syserr("readaliases: db put (%s)", al.q_user);
66050575Seric # endif
66153742Seric # ifdef IF_MAKEDBMFILES
66253742Seric 			IF_MAKEDBMFILES
66356845Seric 				if (dbm_store(dbmp, key.dbm, content.dbm, DBM_REPLACE) != 0)
66456845Seric 					syserr("readaliases: dbm store (%s)",
66556845Seric 						al.q_user);
66653742Seric # endif
6674157Seric 		}
6684157Seric 		else
66956845Seric # endif /* NDBM */
6704157Seric 		{
6714157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
6724157Seric 			s->s_alias = newstr(rhs);
6734157Seric 		}
6744322Seric 
6754322Seric 		/* statistics */
6764322Seric 		naliases++;
6774322Seric 		bytes += lhssize + rhssize;
6784322Seric 		if (rhssize > longest)
6794322Seric 			longest = rhssize;
6801515Seric 	}
68119784Seric 
68256845Seric # if defined(NDBM) || defined(NEWDB)
68319784Seric 	if (init)
68419784Seric 	{
68519784Seric 		/* add the distinquished alias "@" */
68656845Seric 		DBdatum key;
68719784Seric 
68856845Seric 		key.xx.size = 2;
68956845Seric 		key.xx.data = "@";
69050575Seric # ifdef NEWDB
69150576Seric 		if (dbp->sync(dbp) != 0 ||
69256845Seric 		    dbp->put(dbp, &key.dbt, &key.dbt, 0) != 0 ||
69350576Seric 		    dbp->close(dbp) != 0)
69450576Seric 			syserr("readaliases: db close failure");
69553742Seric # endif
69656789Seric # ifdef IF_MAKEDBMFILES
69756793Seric 		IF_MAKEDBMFILES
69856845Seric 		{
69956845Seric 			if (dbm_store(dbmp, key.dbm, key.dbm, DBM_REPLACE) != 0 ||
70056845Seric 			    dbm_error(dbmp))
70156845Seric 				syserr("readaliases: dbm close failure");
70256845Seric 			dbm_close(dbmp);
70356845Seric 		}
70450575Seric # endif
70519784Seric 
70619784Seric 		/* restore the old signal */
70719784Seric 		(void) signal(SIGINT, oldsigint);
70819784Seric 	}
70956845Seric # endif /* NDBM */
71019784Seric 
71119784Seric 	/* closing the alias file drops the lock */
7124098Seric 	(void) fclose(af);
71355012Seric 	e->e_to = NULL;
7149368Seric 	FileName = NULL;
7157051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
7164322Seric 			naliases, longest, bytes);
71724944Seric # ifdef LOG
71824944Seric 	if (LogLevel >= 8)
71924944Seric 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
72024944Seric 			naliases, longest, bytes);
72156795Seric # endif /* LOG */
722292Seric }
723292Seric /*
724292Seric **  FORWARD -- Try to forward mail
725292Seric **
726292Seric **	This is similar but not identical to aliasing.
727292Seric **
728292Seric **	Parameters:
7294314Seric **		user -- the name of the user who's mail we would like
7304314Seric **			to forward to.  It must have been verified --
7314314Seric **			i.e., the q_home field must have been filled
7324314Seric **			in.
7334999Seric **		sendq -- a pointer to the head of the send queue to
7344999Seric **			put this user's aliases in.
735292Seric **
736292Seric **	Returns:
7374098Seric **		none.
738292Seric **
739292Seric **	Side Effects:
7403185Seric **		New names are added to send queues.
741292Seric */
742292Seric 
74355012Seric forward(user, sendq, e)
7442966Seric 	ADDRESS *user;
7454999Seric 	ADDRESS **sendq;
74655012Seric 	register ENVELOPE *e;
747292Seric {
74857136Seric 	char *pp;
74957136Seric 	char *ep;
7504536Seric 	extern bool safefile();
7514069Seric 
7527671Seric 	if (tTd(27, 1))
7534098Seric 		printf("forward(%s)\n", user->q_paddr);
7544098Seric 
7554594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
7564098Seric 		return;
7574314Seric 	if (user->q_home == NULL)
7584314Seric 		syserr("forward: no home");
7594069Seric 
7604069Seric 	/* good address -- look for .forward file in home */
76155012Seric 	define('z', user->q_home, e);
76257136Seric 	define('u', user->q_user, e);
76357136Seric 	define('h', user->q_host, e);
76457136Seric 	if (ForwardPath == NULL)
76557136Seric 		ForwardPath = newstr("\001z/.forward");
76657136Seric 
76757136Seric 	for (pp = ForwardPath; pp != NULL; pp = ep)
76857136Seric 	{
76957232Seric 		char buf[MAXPATHLEN+1];
77057136Seric 
77157136Seric 		ep = strchr(pp, ':');
77257136Seric 		if (ep != NULL)
77357136Seric 			*ep = '\0';
77457136Seric 		expand(pp, buf, &buf[sizeof buf - 1], e);
77557136Seric 		if (ep != NULL)
77657136Seric 			*ep++ = ':';
77757136Seric 		if (tTd(27, 3))
77857136Seric 			printf("forward: trying %s\n", buf);
77957136Seric 		if (include(buf, TRUE, user, sendq, e) == 0)
78057136Seric 			break;
78157136Seric 	}
782292Seric }
783