xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 63902)
122694Sdist /*
235073Sbostic  * Copyright (c) 1983 Eric P. Allman
362522Sbostic  * Copyright (c) 1988, 1993
462522Sbostic  *	The Regents of the University of California.  All rights reserved.
533728Sbostic  *
642824Sbostic  * %sccs.include.redist.c%
733728Sbostic  */
822694Sdist 
958332Seric # include "sendmail.h"
1050577Seric # include <signal.h>
1150577Seric # include <pwd.h>
1256766Seric 
1333728Sbostic #ifndef lint
14*63902Seric static char sccsid[] = "@(#)alias.c	8.5 (Berkeley) 07/19/93";
1533728Sbostic #endif /* not lint */
1659673Seric 
1759673Seric 
1860537Seric MAP	*AliasDB[MAXALIASDB + 1];	/* actual database list */
1959673Seric int	NAliasDBs;			/* number of alias databases */
2059673Seric /*
21292Seric **  ALIAS -- Compute aliases.
22292Seric **
239368Seric **	Scans the alias file for an alias for the given address.
249368Seric **	If found, it arranges to deliver to the alias list instead.
259368Seric **	Uses libdbm database if -DDBM.
26292Seric **
27292Seric **	Parameters:
284097Seric **		a -- address to alias.
294999Seric **		sendq -- a pointer to the head of the send queue
304999Seric **			to put the aliases in.
3158092Seric **		e -- the current envelope.
32292Seric **
33292Seric **	Returns:
34292Seric **		none
35292Seric **
36292Seric **	Side Effects:
373185Seric **		Aliases found are expanded.
38292Seric **
39292Seric **	Deficiencies:
40292Seric **		It should complain about names that are aliased to
41292Seric **			nothing.
42292Seric */
43292Seric 
4455012Seric alias(a, sendq, e)
454097Seric 	register ADDRESS *a;
464999Seric 	ADDRESS **sendq;
4755012Seric 	register ENVELOPE *e;
48292Seric {
494081Seric 	register char *p;
5058082Seric 	int naliases;
5158170Seric 	char *owner;
5258170Seric 	char obuf[MAXNAME + 6];
535701Seric 	extern char *aliaslookup();
54292Seric 
557671Seric 	if (tTd(27, 1))
564098Seric 		printf("alias(%s)\n", a->q_paddr);
57292Seric 
584098Seric 	/* don't realias already aliased names */
5958680Seric 	if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
604098Seric 		return;
614098Seric 
6259673Seric 	if (NoAlias)
6359673Seric 		return;
6459673Seric 
6555012Seric 	e->e_to = a->q_paddr;
664098Seric 
674314Seric 	/*
684314Seric 	**  Look up this name
694314Seric 	*/
704314Seric 
7159673Seric 	p = aliaslookup(a->q_user, e);
724098Seric 	if (p == NULL)
734098Seric 		return;
74292Seric 
75292Seric 	/*
764098Seric 	**  Match on Alias.
774098Seric 	**	Deliver to the target list.
781515Seric 	*/
791515Seric 
807671Seric 	if (tTd(27, 1))
814098Seric 		printf("%s (%s, %s) aliased to %s\n",
824098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
8358092Seric 	if (bitset(EF_VRFYONLY, e->e_flags))
8458154Seric 	{
8558154Seric 		a->q_flags |= QVERIFIED;
8658884Seric 		e->e_nrcpts++;
8758092Seric 		return;
8858154Seric 	}
8958154Seric 	message("aliased to %s", p);
9057977Seric #ifdef LOG
9158020Seric 	if (LogLevel > 9)
9257977Seric 		syslog(LOG_INFO, "%s: alias %s => %s", e->e_id, a->q_paddr, p);
9357977Seric #endif
9458082Seric 	a->q_flags &= ~QSELFREF;
954098Seric 	AliasLevel++;
9658082Seric 	naliases = sendtolist(p, a, sendq, e);
974098Seric 	AliasLevel--;
9858082Seric 	if (naliases > 0 && !bitset(QSELFREF, a->q_flags))
9958065Seric 	{
10058065Seric 		if (tTd(27, 5))
10158065Seric 		{
10258065Seric 			printf("alias: QDONTSEND ");
10358065Seric 			printaddr(a, FALSE);
10458065Seric 		}
10558065Seric 		a->q_flags |= QDONTSEND;
10658065Seric 	}
10758170Seric 
10858170Seric 	/*
10958170Seric 	**  Look for owner of alias
11058170Seric 	*/
11158170Seric 
11258170Seric 	(void) strcpy(obuf, "owner-");
11358170Seric 	if (strncmp(a->q_user, "owner-", 6) == 0)
11458170Seric 		(void) strcat(obuf, "owner");
11558170Seric 	else
11658170Seric 		(void) strcat(obuf, a->q_user);
11758170Seric 	if (!bitnset(M_USR_UPPER, a->q_mailer->m_flags))
11858170Seric 		makelower(obuf);
11959673Seric 	owner = aliaslookup(obuf, e);
12058170Seric 	if (owner != NULL)
12158170Seric 	{
12258170Seric 		if (strchr(owner, ',') != NULL)
12358170Seric 			owner = obuf;
12458170Seric 		a->q_owner = newstr(owner);
12558170Seric 	}
1264098Seric }
1274098Seric /*
1285701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1295701Seric **
1305701Seric **	Parameters:
1315701Seric **		name -- the name to look up.
1325701Seric **
1335701Seric **	Returns:
1345701Seric **		the value of name.
1355701Seric **		NULL if unknown.
1365701Seric **
1375701Seric **	Side Effects:
1385701Seric **		none.
1395701Seric **
1405701Seric **	Warnings:
1415701Seric **		The return value will be trashed across calls.
1425701Seric */
1435701Seric 
1445701Seric char *
14559673Seric aliaslookup(name, e)
1465701Seric 	char *name;
14759673Seric 	ENVELOPE *e;
1485701Seric {
14959673Seric 	register int dbno;
15060089Seric 	register MAP *map;
15159673Seric 	register char *p;
1525701Seric 
15359673Seric 	for (dbno = 0; dbno < NAliasDBs; dbno++)
15459673Seric 	{
15560089Seric 		auto int stat;
15660089Seric 
15760537Seric 		map = AliasDB[dbno];
15860207Seric 		if (!bitset(MF_OPEN, map->map_mflags))
15959673Seric 			continue;
16060207Seric 		p = (*map->map_class->map_lookup)(map, name, NULL, &stat);
16159673Seric 		if (p != NULL)
16259673Seric 			return p;
16359673Seric 	}
16459673Seric 	return NULL;
16559673Seric }
16659673Seric /*
16759673Seric **  SETALIAS -- set up an alias map
16859673Seric **
16959673Seric **	Called when reading configuration file.
17059673Seric **
17159673Seric **	Parameters:
17259673Seric **		spec -- the alias specification
17359673Seric **
17459673Seric **	Returns:
17559673Seric **		none.
17659673Seric */
17757381Seric 
17859673Seric setalias(spec)
17959673Seric 	char *spec;
18059673Seric {
18159673Seric 	register char *p;
18260089Seric 	register MAP *map;
18359673Seric 	char *class;
18459673Seric 	STAB *s;
18559673Seric 
18659697Seric 	if (tTd(27, 8))
18759697Seric 		printf("setalias(%s)\n", spec);
18859697Seric 
18959758Seric 	for (p = spec; p != NULL; )
19051756Seric 	{
19160537Seric 		char aname[50];
19260537Seric 
19359758Seric 		while (isspace(*p))
19459758Seric 			p++;
19560502Seric 		if (*p == '\0')
19659673Seric 			break;
19759673Seric 		spec = p;
19859673Seric 
19959758Seric 		if (NAliasDBs >= MAXALIASDB)
20059758Seric 		{
20159758Seric 			syserr("Too many alias databases defined, %d max", MAXALIASDB);
20259758Seric 			return;
20359758Seric 		}
20460537Seric 		(void) sprintf(aname, "Alias%d", NAliasDBs);
20560537Seric 		s = stab(aname, ST_MAP, ST_ENTER);
20660537Seric 		map = &s->s_map;
20760537Seric 		AliasDB[NAliasDBs] = map;
20860089Seric 		bzero(map, sizeof *map);
20959758Seric 
21059758Seric 		p = strpbrk(p, " ,/:");
21159758Seric 		if (p != NULL && *p == ':')
21259758Seric 		{
21360089Seric 			/* map name */
21459758Seric 			*p++ = '\0';
21559758Seric 			class = spec;
21659758Seric 			spec = p;
21759758Seric 		}
21859758Seric 		else
21959758Seric 		{
22059758Seric 			class = "implicit";
22160228Seric 			map->map_mflags = MF_OPTIONAL|MF_INCLNULL;
22259758Seric 		}
22359758Seric 
22459758Seric 		/* find end of spec */
22559758Seric 		if (p != NULL)
22659758Seric 			p = strchr(p, ',');
22759758Seric 		if (p != NULL)
22859758Seric 			*p++ = '\0';
22959758Seric 
23059758Seric 		/* look up class */
23160089Seric 		s = stab(class, ST_MAPCLASS, ST_FIND);
23259758Seric 		if (s == NULL)
23359758Seric 		{
23459758Seric 			if (tTd(27, 1))
23559758Seric 				printf("Unknown alias class %s\n", class);
23659758Seric 		}
23760207Seric 		else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
23860207Seric 		{
23960207Seric 			syserr("setalias: map class %s can't handle aliases",
24060207Seric 				class);
24160207Seric 		}
24259758Seric 		else
24359758Seric 		{
24460207Seric 			map->map_class = &s->s_mapclass;
24560089Seric 			if (map->map_class->map_parse(map, spec))
24660089Seric 			{
24760207Seric 				map->map_mflags |= MF_VALID|MF_ALIAS;
24860089Seric 				NAliasDBs++;
24960089Seric 			}
25059758Seric 		}
25159756Seric 	}
2525701Seric }
2535701Seric /*
25459673Seric **  ALIASWAIT -- wait for distinguished @:@ token to appear.
25559673Seric **
25659673Seric **	This can decide to reopen or rebuild the alias file
25759673Seric */
25859673Seric 
25960207Seric aliaswait(map, ext)
26060089Seric 	MAP *map;
26160207Seric 	char *ext;
26259673Seric {
26359673Seric 	int atcnt;
26459673Seric 	time_t mtime;
26559673Seric 	struct stat stb;
26659673Seric 	char buf[MAXNAME];
26759673Seric 
26859697Seric 	if (tTd(27, 3))
26960207Seric 		printf("aliaswait(%s:%s)\n",
27060207Seric 			map->map_class->map_cname, map->map_file);
27159697Seric 
27217471Seric 	atcnt = SafeAlias * 2;
27317471Seric 	if (atcnt > 0)
27417471Seric 	{
27560089Seric 		auto int st;
27660089Seric 
27759673Seric 		while (atcnt-- >= 0 &&
27860089Seric 		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
27925689Seric 		{
28025689Seric 			/*
28159673Seric 			**  Close and re-open the alias database in case
28259673Seric 			**  the one is mv'ed instead of cp'ed in.
28325689Seric 			*/
28425689Seric 
28559697Seric 			if (tTd(27, 2))
28659697Seric 				printf("aliaswait: sleeping\n");
28759697Seric 
28860089Seric 			map->map_class->map_close(map);
28917471Seric 			sleep(30);
29060089Seric 			map->map_class->map_open(map, O_RDONLY);
29125689Seric 		}
29217471Seric 	}
2938437Seric 
29459673Seric 	/* see if we need to go into auto-rebuild mode */
29560207Seric 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
29660207Seric 	{
29760207Seric 		if (tTd(27, 3))
29860207Seric 			printf("aliaswait: not rebuildable\n");
29959673Seric 		return;
30060207Seric 	}
30160207Seric 	if (stat(map->map_file, &stb) < 0)
30260207Seric 	{
30360207Seric 		if (tTd(27, 3))
30460207Seric 			printf("aliaswait: no source file\n");
30560207Seric 		return;
30660207Seric 	}
30759673Seric 	mtime = stb.st_mtime;
30860089Seric 	(void) strcpy(buf, map->map_file);
30960207Seric 	if (ext != NULL)
31060207Seric 		(void) strcat(buf, ext);
31159673Seric 	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || atcnt < 0)
3124322Seric 	{
31359673Seric 		/* database is out of date */
31440559Sbostic 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
3154322Seric 		{
31660089Seric 			message("auto-rebuilding alias database %s", buf);
31760207Seric 			rebuildaliases(map, TRUE);
3184322Seric 		}
3194322Seric 		else
3204322Seric 		{
32119039Seric #ifdef LOG
32258020Seric 			if (LogLevel > 3)
32359673Seric 				syslog(LOG_INFO, "alias database %s out of date",
32460089Seric 					buf);
32556795Seric #endif /* LOG */
32660089Seric 			message("Warning: alias database %s out of date", buf);
3274322Seric 		}
3284322Seric 	}
32959673Seric }
33059673Seric /*
33159673Seric **  REBUILDALIASES -- rebuild the alias database.
33259673Seric **
33359673Seric **	Parameters:
33460089Seric **		map -- the database to rebuild.
33559673Seric **		automatic -- set if this was automatically generated.
33659673Seric **
33759673Seric **	Returns:
33859673Seric **		none.
33959673Seric **
34059673Seric **	Side Effects:
34159673Seric **		Reads the text version of the database, builds the
34259673Seric **		DBM or DB version.
34359673Seric */
3444322Seric 
34560207Seric rebuildaliases(map, automatic)
34660089Seric 	register MAP *map;
34759673Seric 	bool automatic;
34859673Seric {
34959673Seric 	FILE *af;
35059673Seric 	void (*oldsigint)();
3514322Seric 
35260207Seric 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
35359673Seric 		return;
3544322Seric 
35559673Seric #ifdef LOG
35659673Seric 	if (LogLevel > 7)
3578437Seric 	{
35859673Seric 		syslog(LOG_NOTICE, "alias database %s %srebuilt by %s",
35960089Seric 			map->map_file, automatic ? "auto" : "", username());
36059673Seric 	}
36156795Seric #endif /* LOG */
36259673Seric 
36359673Seric 	/* try to lock the source file */
36460089Seric 	if ((af = fopen(map->map_file, "r+")) == NULL)
36559673Seric 	{
36659756Seric 		if (tTd(27, 1))
36759756Seric 			printf("Can't open %s: %s\n",
36860089Seric 				map->map_file, errstring(errno));
36959673Seric 		errno = 0;
37059673Seric 		return;
3718437Seric 	}
37259673Seric 
37359673Seric 	/* see if someone else is rebuilding the alias file */
37460089Seric 	if (!lockfile(fileno(af), map->map_file, LOCK_EX|LOCK_NB))
37559673Seric 	{
37659673Seric 		/* yes, they are -- wait until done */
37759673Seric 		message("Alias file %s is already being rebuilt",
37860089Seric 			map->map_file);
37959673Seric 		if (OpMode != MD_INITALIAS)
38059673Seric 		{
38159673Seric 			/* wait for other rebuild to complete */
38260089Seric 			(void) lockfile(fileno(af), map->map_file,
38359673Seric 					LOCK_EX);
38459673Seric 		}
38559673Seric 		(void) fclose(af);
38659673Seric 		errno = 0;
38759673Seric 		return;
38859673Seric 	}
38959673Seric 
39059673Seric 	oldsigint = signal(SIGINT, SIG_IGN);
39159673Seric 
39260207Seric 	if (map->map_class->map_open(map, O_RDWR))
39360089Seric 	{
39460207Seric 		map->map_mflags |= MF_OPEN|MF_WRITABLE;
39560207Seric 		readaliases(map, af, automatic);
39660089Seric 	}
39760207Seric 	else
39860207Seric 	{
39960207Seric 		if (tTd(27, 1))
40060207Seric 			printf("Can't create database for %s: %s\n",
40160207Seric 				map->map_file, errstring(errno));
40260207Seric 		if (!automatic)
40360207Seric 			syserr("Cannot create database for alias file %s",
40460207Seric 				map->map_file);
40560207Seric 	}
40659673Seric 
40759673Seric 	/* close the file, thus releasing locks */
40859673Seric 	fclose(af);
40959673Seric 
41059673Seric 	/* add distinguished entries and close the database */
41160207Seric 	if (bitset(MF_OPEN, map->map_mflags))
41260089Seric 		map->map_class->map_close(map);
41359673Seric 
41459673Seric 	/* restore the old signal */
41559673Seric 	(void) signal(SIGINT, oldsigint);
4164157Seric }
4174157Seric /*
4184157Seric **  READALIASES -- read and process the alias file.
4194157Seric **
4204157Seric **	This routine implements the part of initaliases that occurs
4214157Seric **	when we are not going to use the DBM stuff.
4224157Seric **
4234157Seric **	Parameters:
42460089Seric **		map -- the alias database descriptor.
42559673Seric **		af -- file to read the aliases from.
42659733Seric **		automatic -- set if this was an automatic rebuild.
4274157Seric **
4284157Seric **	Returns:
4294157Seric **		none.
4304157Seric **
4314157Seric **	Side Effects:
4324157Seric **		Reads aliasfile into the symbol table.
4334157Seric **		Optionally, builds the .dir & .pag files.
4344157Seric */
4354157Seric 
43660207Seric readaliases(map, af, automatic)
43760089Seric 	register MAP *map;
43859673Seric 	FILE *af;
43959733Seric 	int automatic;
4404157Seric {
4414098Seric 	register char *p;
4424098Seric 	char *rhs;
4434098Seric 	bool skipping;
44459673Seric 	long naliases, bytes, longest;
4454098Seric 	ADDRESS al, bl;
4469368Seric 	char line[BUFSIZ];
4474098Seric 
4484314Seric 	/*
4494314Seric 	**  Read and interpret lines
4504314Seric 	*/
4514314Seric 
45260089Seric 	FileName = map->map_file;
4539368Seric 	LineNumber = 0;
4544322Seric 	naliases = bytes = longest = 0;
4554098Seric 	skipping = FALSE;
4564098Seric 	while (fgets(line, sizeof (line), af) != NULL)
4574098Seric 	{
4584322Seric 		int lhssize, rhssize;
4594322Seric 
4609368Seric 		LineNumber++;
46156795Seric 		p = strchr(line, '\n');
46225278Seric 		if (p != NULL)
46325278Seric 			*p = '\0';
4644098Seric 		switch (line[0])
4654098Seric 		{
4664098Seric 		  case '#':
4674098Seric 		  case '\0':
4684098Seric 			skipping = FALSE;
4694098Seric 			continue;
4704065Seric 
4714098Seric 		  case ' ':
4724098Seric 		  case '\t':
4734098Seric 			if (!skipping)
47458151Seric 				syserr("554 Non-continuation line starts with space");
4754098Seric 			skipping = TRUE;
4764097Seric 			continue;
4774098Seric 		}
4784098Seric 		skipping = FALSE;
4791874Seric 
4804314Seric 		/*
4814314Seric 		**  Process the LHS
48257736Seric 		**	Find the colon separator, and parse the address.
48316898Seric 		**	It should resolve to a local name -- this will
48416898Seric 		**	be checked later (we want to optionally do
48516898Seric 		**	parsing of the RHS first to maximize error
48616898Seric 		**	detection).
4874314Seric 		*/
4884314Seric 
4894098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
4904097Seric 			continue;
49116898Seric 		if (*p++ != ':')
4924098Seric 		{
49358151Seric 			syserr("554 missing colon");
4944097Seric 			continue;
4954098Seric 		}
49660207Seric 		if (parseaddr(line, &al, 1, ':', NULL, CurEnv) == NULL)
4974098Seric 		{
49858151Seric 			syserr("554 illegal alias name");
49916898Seric 			continue;
5004098Seric 		}
5014314Seric 
5024314Seric 		/*
5034314Seric 		**  Process the RHS.
5044314Seric 		**	'al' is the internal form of the LHS address.
5054314Seric 		**	'p' points to the text of the RHS.
5064314Seric 		*/
5074314Seric 
50858914Seric 		while (isascii(*p) && isspace(*p))
50958914Seric 			p++;
5104098Seric 		rhs = p;
5114098Seric 		for (;;)
5124098Seric 		{
5134098Seric 			register char c;
51458662Seric 			register char *nlp;
5151515Seric 
51658662Seric 			nlp = &p[strlen(p)];
51758662Seric 			if (nlp[-1] == '\n')
51858662Seric 				*--nlp = '\0';
51958662Seric 
52059673Seric 			if (CheckAliases)
5214098Seric 			{
5224157Seric 				/* do parsing & compression of addresses */
52325278Seric 				while (*p != '\0')
5244098Seric 				{
52558333Seric 					auto char *delimptr;
52625278Seric 
52758050Seric 					while ((isascii(*p) && isspace(*p)) ||
52858050Seric 								*p == ',')
5294157Seric 						p++;
53025278Seric 					if (*p == '\0')
53125278Seric 						break;
53260207Seric 					if (parseaddr(p, &bl, -1, ',', &delimptr, CurEnv) == NULL)
53358151Seric 						usrerr("553 %s... bad address", p);
53458333Seric 					p = delimptr;
5354098Seric 				}
5364098Seric 			}
5374157Seric 			else
53815769Seric 			{
53958662Seric 				p = nlp;
54015769Seric 			}
5411515Seric 
5424098Seric 			/* see if there should be a continuation line */
5434106Seric 			c = fgetc(af);
5444106Seric 			if (!feof(af))
5454314Seric 				(void) ungetc(c, af);
5464106Seric 			if (c != ' ' && c != '\t')
5474098Seric 				break;
5484098Seric 
5494098Seric 			/* read continuation line */
5504098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
5514098Seric 				break;
5529368Seric 			LineNumber++;
55357135Seric 
55457135Seric 			/* check for line overflow */
55557135Seric 			if (strchr(p, '\n') == NULL)
55657135Seric 			{
55758151Seric 				usrerr("554 alias too long");
55857135Seric 				break;
55957135Seric 			}
5604098Seric 		}
56116898Seric 		if (al.q_mailer != LocalMailer)
56216898Seric 		{
56358151Seric 			syserr("554 cannot alias non-local names");
56416898Seric 			continue;
56516898Seric 		}
5664314Seric 
5674314Seric 		/*
5684314Seric 		**  Insert alias into symbol table or DBM file
5694314Seric 		*/
5704314Seric 
57157381Seric 		if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags))
57257381Seric 			makelower(al.q_user);
5734322Seric 
57459673Seric 		lhssize = strlen(al.q_user);
57559673Seric 		rhssize = strlen(rhs);
57660089Seric 		map->map_class->map_store(map, al.q_user, rhs);
5774157Seric 
57859673Seric 		if (al.q_paddr != NULL)
57959673Seric 			free(al.q_paddr);
58059673Seric 		if (al.q_host != NULL)
58159673Seric 			free(al.q_host);
58259673Seric 		if (al.q_user != NULL)
58359673Seric 			free(al.q_user);
5844322Seric 
5854322Seric 		/* statistics */
5864322Seric 		naliases++;
5874322Seric 		bytes += lhssize + rhssize;
5884322Seric 		if (rhssize > longest)
5894322Seric 			longest = rhssize;
5901515Seric 	}
59119784Seric 
59260207Seric 	CurEnv->e_to = NULL;
59359673Seric 	FileName = NULL;
59459733Seric 	if (Verbose || !automatic)
59559733Seric 		message("%s: %d aliases, longest %d bytes, %d bytes total",
59660089Seric 			map->map_file, naliases, longest, bytes);
59759673Seric # ifdef LOG
59859673Seric 	if (LogLevel > 7)
59959673Seric 		syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total",
60060089Seric 			map->map_file, naliases, longest, bytes);
60159673Seric # endif /* LOG */
60259673Seric }
60359673Seric /*
604292Seric **  FORWARD -- Try to forward mail
605292Seric **
606292Seric **	This is similar but not identical to aliasing.
607292Seric **
608292Seric **	Parameters:
6094314Seric **		user -- the name of the user who's mail we would like
6104314Seric **			to forward to.  It must have been verified --
6114314Seric **			i.e., the q_home field must have been filled
6124314Seric **			in.
6134999Seric **		sendq -- a pointer to the head of the send queue to
6144999Seric **			put this user's aliases in.
615292Seric **
616292Seric **	Returns:
6174098Seric **		none.
618292Seric **
619292Seric **	Side Effects:
6203185Seric **		New names are added to send queues.
621292Seric */
622292Seric 
62355012Seric forward(user, sendq, e)
6242966Seric 	ADDRESS *user;
6254999Seric 	ADDRESS **sendq;
62655012Seric 	register ENVELOPE *e;
627292Seric {
62857136Seric 	char *pp;
62957136Seric 	char *ep;
630*63902Seric #ifdef HASSETREUID
63163753Seric 	register ADDRESS *ca;
63263753Seric 	uid_t saveduid, uid;
63363753Seric #endif
6344069Seric 
6357671Seric 	if (tTd(27, 1))
6364098Seric 		printf("forward(%s)\n", user->q_paddr);
6374098Seric 
6384594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
6394098Seric 		return;
6404314Seric 	if (user->q_home == NULL)
64158059Seric 	{
64258151Seric 		syserr("554 forward: no home");
64358059Seric 		user->q_home = "/nosuchdirectory";
64458059Seric 	}
6454069Seric 
6464069Seric 	/* good address -- look for .forward file in home */
64755012Seric 	define('z', user->q_home, e);
64857136Seric 	define('u', user->q_user, e);
64957136Seric 	define('h', user->q_host, e);
65057136Seric 	if (ForwardPath == NULL)
65158050Seric 		ForwardPath = newstr("\201z/.forward");
65257136Seric 
653*63902Seric #ifdef HASSETREUID
65463753Seric 	ca = getctladdr(user);
65563753Seric 	if (ca != NULL)
65663753Seric 		uid = ca->q_uid;
65763753Seric 	else
65863753Seric 		uid = DefUid;
65963753Seric #endif
66063753Seric 
66157136Seric 	for (pp = ForwardPath; pp != NULL; pp = ep)
66257136Seric 	{
66358247Seric 		int err;
66457232Seric 		char buf[MAXPATHLEN+1];
66557136Seric 
66657136Seric 		ep = strchr(pp, ':');
66757136Seric 		if (ep != NULL)
66857136Seric 			*ep = '\0';
66957136Seric 		expand(pp, buf, &buf[sizeof buf - 1], e);
67057136Seric 		if (ep != NULL)
67157136Seric 			*ep++ = ':';
67257136Seric 		if (tTd(27, 3))
67357136Seric 			printf("forward: trying %s\n", buf);
67463753Seric 
67563787Seric 		if (tTd(27, 9))
67663787Seric 			printf("forward: old uid = %d/%d\n", getuid(), geteuid());
67763787Seric 
678*63902Seric #ifdef HASSETREUID
67963753Seric 		saveduid = geteuid();
68063753Seric 		if (saveduid == 0 && uid != 0)
681*63902Seric 			(void) setreuid(0, uid);
68263753Seric #endif
68363753Seric 
68463787Seric 		if (tTd(27, 9))
68563787Seric 			printf("forward: new uid = %d/%d\n", getuid(), geteuid());
68663787Seric 
68758247Seric 		err = include(buf, TRUE, user, sendq, e);
68863753Seric 
689*63902Seric #ifdef HASSETREUID
69063753Seric 		if (saveduid == 0 && uid != 0)
691*63902Seric 			if (setreuid(-1, 0) < 0 || setreuid(RealUid, 0) < 0)
692*63902Seric 				syserr("setreuid(%d, 0) failure (real=%d, eff=%d)",
693*63902Seric 					RealUid, getuid(), geteuid());
69463753Seric #endif
69563753Seric 
69663787Seric 		if (tTd(27, 9))
69763787Seric 			printf("forward: reset uid = %d/%d\n", getuid(), geteuid());
69863787Seric 
69958247Seric 		if (err == 0)
70057136Seric 			break;
70158247Seric 		if (transienterror(err))
70258247Seric 		{
70358247Seric 			/* we have to suspend this message */
70459563Seric 			if (tTd(27, 2))
70559563Seric 				printf("forward: transient error on %s\n", buf);
70659563Seric #ifdef LOG
70759563Seric 			if (LogLevel > 2)
70859624Seric 				syslog(LOG_ERR, "%s: forward %s: transient error: %s",
70959624Seric 					e->e_id, buf, errstring(err));
71059563Seric #endif
71159611Seric 			message("%s: %s: message queued", buf, errstring(err));
71263853Seric 			user->q_flags |= QQUEUEUP;
71358247Seric 			return;
71458247Seric 		}
71557136Seric 	}
716292Seric }
717